tcm_loop: Add multi-fabric Linux/SCSI LLD fabric module
Subsystems (2)
RFC 22/22 tcm_loop: Add multi-fabric Linux/SCSI LLD fabric module
From: Nicholas Bellinger <[email protected]>
This patch adds the TCM_Loop Linux/SCSI LLD fabric module for accessing TCM device backstores as locally accessable SCSI LUNs in virtual SAS, FC, and iSCSI Target ports using the generic fabric TransportID and Target Port WWN naming handlers from TCM's target_core_fabric_lib.c The TCM_Loop module uses the generic fabric configfs infratructure provided by target_core_fabric_configfs.c and adds a module dependent attribute for the creation/release of the virtual I_T Nexus connected the TCM_Loop Target and Initiator Ports.
TCM_Loop can also be used with the scsi-generic driver so that STGT userspace fabric modules, QEMU-KVM and other hypervisor SCSI passthrough support can access TCM device backstore and control CDB emulation.
--- drivers/target/Kbuild | 3 + drivers/target/Kconfig | 2 + drivers/target/tcm_loop/Kbuild | 11 + drivers/target/tcm_loop/Kconfig | 13 + drivers/target/tcm_loop/tcm_loop_configfs.c | 676 ++++++++++++++++++++++++ drivers/target/tcm_loop/tcm_loop_configfs.h | 2 + drivers/target/tcm_loop/tcm_loop_core.h | 79 +++ drivers/target/tcm_loop/tcm_loop_fabric.c | 567 ++++++++++++++++++++ drivers/target/tcm_loop/tcm_loop_fabric.h | 45 ++ drivers/target/tcm_loop/tcm_loop_fabric_scsi.c | 659 +++++++++++++++++++++++ drivers/target/tcm_loop/tcm_loop_fabric_scsi.h | 10 + 11 files changed, 2067 insertions(+), 0 deletions(-) create mode 100644 drivers/target/tcm_loop/Kbuild create mode 100644 drivers/target/tcm_loop/Kconfig create mode 100644 drivers/target/tcm_loop/tcm_loop_configfs.c create mode 100644 drivers/target/tcm_loop/tcm_loop_configfs.h create mode 100644 drivers/target/tcm_loop/tcm_loop_core.h create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric.c create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric.h create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric_scsi.c create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric_scsi.h
drivers/target/Kbuild +3 -0
@@ -25,3 +25,6 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o obj-$(CONFIG_TCM_FILEIO) += target_core_file.o obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o + +# Fabric modules +obj-$(CONFIG_TCM_LOOP_FABRIC) += tcm_loop/
drivers/target/Kconfig +2 -0
@@ -32,3 +32,5 @@ config TCM_PSCSI default y ---help--- Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered passthrough access to Linux/SCSI device + +source "drivers/target/tcm_loop/Kconfig"
drivers/target/tcm_loop/Kbuild new file +11 -0
@@ -0,0 +1,11 @@ +EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/ -I$(srctree)/include/scsi/ -I$(srctree)/drivers/target/tcm_loop/ + +tcm_loop-objs := tcm_loop_fabric.o \ + tcm_loop_fabric_scsi.o \ + tcm_loop_configfs.o \ + +obj-$(CONFIG_TCM_LOOP_FABRIC) += tcm_loop.o + +ifdef CONFIG_TCM_LOOP_CDB_DEBUG +EXTRA_CFLAGS += -DTCM_LOOP_CDB_DEBUG +endif
drivers/target/tcm_loop/Kconfig new file +13 -0
@@ -0,0 +1,13 @@ +config TCM_LOOP_FABRIC + tristate "TCM Virtual SAS target and Linux/SCSI LDD fabric loopback module " + depends on TARGET_CORE && CONFIGFS_FS + default n + ---help--- + Say Y here to enable the TCM Virtual SAS target and Linux/SCSI LDD fabric loopback module + +config TCM_LOOP_CDB_DEBUG + tristate "TCM loopback fabric module CDB debug code" + depends on TARGET_CORE && CONFIGFS_FS && TCM_LOOP_FABRIC + default n + ---help--- + Say Y here to enable the TCM loopback fabric module CDB debug code
drivers/target/tcm_loop/tcm_loop_configfs.c new file +676 -0
676 lines not on this page · raw message
drivers/target/tcm_loop/tcm_loop_configfs.h new file +2 -0
@@ -0,0 +1,2 @@ +extern int tcm_loop_register_configfs (void ); +extern void tcm_loop_deregister_configfs (void );
drivers/target/tcm_loop/tcm_loop_core.h new file +79 -0
79 lines not on this page · raw message
drivers/target/tcm_loop/tcm_loop_fabric.c new file +567 -0
567 lines not on this page · raw message
drivers/target/tcm_loop/tcm_loop_fabric.h new file +45 -0
45 lines not on this page · raw message
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +659 -0
659 lines not on this page · raw message
drivers/target/tcm_loop/tcm_loop_fabric_scsi.h new file +10 -0
@@ -0,0 +1,10 @@ +extern struct kmem_cache *tcm_loop_cmd_cache; + +extern int tcm_loop_execute_core_cmd (struct tcm_loop_cmd *,struct scsi_cmnd *); +extern void tcm_loop_check_stop_free (struct se_cmd *); +extern void tcm_loop_deallocate_core_cmd (struct se_cmd *); +extern void tcm_loop_scsi_forget_host (struct Scsi_Host *); +extern void tcm_loop_deallocate_core_cmd (struct se_cmd *); +extern int tcm_loop_setup_hba_bus (struct tcm_loop_hba *,int ); +extern int tcm_loop_alloc_core_bus (void ); +extern void tcm_loop_release_core_bus (void );
Signature · 1 line
1.7.2.2
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 297
Quoted 11 lines · Nicholas A. Bellinger
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +11
-0+ +static void tcm_loop_primary_release (struct device *dev) +{ +return ; +} + +static struct device tcm_loop_primary = { + .init_name ="tcm_loop_0" , + .release = tcm_loop_primary_release, +}; +
No, you can not have statically allocated devices and dummy release functions.
Signature · 1 line
Dmitry
Quoted 16 lines · Dmitry Torokhov
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 297
Quoted 11 lines · Nicholas A. Bellinger
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +11
-0+ +static void tcm_loop_primary_release (struct device *dev) +{ +return ; +} + +static struct device tcm_loop_primary = { + .init_name ="tcm_loop_0" , + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline drivers/scsi/scsi_debug.c is doing for struct device pseudo_primary:
static void pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}
static struct device pseudo_primary = {
.init_name = "pseudo_0",
.release = pseudo_0_release,
};
so for these type of things in TCM_Loop I tend to follow what scsi_debug does,
so I don't exactly see a issue here atm. Anybody else have comments..?
Best,
--nab
Quoted 27 lines · Nicholas A. Bellinger
Quoted 16 lines · Dmitry Torokhov
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 297
Quoted 11 lines · Nicholas A. Bellinger
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +11
-0+ +static void tcm_loop_primary_release (struct device *dev) +{ +return ; +} + +static struct device tcm_loop_primary = { + .init_name ="tcm_loop_0" , + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline
drivers/scsi/scsi_debug.cis doing for struct devicepseudo_primary:static void
pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE&scsi_debug_opts)
printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}
Quoted 7 lines
static struct device
pseudo_primary= {
.init_name = "pseudo_0",
.release =pseudo_0_release,
};so for these type of things in
TCM_LoopI tend to follow whatscsi_debugdoes,
so I don't exactly see a issue here atm. Anybody else have comments..?
The kobject embedded in the device structure is refcounted and it's lifetime may be different from lifetime of your module. If somebody happen to hold reference to the driver while your module is being unloaded bad things will happen.
I am sure Greg has something on this topic ready for cut-and-paste.
Signature · 1 line
Dmitry
Quoted 36 lines · Dmitry Torokhov
Quoted 27 lines · Nicholas A. Bellinger
Quoted 16 lines · Dmitry Torokhov
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 297
Quoted 11 lines · Nicholas A. Bellinger
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +11
-0+ +static void tcm_loop_primary_release (struct device *dev) +{ +return ; +} + +static struct device tcm_loop_primary = { + .init_name ="tcm_loop_0" , + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline
drivers/scsi/scsi_debug.cis doing for struct devicepseudo_primary:static void
pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE&scsi_debug_opts)
printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}Quoted 7 lines
static struct device
pseudo_primary= {
.init_name = "pseudo_0",
.release =pseudo_0_release,
};so for these type of things in
TCM_LoopI tend to follow whatscsi_debugdoes,
so I don't exactly see a issue here atm. Anybody else have comments..?
Quoted 6 lines
The kobject embedded in the device structure is refcounted and it's lifetime may be different from lifetime of your module. If somebody happen to hold reference to the driver while your module is being unloaded bad things will happen.
I am sure Greg has something on this topic ready for cut-and-paste.
Documentation/kobject.txt :)
Quoted 37 lines · Greg KH
Quoted 36 lines · Dmitry Torokhov
Quoted 34 lines · Nicholas A. Bellinger
Quoted 16 lines · Dmitry Torokhov
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 297
Quoted 11 lines · Nicholas A. Bellinger
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +11
-0+ +static void tcm_loop_primary_release (struct device *dev) +{ +return ; +} + +static struct device tcm_loop_primary = { + .init_name ="tcm_loop_0" , + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline
drivers/scsi/scsi_debug.cis doing for struct devicepseudo_primary:static void
pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE&scsi_debug_opts)
printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}static struct device
pseudo_primary= {
.init_name = "pseudo_0",
.release =pseudo_0_release,
};so for these type of things in
TCM_LoopI tend to follow whatscsi_debugdoes,
so I don't exactly see a issue here atm. Anybody else have comments..?
Quoted 8 lines
Quoted 6 lines
The kobject embedded in the device structure is refcounted and it's lifetime may be different from lifetime of your module. If somebody happen to hold reference to the driver while your module is being unloaded bad things will happen.
I am sure Greg has something on this topic ready for cut-and-paste.
Documentation/kobject.txt:)
I still don't get it..? Does this mean that scsi_debug and TCM_Loop are currently broken wrt to this item or not..?
Best,
--nab
Quoted 43 lines · Nicholas A. Bellinger
Quoted 42 lines · Greg KH
Quoted 41 lines · Dmitry Torokhov
Quoted 27 lines · Nicholas A. Bellinger
Quoted 16 lines · Dmitry Torokhov
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 297
Quoted 11 lines · Nicholas A. Bellinger
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +11
-0+ +static void tcm_loop_primary_release (struct device *dev) +{ +return ; +} + +static struct device tcm_loop_primary = { + .init_name ="tcm_loop_0" , + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline
drivers/scsi/scsi_debug.cis doing for struct devicepseudo_primary:static void
pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE&scsi_debug_opts)
printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}Quoted 7 lines
static struct device
pseudo_primary= {
.init_name = "pseudo_0",
.release =pseudo_0_release,
};so for these type of things in
TCM_LoopI tend to follow whatscsi_debugdoes,
so I don't exactly see a issue here atm. Anybody else have comments..?The kobject embedded in the device structure is refcounted and it's lifetime may be different from lifetime of your module. If somebody happen to hold reference to the driver while your module is being unloaded bad things will happen.
Quoted 7 lines
Quoted 3 lines
Quoted 1 line
I am sure Greg has something on this topic ready for cut-and-paste.
Documentation/kobject.txt:)I still don't get it..? Does this mean that
scsi_debugandTCM_Loopare currently broken wrt to this item or not..?
Yes they are broken.
Signature · 1 line
Dmitry
Quoted 46 lines · Dmitry Torokhov
Quoted 45 lines · Nicholas A. Bellinger
Quoted 44 lines · Greg KH
Quoted 42 lines · Dmitry Torokhov
Quoted 27 lines · Nicholas A. Bellinger
Quoted 16 lines · Dmitry Torokhov
Review
Quoted 11 lines · Nicholas A. Bellinger
(quoted diff) +11
-0+ +static void tcm_loop_primary_release(struct device *dev) +{ + return; +} + +static struct device tcm_loop_primary = { + .init_name = "tcm_loop_0", + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline
drivers/scsi/scsi_debug.cis doing for struct devicepseudo_primary:static void
pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE&scsi_debug_opts)
printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}Quoted 7 lines
static struct device
pseudo_primary= {
.init_name = "pseudo_0",
.release =pseudo_0_release,
};so for these type of things in
TCM_LoopI tend to follow whatscsi_debugdoes,
so I don't exactly see a issue here atm. Anybody else have comments..?The kobject embedded in the device structure is refcounted and it's lifetime may be different from lifetime of your module. If somebody happen to hold reference to the driver while your module is being unloaded bad things will happen.
I am sure Greg has something on this topic ready for cut-and-paste.
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
Documentation/kobject.txt:)I still don't get it..? Does this mean that
scsi_debugandTCM_Loopare currently broken wrt to this item or not..?Yes they are broken.
Hi Dmitry and Greg,
Would you mind giving a bit more specific idea as to what a patch to fix this issue would involve for an emulated struct device usage..? I would be happy to generate patches for scsi_debug and TCM_loop to resolve this issue, but I need a bit more information what I should be looking at.
Thanks,
--nab
Quoted 53 lines · Nicholas A. Bellinger
Quoted 52 lines · Dmitry Torokhov
Quoted 50 lines · Nicholas A. Bellinger
Quoted 37 lines · Greg KH
Quoted 36 lines · Dmitry Torokhov
Quoted 34 lines · Nicholas A. Bellinger
Quoted 16 lines · Dmitry Torokhov
Review
Quoted 11 lines · Nicholas A. Bellinger
(quoted diff) +11
-0+ +static void tcm_loop_primary_release(struct device *dev) +{ + return; +} + +static struct device tcm_loop_primary = { + .init_name = "tcm_loop_0", + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline
drivers/scsi/scsi_debug.cis doing for struct devicepseudo_primary:static void
pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE&scsi_debug_opts)
printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
}static struct device
pseudo_primary= {
.init_name = "pseudo_0",
.release =pseudo_0_release,
};so for these type of things in
TCM_LoopI tend to follow whatscsi_debugdoes,
so I don't exactly see a issue here atm. Anybody else have comments..?Quoted 8 lines
Quoted 6 lines
The kobject embedded in the device structure is refcounted and it's lifetime may be different from lifetime of your module. If somebody happen to hold reference to the driver while your module is being unloaded bad things will happen.
I am sure Greg has something on this topic ready for cut-and-paste.
Documentation/kobject.txt:)I still don't get it..? Does this mean that
scsi_debugandTCM_Loopare currently broken wrt to this item or not..?
Quoted 9 lines
Quoted 1 line
Yes they are broken.
Hi Dmitry and Greg,
Would you mind giving a bit more specific idea as to what a patch to fix this issue would involve for an emulated struct device usage..? I would be happy to generate patches for
scsi_debugandTCM_loopto resolve this issue, but I need a bit more information what I should be looking at.
I think what you need is root_device_create() which will allocate a device structure and assign ->release() that is implemented outside of your module. This way your module may be unloaded even if there are outstanding references to tcm_loop_primary device.
The rest of devices you create needs to increment module counter or otherwise ensure that their methods stay available until they are cleaned up (->release() is called).
Hope this helps.
Signature · 1 line
Dmitry
Quoted 64 lines · Dmitry Torokhov
Quoted 53 lines · Nicholas A. Bellinger
Quoted 52 lines · Dmitry Torokhov
Quoted 50 lines · Nicholas A. Bellinger
Quoted 37 lines · Greg KH
Quoted 36 lines · Dmitry Torokhov
Quoted 34 lines · Nicholas A. Bellinger
Quoted 16 lines · Dmitry Torokhov
Review
Quoted 11 lines · Nicholas A. Bellinger
(quoted diff) +11
-0+ +static void tcm_loop_primary_release(struct device *dev) +{ + return; +} + +static struct device tcm_loop_primary = { + .init_name = "tcm_loop_0", + .release = tcm_loop_primary_release, +}; +No, you can not have statically allocated devices and dummy release functions.
Last time I checked this is still what mainline
drivers/scsi/scsi_debug.cis doing for struct devicepseudo_primary:static void
pseudo_0_release(struct device *dev)
{
if (SCSI_DEBUG_OPT_NOISE&scsi_debug_opts)printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n"); }static struct device
pseudo_primary= {
.init_name = "pseudo_0",
.release =pseudo_0_release,
};so for these type of things in
TCM_LoopI tend to follow whatscsi_debugdoes, so I don't exactly see a issue here atm. Anybody else have comments..?Quoted 8 lines
Quoted 6 lines
The kobject embedded in the device structure is refcounted and it's lifetime may be different from lifetime of your module. If somebody happen to hold reference to the driver while your module is being unloaded bad things will happen.
I am sure Greg has something on this topic ready for cut-and-paste.
Documentation/kobject.txt:)I still don't get it..? Does this mean that
scsi_debugandTCM_Loopare currently broken wrt to this item or not..?Quoted 9 lines
Quoted 1 line
Yes they are broken.
Hi Dmitry and Greg,
Would you mind giving a bit more specific idea as to what a patch to fix this issue would involve for an emulated struct device usage..? I would be happy to generate patches for
scsi_debugandTCM_loopto resolve this issue, but I need a bit more information what I should be looking at.
Quoted 4 lines
I think what you need is
root_device_create()which will allocate a device structure and assign ->release() that is implemented outside of your module. This way your module may be unloaded even if there are outstanding references totcm_loop_primarydevice.
I am embarrassed to admit that scst_local (or whatever its new name will be) probably has the same fault, as it too was based on scsi_debug ...
However, I did a search of the 2.6.24.1 source tree and could not find any instances of root_device_create() being called nor defined. I just double and triple checked the spelling.
Ahhh, I see why. A slightly different search suggests that it is root_device_register(const char *name).
From include/linux/device.h:
static inline struct device *root_device_register(const char *name)
{
return __root_device_register(name, THIS_MODULE);
}Was that the one you were thinking of?
Quoted 3 lines
The rest of devices you create needs to increment module counter or otherwise ensure that their methods stay available until they are cleaned up (->release() is called).
OK, I need to check out how this is done.
Quoted 1 line
Hope this helps.
Sure does.
Signature · 6 lines
Regards, Richard Sharpe -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 10 lines · Richard Sharpe
Quoted 4 lines
I think what you need is
root_device_create()which will allocate a device structure and assign ->release() that is implemented outside of your module. This way your module may be unloaded even if there are outstanding references totcm_loop_primarydevice.I am embarrassed to admit that
scst_local(or whatever its new name will be) probably has the same fault, as it too was based onscsi_debug...However, I did a search of the 2.6.24.1 source tree and could not find
Typo alert: 2.6.34.1!
Quoted 29 lines
any instances of
root_device_create()being called nor defined. I just double and triple checked the spelling.Ahhh, I see why. A slightly different search suggests that it is
root_device_register(const char *name).From
include/linux/device.h:static inline struct device *root_device_register(const char *name) {
return__root_device_register(name, THIS_MODULE); }Was that the one you were thinking of?
Quoted 3 lines
The rest of devices you create needs to increment module counter or otherwise ensure that their methods stay available until they are cleaned up (->release() is called).
OK, I need to check out how this is done.
Quoted 1 line
Hope this helps.
Sure does.
--
Regards,
Richard Sharpe
Signature · 6 lines
Regards, Richard Sharpe -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 19 lines · Richard Sharpe
Quoted 5 lines · Dmitry Torokhov
I think what you need is
root_device_create()which will allocate a device structure and assign ->release() that is implemented outside of your module. This way your module may be unloaded even if there are outstanding references totcm_loop_primarydevice.I am embarrassed to admit that
scst_local(or whatever its new name will be) probably has the same fault, as it too was based onscsi_debug...However, I did a search of the 2.6.24.1 source tree and could not find any instances of
root_device_create()being called nor defined. I just double and triple checked the spelling.Ahhh, I see why. A slightly different search suggests that it is
root_device_register(const char *name).
Quoted 9 lines
From
include/linux/device.h:static inline struct device *root_device_register(const char *name)
{ return __root_device_register(name, THIS_MODULE); }Was that the one you were thinking of?
Yes, that is the one. I'll blame Greg for inconsistent naming that caused this error :) - we usually have xxx_register() for objects that we already have on hand and xxx_create() (as in device_create) when we want the object to be allocated for us.
Thanks.
Signature · 1 line
Dmitry
Quoted 4 lines · Dmitry Torokhov
Yes, that is the one. I'll blame Greg for inconsistent naming that caused this error :) - we usually have
xxx_register()for objects that we already have on hand andxxx_create()(as indevice_create) when we want the object to be allocated for us.
Yes, we have a number of naming inconsistancies in the driver core like this. I need to sit down and fix them all one of these days...
thanks,
greg k-h
Hi Nicholas,
Some random questions:
Quoted 177 lines · Nicholas A. Bellinger
From: Nicholas Bellinger <[email protected]>
This patch adds the
TCM_LoopLinux/SCSI LLD fabric module for accessing TCM device backstores as locally accessable SCSI LUNs in virtual SAS, FC, and iSCSI Target ports using the generic fabric TransportID and Target Port WWN naming handlers from TCM'starget_core_fabric_lib.cTheTCM_Loopmodule uses the generic fabric configfs infratructure provided bytarget_core_fabric_configfs.cand adds a module dependent attribute for the creation/release of the virtualI_TNexus connected theTCM_LoopTarget and Initiator Ports.
TCM_Loopcan also be used with the scsi-generic driver so that STGT userspace fabric modules, QEMU-KVM and other hypervisor SCSI passthrough support can access TCM device backstore and control CDB emulation.Signed-off-by Nicholas A. Bellinger <[email protected]>--- drivers/target/Kbuild | 3 + drivers/target/Kconfig | 2 + drivers/target/tcm_loop/Kbuild | 11 + drivers/target/tcm_loop/Kconfig | 13 + drivers/target/tcm_loop/tcm_loop_configfs.c | 676 ++++++++++++++++++++++++ drivers/target/tcm_loop/tcm_loop_configfs.h | 2 + drivers/target/tcm_loop/tcm_loop_core.h | 79 +++ drivers/target/tcm_loop/tcm_loop_fabric.c | 567 ++++++++++++++++++++ drivers/target/tcm_loop/tcm_loop_fabric.h | 45 ++ drivers/target/tcm_loop/tcm_loop_fabric_scsi.c | 659 +++++++++++++++++++++++ drivers/target/tcm_loop/tcm_loop_fabric_scsi.h | 10 + 11 files changed, 2067 insertions(+), 0 deletions(-) create mode 100644 drivers/target/tcm_loop/Kbuild create mode 100644 drivers/target/tcm_loop/Kconfig create mode 100644 drivers/target/tcm_loop/tcm_loop_configfs.c create mode 100644 drivers/target/tcm_loop/tcm_loop_configfs.h create mode 100644 drivers/target/tcm_loop/tcm_loop_core.h create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric.c create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric.h create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric_scsi.c create mode 100644 drivers/target/tcm_loop/tcm_loop_fabric_scsi.hdrivers/target/Kbuild +3
-0@@ -25,3 +25,6 @@ obj-$(CONFIG_TARGET_CORE) += target_core_mod.o obj-$(CONFIG_TCM_IBLOCK) += target_core_iblock.o obj-$(CONFIG_TCM_FILEIO) += target_core_file.o obj-$(CONFIG_TCM_PSCSI) += target_core_pscsi.o + +# Fabric modules +obj-$(CONFIG_TCM_LOOP_FABRIC) += tcm_loop/drivers/target/Kconfig +2
-0@@ -32,3 +32,5 @@ config TCM_PSCSI default y ---help--- Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered passthrough access to Linux/SCSI device + +source "drivers/target/tcm_loop/Kconfig"drivers/target/tcm_loop/Kbuild new file +11
-0@@ -0,0 +1,11 @@ +EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/ -I$(srctree)/include/scsi/ -I$(srctree)/drivers/target/tcm_loop/ + +tcm_loop-objs := tcm_loop_fabric.o \ + tcm_loop_fabric_scsi.o \ + tcm_loop_configfs.o \ + +obj-$(CONFIG_TCM_LOOP_FABRIC) += tcm_loop.o + +ifdef CONFIG_TCM_LOOP_CDB_DEBUG +EXTRA_CFLAGS += -DTCM_LOOP_CDB_DEBUG +endifdrivers/target/tcm_loop/Kconfig new file +13
-0@@ -0,0 +1,13 @@ +config TCM_LOOP_FABRIC + tristate "TCM Virtual SAS target and Linux/SCSI LDD fabric loopback module " + depends on TARGET_CORE && CONFIGFS_FS + default n + ---help--- + Say Y here to enable the TCM Virtual SAS target and Linux/SCSI LDD fabric loopback module + +config TCM_LOOP_CDB_DEBUG + tristate "TCM loopback fabric module CDB debug code" + depends on TARGET_CORE && CONFIGFS_FS && TCM_LOOP_FABRIC + default n + ---help--- + Say Y here to enable the TCM loopback fabric module CDB debug codedrivers/target/tcm_loop/tcm_loop_configfs.c new file +77
-0@@ -0,0 +1,676 @@ +/******************************************************************************* +* Filename: tcm_loop_configfs.c +* +* This file contains configfs implemenation for initiator and target +* side CDB level emulation of SAS, FC and iSCSI ports to Linux/SCSI LUNs. +* +* Copyright (c) 2009, 2010 Rising Tide, Inc. +* Copyright (c) 2009, 2010 Linux-iSCSI.org +* +* Copyright (c) 2009, 2010 Nicholas A. Bellinger <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +****************************************************************************/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <generated/utsrelease.h> +#include <linux/utsname.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/configfs.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_cmnd.h> + +#include <target/target_core_base.h> +#include <target/target_core_transport.h> +#include <target/target_core_fabric_ops.h> +#include <target/target_core_fabric_configfs.h> +#include <target/target_core_fabric_lib.h> +#include <target/target_core_device.h> +#include <target/target_core_tpg.h> +#include <target/target_core_configfs.h> +#include <target/target_core_base.h> +#include <target/configfs_macros.h> + +#include <tcm_loop_core.h> +#include <tcm_loop_configfs.h> +#include <tcm_loop_fabric.h> +#include <tcm_loop_fabric_scsi.h> + +/* Local pointer to allocated TCM configfs fabric module */ +struct target_fabric_configfs *tcm_loop_fabric_configfs; + +static int tcm_loop_hba_no_cnt; + +static char *tcm_loop_dump_proto_id (struct tcm_loop_hba *tl_hba) +{ +switch (tl_hba->tl_proto_id) { +case SCSI_PROTOCOL_SAS : +return "SAS" ; +case SCSI_PROTOCOL_FCP : +return "FCP" ; +case SCSI_PROTOCOL_ISCSI : +return "iSCSI" ; +default : +break ; + } + +return "Unknown" ; +} + +/* Start items for tcm_loop_port_cit */ +
drivers/target/tcm_loop/tcm_loop_configfs.c line 87
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+int tcm_loop_port_link ( +struct se_portal_group *se_tpg, +struct se_lun *lun) +{ +struct tcm_loop_tpg *tl_tpg =container_of (se_tpg, + struct tcm_loop_tpg, tl_se_tpg); +struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + +atomic_inc (&tl_tpg->tl_tpg_port_count); +smp_mb__after_atomic_inc ();
Why exactly this memory barrier is needed?
drivers/target/tcm_loop/tcm_loop_configfs.c line 93
Quoted 6 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +6
-0+/* +* Add Linux/SCSI struct scsi_device by HCTL +*/ +scsi_add_device (tl_hba->sh,0 , tl_tpg->tl_tpgt, lun->unpacked_lun); + +printk (KERN_INFO "TCM_Loop_ConfigFS: Port Link Successful\n" );
Consider using pr_err() and friends throughout the code.
drivers/target/tcm_loop/tcm_loop_configfs.c line 106
Quoted 13 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +13
-0+return 0 ; +} + +void tcm_loop_port_unlink ( +struct se_portal_group *se_tpg, +struct se_lun *se_lun) +{ +struct scsi_device *sd; +struct tcm_loop_hba *tl_hba; +struct tcm_loop_tpg *tl_tpg; + + tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg); + tl_hba = tl_tpg->tl_hba;
Stray whitespace at the end.
drivers/target/tcm_loop/tcm_loop_configfs.c line 118
Quoted 12 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +12
-0+ + sd =scsi_device_lookup (tl_hba->sh,0 , tl_tpg->tl_tpgt, + se_lun->unpacked_lun); +if (!(sd)) { +printk (KERN_ERR "Unable to locate struct scsi_device for %d:%d:" +"%d\n" ,0 , tl_tpg->tl_tpgt, se_lun->unpacked_lun); +return ; + } +/* +* Remove Linux/SCSI struct scsi_device by HCTL +*/ +scsi_remove_device (sd);
Stray whitespace.
drivers/target/tcm_loop/tcm_loop_configfs.c line 120
Quoted 2 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +2
-0+scsi_device_put (sd); +
Stray whitespace.
drivers/target/tcm_loop/tcm_loop_configfs.c line 122
Quoted 2 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +2
-0+atomic_dec (&tl_tpg->tl_tpg_port_count); +smp_mb__after_atomic_dec ();
Why is this barrier needed.
Quoted 28 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +28
-0+ +printk (KERN_INFO "TCM_Loop_ConfigFS: Port Unlink Successful\n" ); +} + +/* End items for tcm_loop_port_cit */ + +/* Start items for tcm_loop_nexus_cit */ + +static int tcm_loop_make_nexus ( +struct tcm_loop_tpg *tl_tpg, +const char *name) +{ +struct se_portal_group *se_tpg; +struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; +struct tcm_loop_nexus *tl_nexus; + +if (tl_tpg->tl_hba->tl_nexus) { +printk (KERN_INFO "tl_tpg->tl_hba->tl_nexus already exists\n" ); +return -EEXIST ; + } + se_tpg = &tl_tpg->tl_se_tpg; + + tl_nexus =kzalloc (sizeof (struct tcm_loop_nexus ),GFP_KERNEL ); +if (!(tl_nexus)) { +printk (KERN_ERR "Unable to allocate struct tcm_loop_nexus\n" ); +return -ENOMEM ; + } +/*
drivers/target/tcm_loop/tcm_loop_configfs.c line 160
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+* Initialize the struct se_session pointer +*/ + tl_nexus->se_sess =transport_init_session (); +if (!(tl_nexus->se_sess)) + goto out; +/* +* Since we are running in 'demo mode' this call with generate a +* struct se_node_acl for the tcm_loop struct se_portal_group with the SCSI +* Initiator port name of the passed configfs group 'name'. +*/
Stray whitespace.
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+ tl_nexus->se_sess->se_node_acl =core_tpg_check_initiator_node_acl ( + se_tpg, (unsigned char *)name); +if (!(tl_nexus->se_sess->se_node_acl)) { +transport_free_session (tl_nexus->se_sess); + goto out; + } +/* +* Now, register the SAS I_T Nexus as active with the call to +* transport_register_session() +*/
drivers/target/tcm_loop/tcm_loop_configfs.c line 180
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+__transport_register_session (se_tpg, tl_nexus->se_sess->se_node_acl, + tl_nexus->se_sess, (void *)tl_nexus); + tl_tpg->tl_hba->tl_nexus = tl_nexus; +printk (KERN_INFO "TCM_Loop_ConfigFS: Established I_T Nexus to emulated" +" %s Initiator Port: %s\n" ,tcm_loop_dump_proto_id (tl_hba), + name); +return 0 ; + +out: +kfree (tl_nexus);
Stray whitespace.
Quoted 15 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +15
-0+return -ENOMEM ; +} + +static int tcm_loop_drop_nexus ( +struct tcm_loop_tpg *tpg) +{ +struct se_session *se_sess; +struct tcm_loop_nexus *tl_nexus; +struct tcm_loop_hba *tl_hba = tpg->tl_hba; + + tl_nexus = tpg->tl_hba->tl_nexus; +if (!(tl_nexus)) +return -ENODEV ; + + se_sess = tl_nexus->se_sess;
drivers/target/tcm_loop/tcm_loop_configfs.c line 205
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+if (!(se_sess)) +return -ENODEV ; + +if (atomic_read (&tpg->tl_tpg_port_count)) { +printk (KERN_ERR "Unable to remove TCM_Loop I_T Nexus with" +" active TPG port count: %d\n" , +atomic_read (&tpg->tl_tpg_port_count)); +return -EPERM ; + } +
Is this function mutually exclusive with tcm_loop_port_link()? What prevents port count to be bumped here?
drivers/target/tcm_loop/tcm_loop_configfs.c line 207
Quoted 2 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +2
-0+ printk(KERN_INFO "TCM_Loop_ConfigFS: Removing I_T Nexus to emulated" + " %s Initiator Port: %s\n", tcm_loop_dump_proto_id(tl_hba),
Do not break strings ewven if they cross 80 column boundary.
Quoted 93 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +93
-0+ tl_nexus->se_sess->se_node_acl->initiatorname); +/* +* Release the SCSI I_T Nexus to the emulated SAS Target Port +*/ +transport_deregister_session (tl_nexus->se_sess); + tpg->tl_hba->tl_nexus =NULL ; +kfree (tl_nexus); +return 0 ; +} + +/* End items for tcm_loop_nexus_cit */ + +static ssize_t tcm_loop_tpg_show_nexus ( +struct se_portal_group *se_tpg, +char *page) +{ +struct tcm_loop_tpg *tl_tpg =container_of (se_tpg, + struct tcm_loop_tpg, tl_se_tpg); +struct tcm_loop_nexus *tl_nexus; +ssize_t ret; + + tl_nexus = tl_tpg->tl_hba->tl_nexus; +if (!(tl_nexus)) +return -ENODEV ; + + ret =snprintf (page,PAGE_SIZE ,"%s\n" , + tl_nexus->se_sess->se_node_acl->initiatorname); + +return ret; +} + +static ssize_t tcm_loop_tpg_store_nexus ( +struct se_portal_group *se_tpg, +const char *page, +size_t count) +{ +struct tcm_loop_tpg *tl_tpg =container_of (se_tpg, + struct tcm_loop_tpg, tl_se_tpg); +struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; +unsigned char i_port[TL_WWN_ADDR_LEN ], *ptr, *port_ptr; +int ret; +/* +* Shutdown the active I_T nexus if 'NULL' is passed.. +*/ +if (!(strncmp (page,"NULL" ,4 ))) { + ret =tcm_loop_drop_nexus (tl_tpg); +return (!ret) ? count : ret; + } +/* +* Otherwise make sure the passed virtual Initiator port WWN matches +* the fabric protocol_id set in tcm_loop_make_scsi_hba(), and call +* tcm_loop_make_nexus() +*/ +if (strlen (page) >TL_WWN_ADDR_LEN ) { +printk (KERN_ERR "Emulated NAA Sas Address: %s, exceeds" +" max: %d\n" , page,TL_WWN_ADDR_LEN ); +return -EINVAL ; + } +snprintf (&i_port[0 ],TL_WWN_ADDR_LEN ,"%s" , page); + + ptr =strstr (i_port,"naa." ); +if (ptr) { +if (tl_hba->tl_proto_id !=SCSI_PROTOCOL_SAS ) { +printk (KERN_ERR "Passed SAS Initiator Port %s does not" +" match target port protoid: %s\n" , i_port, +tcm_loop_dump_proto_id (tl_hba)); +return -EINVAL ; + } + port_ptr = &i_port[0 ]; + goto check_newline; + } + ptr =strstr (i_port,"fc." ); +if (ptr) { +if (tl_hba->tl_proto_id !=SCSI_PROTOCOL_FCP ) { +printk (KERN_ERR "Passed FCP Initiator Port %s does not" +" match target port protoid: %s\n" , i_port, +tcm_loop_dump_proto_id (tl_hba)); +return -EINVAL ; + } + port_ptr = &i_port[3 ];/* Skip over "fc." */ + goto check_newline; + } + ptr =strstr (i_port,"iqn." ); +if (ptr) { +if (tl_hba->tl_proto_id !=SCSI_PROTOCOL_ISCSI ) { +printk (KERN_ERR "Passed iSCSI Initiator Port %s does not" +" match target port protoid: %s\n" , i_port, +tcm_loop_dump_proto_id (tl_hba)); +return -EINVAL ; + } + port_ptr = &i_port[0 ]; + goto check_newline; + }
drivers/target/tcm_loop/tcm_loop_configfs.c line 310
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+printk (KERN_ERR "Unable to locate prefix for emulated Initiator Port:" +" %s\n" , i_port); +return -EINVAL ; +/* +* Clear any trailing newline for the NAA WWN +*/ +check_newline: +if (i_port[strlen (i_port)-1 ] =='\n' ) + i_port[strlen (i_port)-1 ] ='\0' ; +
Stray whitespace.
Quoted 105 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +105
-0+ ret =tcm_loop_make_nexus (tl_tpg, port_ptr); +if (ret <0 ) +return ret; + +return count; +} + +TF_TPG_BASE_ATTR (tcm_loop, nexus,S_IRUGO |S_IWUSR ); + +static struct configfs_attribute *tcm_loop_tpg_attrs[] = { + &tcm_loop_tpg_nexus.attr, +NULL , +}; + +/* Start items for tcm_loop_naa_cit */ + +struct se_portal_group *tcm_loop_make_naa_tpg ( +struct se_wwn *wwn, +struct config_group *group, +const char *name) +{ +struct tcm_loop_hba *tl_hba =container_of (wwn, + struct tcm_loop_hba, tl_hba_wwn); +struct tcm_loop_tpg *tl_tpg; +char *tpgt_str, *end_ptr; +int ret; +unsigned short int tpgt; + + tpgt_str =strstr (name,"tpgt_" ); +if (!(tpgt_str)) { +printk (KERN_ERR "Unable to locate \"tpgt_#\" directory" +" group\n" ); +return ERR_PTR (-EINVAL ); + } + tpgt_str +=5 ;/* Skip ahead of "tpgt_" */ + tpgt = (unsigned short int )simple_strtoul (tpgt_str, &end_ptr,0 ); + +if (tpgt >TL_TPGS_PER_HBA ) { +printk (KERN_ERR "Passed tpgt: %hu exceeds TL_TPGS_PER_HBA:" +" %u\n" , tpgt,TL_TPGS_PER_HBA ); +return ERR_PTR (-EINVAL ); + } + tl_tpg = &tl_hba->tl_hba_tpgs[tpgt]; + tl_tpg->tl_hba = tl_hba; + tl_tpg->tl_tpgt = tpgt; +/* +* Register the tl_tpg as a emulated SAS TCM Target Endpoint +*/ + ret =core_tpg_register (&tcm_loop_fabric_configfs->tf_ops, + wwn, &tl_tpg->tl_se_tpg, (void *)tl_tpg, +TRANSPORT_TPG_TYPE_NORMAL ); +if (ret <0 ) +return ERR_PTR (-ENOMEM ); + +printk (KERN_INFO "TCM_Loop_ConfigFS: Allocated Emulated %s" +" Target Port %s,t,0x%04x\n" ,tcm_loop_dump_proto_id (tl_hba), +config_item_name (&wwn->wwn_group.cg_item), tpgt); + +return &tl_tpg->tl_se_tpg; +} + +void tcm_loop_drop_naa_tpg ( +struct se_portal_group *se_tpg) +{ +struct se_wwn *wwn = se_tpg->se_tpg_wwn; +struct tcm_loop_tpg *tl_tpg =container_of (se_tpg, + struct tcm_loop_tpg, tl_se_tpg); +struct tcm_loop_hba *tl_hba; +unsigned short tpgt; + + tl_hba = tl_tpg->tl_hba; + tpgt = tl_tpg->tl_tpgt; +/* +* Release the I_T Nexus for the Virtual SAS link if present +*/ +tcm_loop_drop_nexus (tl_tpg); +/* +* Deregister the tl_tpg as a emulated SAS TCM Target Endpoint +*/ +core_tpg_deregister (se_tpg); + +printk (KERN_INFO "TCM_Loop_ConfigFS: Deallocated Emulated %s" +" Target Port %s,t,0x%04x\n" ,tcm_loop_dump_proto_id (tl_hba), +config_item_name (&wwn->wwn_group.cg_item), tpgt); +} + +/* End items for tcm_loop_naa_cit */ + +/* Start items for tcm_loop_cit */ + +struct se_wwn *tcm_loop_make_scsi_hba ( +struct target_fabric_configfs *tf, +struct config_group *group, +const char *name) +{ +struct tcm_loop_hba *tl_hba; +struct Scsi_Host *sh; +char *ptr; +int ret, off =0 ; + + tl_hba =kzalloc (sizeof (struct tcm_loop_hba ),GFP_KERNEL ); +if (!(tl_hba)) { +printk (KERN_ERR "Unable to allocate struct tcm_loop_hba\n" ); +return ERR_PTR (-ENOMEM ); + }
drivers/target/tcm_loop/tcm_loop_configfs.c line 425
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+/* +* Determine the emulated Protocol Identifier and Target Port Name +* based on the incoming configfs directory name. +*/ + ptr =strstr (name,"naa." ); +if (ptr) { + tl_hba->tl_proto_id =SCSI_PROTOCOL_SAS ; + goto check_len; + } + ptr =strstr (name,"fc." );
Stray whitespace at the end.
Quoted 33 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +33
-0+if (ptr) { + tl_hba->tl_proto_id =SCSI_PROTOCOL_FCP ; + off =3 ;/* Skip over "fc." */ + goto check_len; + } + ptr =strstr (name,"iqn." ); +if (ptr) { + tl_hba->tl_proto_id =SCSI_PROTOCOL_ISCSI ; + goto check_len; + } + +printk (KERN_ERR "Unable to locate prefix for emulated Target Port:" +" %s\n" , name); +return ERR_PTR (-EINVAL ); + +check_len: +if (strlen (name) >TL_WWN_ADDR_LEN ) { +printk (KERN_ERR "Emulated NAA %s Address: %s, exceeds" +" max: %d\n" , name,tcm_loop_dump_proto_id (tl_hba), +TL_WWN_ADDR_LEN ); +kfree (tl_hba); +return ERR_PTR (-EINVAL ); + } +snprintf (&tl_hba->tl_wwn_address[0 ],TL_WWN_ADDR_LEN ,"%s" , &name[off]); + +/* +* Setup the tl_hba->tl_hba_qobj +*/ + tl_hba->tl_hba_qobj =kzalloc (sizeof (struct se_queue_obj ),GFP_KERNEL ); +if (!(tl_hba->tl_hba_qobj)) { +kfree (tl_hba); +printk ("Unable to allocate tl_hba->tl_hba_qobj\n" ); +return ERR_PTR (-ENOMEM );
drivers/target/tcm_loop/tcm_loop_configfs.c line 468
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+ } +transport_init_queue_obj (tl_hba->tl_hba_qobj); + +/* +* Call device_register(tl_hba->dev) to register the emulated +* Linux/SCSI LLD of type struct Scsi_Host at tl_hba->sh after +* device_register() callbacks in tcm_loop_driver_probe() +*/ + ret =tcm_loop_setup_hba_bus (tl_hba, tcm_loop_hba_no_cnt); +if (ret)
Stray whitespace at the end.
Quoted 5 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +5
-0+ goto out; + + sh = tl_hba->sh; +/* +* Start up the per struct Scsi_Host tcm_loop processing thread
drivers/target/tcm_loop/tcm_loop_configfs.c line 483
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+*/ + tl_hba->tl_kthread =kthread_run (tcm_loop_processing_thread, + (void *)tl_hba,"tcm_loop_%d" , sh->host_no); +if (IS_ERR (tl_hba->tl_kthread)) { +printk (KERN_ERR "Unable to start tcm_loop kthread\n" ); +device_unregister (&tl_hba->dev); + ret = -ENOMEM ; + goto out; + } +wait_for_completion (&tl_hba->tl_hba_qobj->thread_create_comp);
Why is this needed? kthread_create wait till thread is actually created.
drivers/target/tcm_loop/tcm_loop_configfs.c line 485
Quoted 2 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +2
-0+ + tcm_loop_hba_no_cnt++;
Should this be atomic? What prevents several threads from running this code?
Quoted 13 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +13
-0+printk (KERN_INFO "TCM_Loop_ConfigFS: Allocated emulated Target" +" %s Address: %s at Linux/SCSI Host ID: %d\n" , +tcm_loop_dump_proto_id (tl_hba), name, sh->host_no); + +return &tl_hba->tl_hba_wwn; +out: +kfree (tl_hba->tl_hba_qobj); +kfree (tl_hba); +return ERR_PTR (ret); +} + +void tcm_loop_drop_scsi_hba ( +struct se_wwn *wwn)
drivers/target/tcm_loop/tcm_loop_configfs.c line 508
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +10
-0+{ +struct tcm_loop_hba *tl_hba =container_of (wwn, + struct tcm_loop_hba, tl_hba_wwn); +int host_no = tl_hba->sh->host_no; + +/* +* Shutdown the per HBA tcm_loop processing kthread +*/ +kthread_stop (tl_hba->tl_kthread); +wait_for_completion (&tl_hba->tl_hba_qobj->thread_done_comp);
Why? kthread_stop() already does this.
Quoted 342 lines
drivers/target/tcm_loop/tcm_loop_configfs.c new file +168
-0+/* +* Call device_unregister() on the original tl_hba->dev. +* tcm_loop_fabric_scsi.c:tcm_loop_release_adapter() will +* release *tl_hba; +*/ +device_unregister (&tl_hba->dev); + +printk (KERN_INFO "TCM_Loop_ConfigFS: Deallocated emulated Target" +" SAS Address: %s at Linux/SCSI Host ID: %d\n" , +config_item_name (&wwn->wwn_group.cg_item), host_no); +} + +/* Start items for tcm_loop_cit */ +static ssize_t tcm_loop_wwn_show_attr_version ( +struct target_fabric_configfs *tf, +char *page) +{ +return sprintf (page,"TCM Loopback Fabric module %s on %s/%s" +" on " UTS_RELEASE "\n" ,TCM_LOOP_VERSION ,utsname ()->sysname, +utsname ()->machine); +} + +TF_WWN_ATTR_RO (tcm_loop, version); + +static struct configfs_attribute *tcm_loop_wwn_attrs[] = { + &tcm_loop_wwn_version.attr, +NULL , +}; + +/* End items for tcm_loop_cit */ + +int tcm_loop_register_configfs (void ) +{ +struct target_fabric_configfs *fabric; +struct config_group *tf_cg; +int ret; +/* +* Set the TCM Loop HBA counter to zero +*/ + tcm_loop_hba_no_cnt =0 ; +/* +* Register the top level struct config_item_type with TCM core +*/ + fabric =target_fabric_configfs_init (THIS_MODULE ,"loopback" ); +if (!(fabric)) { +printk (KERN_ERR "tcm_loop_register_configfs() failed!\n" ); +return -1 ; + } +/* +* Setup the fabric API of function pointers used by target_core_mod +*/ + fabric->tf_ops.get_fabric_name = &tcm_loop_get_fabric_name; + fabric->tf_ops.get_fabric_proto_ident = &tcm_loop_get_fabric_proto_ident; + fabric->tf_ops.tpg_get_wwn = &tcm_loop_get_endpoint_wwn; + fabric->tf_ops.tpg_get_tag = &tcm_loop_get_tag; + fabric->tf_ops.tpg_get_default_depth = &tcm_loop_get_default_depth; + fabric->tf_ops.tpg_get_pr_transport_id = &tcm_loop_get_pr_transport_id; + fabric->tf_ops.tpg_get_pr_transport_id_len = + &tcm_loop_get_pr_transport_id_len; + fabric->tf_ops.tpg_parse_pr_out_transport_id = + &tcm_loop_parse_pr_out_transport_id; + fabric->tf_ops.tpg_check_demo_mode = &tcm_loop_check_demo_mode; + fabric->tf_ops.tpg_check_demo_mode_cache = + &tcm_loop_check_demo_mode_cache; + fabric->tf_ops.tpg_check_demo_mode_write_protect = + &tcm_loop_check_demo_mode_write_protect; +/* +* The TCM loopback fabric module runs in demo-mode to a local +* virtual SCSI device, so fabric dependent initator ACLs are +* not required. +*/ + fabric->tf_ops.tpg_alloc_fabric_acl = &tcm_loop_tpg_alloc_fabric_acl; + fabric->tf_ops.tpg_release_fabric_acl = + &tcm_loop_tpg_release_fabric_acl; +#ifdef SNMP_SUPPORT + fabric->tf_ops.tpg_get_inst_index = &tcm_loop_get_inst_index; +#endif /* SNMP_SUPPORT */ +/* +* Since tcm_loop is mapping physical memory from Linux/SCSI +* struct scatterlist arrays for each struct scsi_cmnd I/O, +* we do not need TCM to allocate a iovec array for +* virtual memory address mappings +*/ + fabric->tf_ops.alloc_cmd_iovecs =NULL ; + fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free; + fabric->tf_ops.release_cmd_to_pool = &tcm_loop_deallocate_core_cmd; + fabric->tf_ops.release_cmd_direct = &tcm_loop_deallocate_core_cmd; + fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session; + fabric->tf_ops.close_session = &tcm_loop_close_session; + fabric->tf_ops.stop_session = &tcm_loop_stop_session; + fabric->tf_ops.fall_back_to_erl0 = &tcm_loop_fall_back_to_erl0; + fabric->tf_ops.sess_logged_in = &tcm_loop_sess_logged_in; +#ifdef SNMP_SUPPORT + fabric->tf_ops.sess_get_index = &tpg_loop_sess_get_index: +#endif /* SNMP_SUPPORT */ + fabric->tf_ops.sess_get_initiator_sid =NULL ; + fabric->tf_ops.write_pending = &tcm_loop_write_pending; + fabric->tf_ops.write_pending_status = &tcm_loop_write_pending_status; +/* +* Not used for TCM loopback +*/ + fabric->tf_ops.set_default_node_attributes = + &tcm_loop_set_default_node_attributes; + fabric->tf_ops.get_task_tag = &tcm_loop_get_task_tag; + fabric->tf_ops.get_cmd_state = &tcm_loop_get_cmd_state; + fabric->tf_ops.new_cmd_failure = &tcm_loop_new_cmd_failure; + fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in; + fabric->tf_ops.queue_status = &tcm_loop_queue_status; + fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp; + fabric->tf_ops.set_fabric_sense_len = &tcm_loop_set_fabric_sense_len; + fabric->tf_ops.get_fabric_sense_len = &tcm_loop_get_fabric_sense_len; + fabric->tf_ops.is_state_remove = &tcm_loop_is_state_remove; + fabric->tf_ops.pack_lun = &tcm_loop_pack_lun; + + tf_cg = &fabric->tf_group; +/* +* Setup function pointers for generic logic in target_core_fabric_configfs.c +*/ + fabric->tf_ops.fabric_make_wwn = &tcm_loop_make_scsi_hba; + fabric->tf_ops.fabric_drop_wwn = &tcm_loop_drop_scsi_hba; + fabric->tf_ops.fabric_make_tpg = &tcm_loop_make_naa_tpg; + fabric->tf_ops.fabric_drop_tpg = &tcm_loop_drop_naa_tpg; +/* +* fabric_post_link() and fabric_pre_unlink() are used for +* registration and release of TCM Loop Virtual SCSI LUNs. +*/ + fabric->tf_ops.fabric_post_link = &tcm_loop_port_link; + fabric->tf_ops.fabric_pre_unlink = &tcm_loop_port_unlink; + fabric->tf_ops.fabric_make_np =NULL ; + fabric->tf_ops.fabric_drop_np =NULL ; +/* +* Setup default attribute lists for various fabric->tf_cit_tmpl +*/ +TF_CIT_TMPL (fabric)->tfc_wwn_cit.ct_attrs = tcm_loop_wwn_attrs; +TF_CIT_TMPL (fabric)->tfc_tpg_base_cit.ct_attrs = tcm_loop_tpg_attrs; +TF_CIT_TMPL (fabric)->tfc_tpg_attrib_cit.ct_attrs =NULL ; +TF_CIT_TMPL (fabric)->tfc_tpg_param_cit.ct_attrs =NULL ; +TF_CIT_TMPL (fabric)->tfc_tpg_np_base_cit.ct_attrs =NULL ; +/* +* Once fabric->tf_ops has been setup, now register the fabric for +* use within TCM +*/ + ret =target_fabric_configfs_register (fabric); +if (ret <0 ) { +printk (KERN_ERR "target_fabric_configfs_register() for" +" LIO-Target failed!\n" ); +target_fabric_configfs_free (fabric); +return -1 ; + } +/* +* Setup our local pointer to *fabric. +*/ + tcm_loop_fabric_configfs = fabric; +printk (KERN_INFO "TCM_LOOP[0] - Set fabric ->" +" tcm_loop_fabric_configfs\n" ); +return 0 ; +} + +void tcm_loop_deregister_configfs (void ) +{ +if (!(tcm_loop_fabric_configfs)) +return ; + +target_fabric_configfs_deregister (tcm_loop_fabric_configfs); + tcm_loop_fabric_configfs =NULL ; +printk (KERN_INFO "TCM_LOOP[0] - Cleared" +" tcm_loop_fabric_configfs\n" ); +}drivers/target/tcm_loop/tcm_loop_configfs.h new file +2
-0@@ -0,0 +1,2 @@ +extern int tcm_loop_register_configfs (void ); +extern void tcm_loop_deregister_configfs (void );drivers/target/tcm_loop/tcm_loop_core.h new file +79
-0@@ -0,0 +1,79 @@ +#define TCM_LOOP_VERSION "v2.0" +#define TL_WWN_ADDR_LEN 256 +#define TL_TPGS_PER_HBA 32 +/* +* Defaults for struct scsi_host_template tcm_loop_driver_template +* +* We use large can_queue and cmd_per_lun here and let TCM enforce +* the underlying se_device_t->queue_depth. +*/ +#define TL_SCSI_CAN_QUEUE 1024 +#define TL_SCSI_CMD_PER_LUN 1024 +#define TL_SCSI_MAX_SECTORS 1024 +#define TL_SCSI_SG_TABLESIZE 256 +/* +* Used in tcm_loop_driver_probe() for struct Scsi_Host->max_cmd_len +*/ +#define TL_SCSI_MAX_CMD_LEN 16 + +#ifdef TCM_LOOP_CDB_DEBUG +# define TL_CDB_DEBUG(x...) printk(KERN_INFO x) +#else +# define TL_CDB_DEBUG(x...) +#endif + +struct tcm_loop_cmd { +/* Data Direction from Linux/SCSI CDB+Data descriptor */ +int sc_data_direction; +/* State of Linux/SCSI CDB+Data descriptor */ +u32 sc_cmd_state; +/* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */ +struct scsi_cmnd *sc; +/* Pointer to the TCM allocated struct se_cmd */ +struct se_cmd *tl_se_cmd; +struct list_head *tl_cmd_list; +}; + +struct tcm_loop_tmr { +atomic_t tmr_complete; +wait_queue_head_t tl_tmr_wait; +}; + +struct tcm_loop_nexus { +int it_nexus_active; +/* +* Pointer to Linux/SCSI HBA from linux/include/scsi_host.h +*/ +struct scsi_host *sh; +/* +* Pointer to TCM session for I_T Nexus +*/ +struct se_session *se_sess; +}; + +struct tcm_loop_nacl { +struct se_node_acl se_node_acl; +}; + +struct tcm_loop_tpg { +unsigned short tl_tpgt; +atomic_t tl_tpg_port_count; +struct se_portal_group tl_se_tpg; +struct tcm_loop_hba *tl_hba; +}; + +struct tcm_loop_hba { +u8 tl_proto_id; +unsigned char tl_wwn_address[TL_WWN_ADDR_LEN ]; +struct se_hba_s *se_hba; +struct se_lun *tl_hba_lun; +struct se_port *tl_hba_lun_sep; +struct se_device_s *se_dev_hba_ptr; +struct se_queue_obj *tl_hba_qobj; +struct task_struct *tl_kthread; +struct tcm_loop_nexus *tl_nexus; +struct device dev; +struct Scsi_Host *sh; +struct tcm_loop_tpg tl_hba_tpgs[TL_TPGS_PER_HBA ]; +struct se_wwn tl_hba_wwn; +};drivers/target/tcm_loop/tcm_loop_fabric.c new file +75
-0@@ -0,0 +1,567 @@ +/******************************************************************************* +* Filename: tcm_loop_fabric.c +* +* This file contains the TCM loopback fabric module for initiator and target +* side CDB level emulation of SAS, FC and iSCSI ports to Linux/SCSI LUNs. +* +* Copyright (c) 2009, 2010 Rising Tide, Inc. +* Copyright (c) 2009, 2010 Linux-iSCSI.org +* +* Copyright (c) 2009, 2010 Nicholas A. Bellinger <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +****************************************************************************/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/string.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_cmnd.h> + +#include <target/target_core_base.h> +#include <target/target_core_transport.h> +#include <target/target_core_fabric_ops.h> +#include <target/target_core_fabric_lib.h> +#include <target/target_core_device.h> +#include <target/target_core_tpg.h> +#include <target/target_core_configfs.h> + +#include <tcm_loop_core.h> +#include <tcm_loop_configfs.h> +#include <tcm_loop_fabric_scsi.h> + +struct kmem_cache *tcm_loop_cmd_cache; + +char *tcm_loop_get_fabric_name (void ) +{ +return "loopback" ; +} + +u8 tcm_loop_get_fabric_proto_ident (struct se_portal_group *se_tpg) +{ +struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; +struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; +/* +* tl_proto_id is set at tcm_loop_configfs.c:tcm_loop_make_scsi_hba() +* time based on the protocol dependent prefix of the passed configfs group. +* +* Based upon tl_proto_id, TCM_Loop emulates the requested fabric +* ProtocolID using target_core_fabric_lib.c symbols. +*/ +switch (tl_hba->tl_proto_id) { +case SCSI_PROTOCOL_SAS : +return sas_get_fabric_proto_ident (se_tpg); +case SCSI_PROTOCOL_FCP : +return fc_get_fabric_proto_ident (se_tpg); +case SCSI_PROTOCOL_ISCSI : +return iscsi_get_fabric_proto_ident (se_tpg); +default : +printk (KERN_ERR "Unknown tl_proto_id: 0x%02x, using" +" SAS emulation\n" , tl_hba->tl_proto_id);
drivers/target/tcm_loop/tcm_loop_fabric.c line 85
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric.c new file +10
-0+break ; + } + +return sas_get_fabric_proto_ident (se_tpg); +} + +char *tcm_loop_get_endpoint_wwn (struct se_portal_group *se_tpg) +{ +struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr;
Stray whitespace at the end.
Quoted 343 lines
drivers/target/tcm_loop/tcm_loop_fabric.c new file +343
-0+/* +* Return the passed NAA identifier for the SAS Target Port +*/ +return &tl_tpg->tl_hba->tl_wwn_address[0 ]; +} + +u16 tcm_loop_get_tag (struct se_portal_group *se_tpg) +{ +struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; +/* +* This Tag is used when forming SCSI Name identifier in EVPD=1 0x83 +* to represent the SCSI Target Port. +*/ +return tl_tpg->tl_tpgt; +} + +u32 tcm_loop_get_default_depth (struct se_portal_group *se_tpg) +{ +return 1 ; +} + + +u32 tcm_loop_get_pr_transport_id ( +struct se_portal_group *se_tpg, +struct se_node_acl *se_nacl, +struct t10_pr_registration *pr_reg, +int *format_code, +unsigned char *buf) +{ +struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; +struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + +switch (tl_hba->tl_proto_id) { +case SCSI_PROTOCOL_SAS : +return sas_get_pr_transport_id (se_tpg, se_nacl, pr_reg, + format_code, buf); +case SCSI_PROTOCOL_FCP : +return fc_get_pr_transport_id (se_tpg, se_nacl, pr_reg, + format_code, buf); +case SCSI_PROTOCOL_ISCSI : +return iscsi_get_pr_transport_id (se_tpg, se_nacl, pr_reg, + format_code, buf); +default : +printk (KERN_ERR "Unknown tl_proto_id: 0x%02x, using" +" SAS emulation\n" , tl_hba->tl_proto_id); +break ; + } + +return sas_get_pr_transport_id (se_tpg, se_nacl, pr_reg, + format_code, buf); +} + +u32 tcm_loop_get_pr_transport_id_len ( +struct se_portal_group *se_tpg, +struct se_node_acl *se_nacl, +struct t10_pr_registration *pr_reg, +int *format_code) +{ +struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; +struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + +switch (tl_hba->tl_proto_id) { +case SCSI_PROTOCOL_SAS : +return sas_get_pr_transport_id_len (se_tpg, se_nacl, pr_reg, + format_code); +case SCSI_PROTOCOL_FCP : +return fc_get_pr_transport_id_len (se_tpg, se_nacl, pr_reg, + format_code); +case SCSI_PROTOCOL_ISCSI : +return iscsi_get_pr_transport_id_len (se_tpg, se_nacl, pr_reg, + format_code); +default : +printk (KERN_ERR "Unknown tl_proto_id: 0x%02x, using" +" SAS emulation\n" , tl_hba->tl_proto_id); +break ; + } + +return sas_get_pr_transport_id_len (se_tpg, se_nacl, pr_reg, + format_code); +} + +/* +* Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above +* Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations. +*/ +char *tcm_loop_parse_pr_out_transport_id ( +struct se_portal_group *se_tpg, +const char *buf, +u32 *out_tid_len, +char **port_nexus_ptr) +{ +struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; +struct tcm_loop_hba *tl_hba = tl_tpg->tl_hba; + +switch (tl_hba->tl_proto_id) { +case SCSI_PROTOCOL_SAS : +return sas_parse_pr_out_transport_id (se_tpg, buf, out_tid_len, + port_nexus_ptr); +case SCSI_PROTOCOL_FCP : +return fc_parse_pr_out_transport_id (se_tpg, buf, out_tid_len, + port_nexus_ptr); +case SCSI_PROTOCOL_ISCSI : +return iscsi_parse_pr_out_transport_id (se_tpg, buf, out_tid_len, + port_nexus_ptr); +default : +printk (KERN_ERR "Unknown tl_proto_id: 0x%02x, using" +" SAS emulation\n" , tl_hba->tl_proto_id); +break ; + } + +return sas_parse_pr_out_transport_id (se_tpg, buf, out_tid_len, + port_nexus_ptr); +} + +/* +* Returning (1) here allows for target_core_mod struct se_node_acl to be generated +* based upon the incoming fabric dependent SCSI Initiator Port +*/ +int tcm_loop_check_demo_mode (struct se_portal_group *se_tpg) +{ +return 1 ; +} + +int tcm_loop_check_demo_mode_cache (struct se_portal_group *se_tpg) +{ +return 0 ; +} + +/* +* Allow I_T Nexus full READ-WRITE access without explict Initiator Node ACLs for +* local virtual Linux/SCSI LLD passthrough into VM hypervisor guest +*/ +int tcm_loop_check_demo_mode_write_protect (struct se_portal_group *se_tpg) +{ +return 0 ; +} + +struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl ( +struct se_portal_group *se_tpg) +{ +struct tcm_loop_nacl *tl_nacl; + + tl_nacl =kzalloc (sizeof (struct tcm_loop_nacl ),GFP_KERNEL ); +if (!(tl_nacl)) { +printk (KERN_ERR "Unable to allocate struct tcm_loop_nacl\n" ); +return NULL ; + } + +return &tl_nacl->se_node_acl; +} + +void tcm_loop_tpg_release_fabric_acl ( +struct se_portal_group *se_tpg, +struct se_node_acl *se_nacl) +{ +struct tcm_loop_nacl *tl_nacl =container_of (se_nacl, + struct tcm_loop_nacl, se_node_acl); + +kfree (tl_nacl); +} + +#ifdef SNMP_SUPPORT +u32 tcm_loop_get_tpg_inst_index (struct se_portal_group *se_tpg) +{ +struct tcm_loop_tpg *tl_tpg = + (struct tcm_loop_tpg *)se_tpg->se_tpg_fabric_ptr; + +// return tpg->tpg_tiqn->tiqn_index; +return 1 ; +} +#endif /* SNMP_SUPPORT */ + +void tcm_loop_new_cmd_failure (struct se_cmd *se_cmd) +{ +/* +* Since TCM_loop is already passing struct scatterlist data from +* struct scsi_cmnd, no more Linux/SCSI failure dependent state need +* to be handled here. +*/ +return ; +} + +int tcm_loop_is_state_remove (struct se_cmd *se_cmd) +{ +/* +* Assume struct scsi_cmnd is not in remove state.. +*/ +return 0 ; +} + +int tcm_loop_sess_logged_in (struct se_session *se_sess) +{ +/* +* Assume that TL Nexus is always active +*/ +return 1 ; +} + +#ifdef SNMP_SUPPORT +u32 tpg_loop_sess_get_index (struct se_session *se_sess) +{ +return 1 ; +} +#endif /* SNMP_SUPPORT */ + + +void tcm_loop_set_default_node_attributes (struct se_node_acl *se_acl) +{ +return ; +} + +u32 tcm_loop_get_task_tag (struct se_cmd *se_cmd) +{ +return 1 ; +} + +int tcm_loop_get_cmd_state (struct se_cmd *se_cmd) +{ +struct tcm_loop_cmd *tl_cmd = + (struct tcm_loop_cmd *)se_cmd->se_fabric_cmd_ptr; + +return tl_cmd->sc_cmd_state; +} + +//#warning FIXME: tcm_loop_shutdown_session() +int tcm_loop_shutdown_session (struct se_session *se_sess) +{ +BUG (); +return 0 ; +} + +//#warning FIXME: tcm_loop_close_session() +void tcm_loop_close_session (struct se_session *se_sess) +{ +BUG (); +}; + + +//#warning FIXME: tcm_loop_stop_session() +void tcm_loop_stop_session ( +struct se_session *se_sess, +int sess_sleep, +int conn_sleep) +{ +BUG (); +} + + +//#warning FIXME: tcm_loop_fall_back_to_erl0() +void tcm_loop_fall_back_to_erl0 (struct se_session *se_sess) +{ +BUG (); +} + +int tcm_loop_write_pending (struct se_cmd *se_cmd) +{ +/* +* Since Linux/SCSI has already sent down a struct scsi_cmnd +* sc->sc_data_direction of DMA_TO_DEVICE with struct scatterlist array +* memory, and memory has already been mapped to struct se_cmd->t_mem_list +* format with transport_generic_map_mem_to_cmd(). +* +* For the TCM control CDBs using a contiguous buffer, do the memcpy +* from the passed Linux/SCSI struct scatterlist located at +* T_TASK(se_cmd)->t_task_pt_buf to the contiguous buffer at +* T_TASK(se_cmd)->t_task_buf. +*/ +if (se_cmd->se_cmd_flags &SCF_PASSTHROUGH_CONTIG_TO_SG ) { +TL_CDB_DEBUG ("Calling transport_memcpy_read_contig()" +" for SCF_PASSTHROUGH_CONTIG_TO_SG\n" ); +transport_memcpy_read_contig (se_cmd, +T_TASK (se_cmd)->t_task_buf, +T_TASK (se_cmd)->t_task_pt_buf); + } +/* +* We now tell TCM to add this WRITE CDB directly into the TCM storage +* object execution queue. +*/ +transport_generic_process_write (se_cmd); +return 0 ; +} + +int tcm_loop_write_pending_status (struct se_cmd *se_cmd) +{ +return 0 ; +} + +int tcm_loop_queue_data_in (struct se_cmd *se_cmd) +{ +struct tcm_loop_cmd *tl_cmd = + (struct tcm_loop_cmd *)se_cmd->se_fabric_cmd_ptr; +struct scsi_cmnd *sc = tl_cmd->sc; +unsigned long flags; + +TL_CDB_DEBUG ("tcm_loop_queue_data_in() called for scsi_cmnd: %p" +" cdb: 0x%02x\n" , sc, sc->cmnd[0 ]); + +if (se_cmd->se_cmd_flags &SCF_PASSTHROUGH_CONTIG_TO_SG ) { +TL_CDB_DEBUG ("Calling transport_memcpy_write_contig()" +" for SCF_PASSTHROUGH_CONTIG_TO_SG\n" ); +transport_memcpy_write_contig (se_cmd, +T_TASK (se_cmd)->t_task_pt_buf, +T_TASK (se_cmd)->t_task_buf); + } + + sc->result =host_byte (DID_OK ) |SAM_STAT_GOOD ; + +spin_lock_irqsave (sc->device->host->host_lock, flags); + (*sc->scsi_done)(sc); +spin_unlock_irqrestore (sc->device->host->host_lock, flags); +return 0 ; +} + +int tcm_loop_queue_status (struct se_cmd *se_cmd) +{ +struct tcm_loop_cmd *tl_cmd = + (struct tcm_loop_cmd *)se_cmd->se_fabric_cmd_ptr; +struct scsi_cmnd *sc = tl_cmd->sc; +unsigned long flags; + +TL_CDB_DEBUG ("tcm_loop_queue_status() called for scsi_cmnd: %p" +" cdb: 0x%02x\n" , sc, sc->cmnd[0 ]); + +if (se_cmd->sense_buffer && + ((se_cmd->se_cmd_flags &SCF_TRANSPORT_TASK_SENSE ) || + (se_cmd->se_cmd_flags &SCF_EMULATED_TASK_SENSE ))) { + +memcpy ((void *)sc->sense_buffer, (void *)se_cmd->sense_buffer, +SCSI_SENSE_BUFFERSIZE ); + sc->result =host_byte (DID_OK ) |driver_byte (DRIVER_SENSE ) | +SAM_STAT_CHECK_CONDITION ; + }else + sc->result =host_byte (DID_OK ) | se_cmd->scsi_status; + +spin_lock_irqsave (sc->device->host->host_lock, flags); + (*sc->scsi_done)(sc); +spin_unlock_irqrestore (sc->device->host->host_lock, flags); +return 0 ; +}
drivers/target/tcm_loop/tcm_loop_fabric.c line 438
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric.c new file +10
-0+ +int tcm_loop_queue_tm_rsp (struct se_cmd *se_cmd) +{ +struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; +struct tcm_loop_tmr *tl_tmr = (struct tcm_loop_tmr *)se_tmr->fabric_tmr_ptr; +/* +* The SCSI EH thread will be sleeping on se_tmr->tl_tmr_wait, go ahead +* and wake up the wait_queue_head_t in tcm_loop_device_reset() +*/ +atomic_set (&tl_tmr->tmr_complete,1 );
Does it really need to be atomic? Maybe a completion (as in wait_for_completion()) instead?
Quoted 30 lines
drivers/target/tcm_loop/tcm_loop_fabric.c new file +30
-0+wake_up (&tl_tmr->tl_tmr_wait); +return 0 ; +} + +u16 tcm_loop_set_fabric_sense_len (struct se_cmd *se_cmd,u32 sense_length) +{ +return 0 ; +} + +u16 tcm_loop_get_fabric_sense_len (void ) +{ +return 0 ; +} + +u64 tcm_loop_pack_lun (unsigned int lun) +{ +u64 result; + +/* LSB of lun into byte 1 big-endian */ + result = ((lun &0xff ) <<8 ); +/* use flat space addressing method */ + result |=0x40 | ((lun >>8 ) &0x3f ); + +return cpu_to_le64 (result); +} + +static struct se_queue_req *tcm_loop_get_qr_from_queue (struct se_queue_obj *qobj) +{ +struct se_queue_req *qr; +unsigned long flags;
drivers/target/tcm_loop/tcm_loop_fabric.c line 478
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric.c new file +10
-0+ +spin_lock_irqsave (&qobj->cmd_queue_lock, flags); +if (list_empty (&qobj->qobj_list)) { +spin_unlock_irqrestore (&qobj->cmd_queue_lock, flags); +return NULL ; + } + + qr = list_first_entry(&qobj->qobj_list, struct se_queue_req, qr_list); +list_del (&qr->qr_list); +atomic_dec (&qobj->queue_cnt);
You seem to manipulate queue_cnt within a spinlock-protected code block (or within vicinity of one and can move it there). Does it have to be atomic as well?
Quoted 130 lines
drivers/target/tcm_loop/tcm_loop_fabric.c new file +89
-0+spin_unlock_irqrestore (&qobj->cmd_queue_lock, flags); + +return qr; +} + +int tcm_loop_processing_thread (void *p) +{ +struct scsi_cmnd *sc; +struct tcm_loop_cmd *tl_cmd; +struct tcm_loop_hba *tl_hba = (struct tcm_loop_hba *)p; +struct se_queue_obj *qobj = tl_hba->tl_hba_qobj; +struct se_queue_req *qr; +int ret; + + current->policy =SCHED_NORMAL ; +set_user_nice (current,-20 ); +spin_lock_irq (¤t->sighand->siglock); +siginitsetinv (¤t->blocked,SHUTDOWN_SIGS ); +recalc_sigpending (); +spin_unlock_irq (¤t->sighand->siglock); + +complete (&qobj->thread_create_comp); + +while (!(kthread_should_stop ())) { + ret =wait_event_interruptible (qobj->thread_wq, +atomic_read (&qobj->queue_cnt) ||kthread_should_stop ()); +if (ret <0 ) + goto out; + + qr =tcm_loop_get_qr_from_queue (qobj); +if (!(qr)) +continue ; + + tl_cmd = (struct tcm_loop_cmd *)qr->cmd; + sc = tl_cmd->sc; +kfree (qr); + +TL_CDB_DEBUG ("processing_thread, calling tcm_loop_execute" +"_core_cmd() for tl_cmd: %p, sc: %p\n" , tl_cmd, sc); +tcm_loop_execute_core_cmd (tl_cmd, sc); + } + +out: +complete (&qobj->thread_done_comp); +return 0 ; +} + +static int __init tcm_loop_fabric_init (void ) +{ +int ret; + + tcm_loop_cmd_cache =kmem_cache_create ("tcm_loop_cmd_cache" , +sizeof (struct tcm_loop_cmd ), + __alignof__(struct tcm_loop_cmd ), +0 ,NULL ); +if (!(tcm_loop_cmd_cache)) { +printk (KERN_ERR "kmem_cache_create() for" +" tcm_loop_cmd_cache failed\n" ); +return -1 ; + } + + ret =tcm_loop_alloc_core_bus (); +if (ret) +return ret; + + ret =tcm_loop_register_configfs (); +if (ret) { +tcm_loop_release_core_bus (); +return ret; + } + +return 0 ; +} + +static void tcm_loop_fabric_exit (void ) +{ +tcm_loop_deregister_configfs (); +tcm_loop_release_core_bus (); +kmem_cache_destroy (tcm_loop_cmd_cache); +} + +#ifdef MODULE +MODULE_DESCRIPTION ("TCM loopback virtual Linux/SCSI fabric module" ); +MODULE_AUTHOR ("Nicholas A. Bellinger <[email protected]>" ); +MODULE_LICENSE ("GPL" ); +module_init (tcm_loop_fabric_init); +module_exit (tcm_loop_fabric_exit); +#endif /* MODULE */ +drivers/target/tcm_loop/tcm_loop_fabric.h new file +35
-0@@ -0,0 +1,45 @@ +extern char *tcm_loop_get_fabric_name (void ); +extern u8 tcm_loop_get_fabric_proto_ident (struct se_portal_group *); +extern char *tcm_loop_get_endpoint_wwn (struct se_portal_group *); +extern u16 tcm_loop_get_tag (struct se_portal_group *); +extern u32 tcm_loop_get_default_depth (struct se_portal_group *); +extern u32 tcm_loop_get_pr_transport_id (struct se_portal_group *,struct se_node_acl *, +struct t10_pr_registration *,int *, +unsigned char *); +extern u32 tcm_loop_get_pr_transport_id_len (struct se_portal_group *, +struct se_node_acl *,struct t10_pr_registration *, +int *); +extern char *tcm_loop_parse_pr_out_transport_id (struct se_portal_group *, +const char *,u32 *,char **); +extern int tcm_loop_check_demo_mode (struct se_portal_group *); +extern int tcm_loop_check_demo_mode_cache (struct se_portal_group *); +extern int tcm_loop_check_demo_mode_write_protect (struct se_portal_group *); +extern struct se_node_acl *tcm_loop_tpg_alloc_fabric_acl ( +struct se_portal_group *); +void tcm_loop_tpg_release_fabric_acl (struct se_portal_group *,struct se_node_acl *); +#ifdef SNMP_SUPPORT +extern u32 tcm_loop_tpg_get_inst_index (struct se_portal_group *); +#endif /* SNMP_SUPPORT */ +extern void tcm_loop_new_cmd_failure (struct se_cmd *); +extern int tcm_loop_is_state_remove (struct se_cmd *); +extern int tcm_loop_sess_logged_in (struct se_session *); +#ifdef SNMP_SUPPORT +extern u32 tpg_loop_sess_get_index (struct se_session *); +#endif /* SNMP_SUPPORT */ +extern void tcm_loop_set_default_node_attributes (struct se_node_acl *); +extern u32 tcm_loop_get_task_tag (struct se_cmd *); +extern int tcm_loop_get_cmd_state (struct se_cmd *); +extern int tcm_loop_shutdown_session (struct se_session *); +extern void tcm_loop_close_session (struct se_session *); +extern void tcm_loop_stop_session (struct se_session *,int ,int ); +extern void tcm_loop_fall_back_to_erl0 (struct se_session *);
drivers/target/tcm_loop/tcm_loop_fabric.h line 45
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric.h new file +10
-0+extern int tcm_loop_write_pending (struct se_cmd *); +extern int tcm_loop_write_pending_status (struct se_cmd *); +extern int tcm_loop_queue_data_in (struct se_cmd *); +extern int tcm_loop_queue_status (struct se_cmd *); +extern int tcm_loop_queue_tm_rsp (struct se_cmd *); +extern u16 tcm_loop_set_fabric_sense_len (struct se_cmd *,u32 ); +extern u16 tcm_loop_get_fabric_sense_len (void ); +extern u64 tcm_loop_pack_lun (unsigned int ); + +extern int tcm_loop_processing_thread (void *);
'extern' is not really needed.
Quoted 108 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +102
-0@@ -0,0 +1,659 @@ +/******************************************************************************* +* Filename: tcm_loop_fabric_scsi.c +* +* This file contains the Linux/SCSI LLD virtual SCSI initiator driver +* for emulated SAS initiator ports +* +* Copyright (c) 2009-2010 Rising Tide, Inc. +* Copyright (c) 2009-2010 Linux-iSCSI.org +* +* Copyright (c) 2009-2010 Nicholas A. Bellinger <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +****************************************************************************/ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/version.h> +#include <generated/utsrelease.h> +#include <linux/utsname.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/string.h> +#include <scsi/scsi.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/libsas.h> /* For TASK_ATTR_* */ + +#include <target/target_core_base.h> +#include <target/target_core_transport.h> +#include <target/target_core_fabric_ops.h> +#include <target/target_core_device.h> +#include <target/target_core_tpg.h> +#include <target/target_core_tmr.h> + +#include <tcm_loop_core.h> +#include <tcm_loop_fabric.h> +#include <tcm_loop_fabric_scsi.h> + +#define to_tcm_loop_hba (hba) container_of(hba, struct tcm_loop_hba, dev) + +/* +* Allocate a tcm_loop cmd descriptor from target_core_mod code +* +* Can be called from interrupt context in tcm_loop_queuecommand() below +*/ +static struct tcm_loop_cmd *tcm_loop_allocate_core_cmd ( +struct tcm_loop_hba *tl_hba, +struct se_portal_group *se_tpg, +struct scsi_cmnd *sc, +int data_direction) +{ +struct se_session *se_sess; +struct tcm_loop_nexus *tl_nexus = tl_hba->tl_nexus; +struct tcm_loop_cmd *tl_cmd; +int sam_task_attr; + +if (!(tl_nexus)) { +scmd_printk (KERN_ERR , sc,"TCM_Loop I_T Nexus" +" does not exist\n" ); +return NULL ; + } + se_sess = tl_nexus->se_sess; + + tl_cmd =kmem_cache_zalloc (tcm_loop_cmd_cache,GFP_ATOMIC ); +if (!(tl_cmd)) { +printk (KERN_ERR "Unable to allocate struct tcm_loop_cmd\n" ); +return NULL ; + } +/* +* Save the pointer to struct scsi_cmnd *sc +*/ + tl_cmd->sc = sc; + +/* +* Locate the SAM Task Attr from struct scsi_cmnd * +*/ +if (sc->device->tagged_supported) { +switch (sc->tag) { +case HEAD_OF_QUEUE_TAG : + sam_task_attr =TASK_ATTR_HOQ ; +break ; +case ORDERED_QUEUE_TAG : + sam_task_attr =TASK_ATTR_ORDERED ; +break ; +default : + sam_task_attr =TASK_ATTR_SIMPLE ; +break ; + } + }else + sam_task_attr =TASK_ATTR_SIMPLE ; +/*
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 112
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +10
-0+* Allocate the struct se_cmd descriptor from target_core_mod infrastructure +*/ + tl_cmd->tl_se_cmd =transport_alloc_se_cmd (se_tpg->se_tpg_tfo, + se_sess, (void *)tl_cmd,scsi_bufflen (sc), + data_direction, sam_task_attr); +if (!(tl_cmd->tl_se_cmd)) { +kmem_cache_free (tcm_loop_cmd_cache, tl_cmd); +return NULL ; + } +
Whitespace.
Quoted 11 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +11
-0+return tl_cmd; +} + +/* +* Queue up the newly allocated struct tcm_loop_cmd to be processed by +* tcm_loop_fabri.c:tcm_loop_processing_thread() +* +* Can be called from interrupt context in tcm_loop_queuecommand() below +*/ +static int tcm_loop_queue_core_cmd( + struct se_queue_obj *qobj,
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 133
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +10
-0+ struct tcm_loop_cmd *tl_cmd) +{ +struct se_queue_req *qr; +unsigned long flags; + + qr =kzalloc (sizeof (struct se_queue_req ),GFP_ATOMIC ); +if (!(qr)) { +printk (KERN_ERR "Unable to allocate memory for" +" struct se_queue_req\n" ); +return -1 ;
Whitespace.
Quoted 111 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +111
-0+ } +INIT_LIST_HEAD (&qr->qr_list); + + qr->cmd = (void *)tl_cmd; +spin_lock_irqsave (&qobj->cmd_queue_lock, flags); +list_add_tail (&qr->qr_list, &qobj->qobj_list); +spin_unlock_irqrestore (&qobj->cmd_queue_lock, flags); + +atomic_inc (&qobj->queue_cnt); +wake_up_interruptible (&qobj->thread_wq); +return 0 ; +} + +/* +* Called by tcm_loop_processing_thread() in tcm_loop_fabric.c +* +* Always called in process context +*/ +int tcm_loop_execute_core_cmd (struct tcm_loop_cmd *tl_cmd,struct scsi_cmnd *sc) +{ +struct se_cmd *se_cmd = tl_cmd->tl_se_cmd; +void *mem_ptr; +int ret; +/* +* Locate the struct se_lun pointer and attach it to struct se_cmd +*/ +if (transport_get_lun_for_cmd (se_cmd,NULL , + tl_cmd->sc->device->lun) <0 ) { +/* NON_EXISTENT_LUN */ +transport_send_check_condition_and_sense (se_cmd, + se_cmd->scsi_sense_reason,0 ); +return 0 ; + } +/* +* Allocate the necessary tasks to complete the received CDB+data +*/ + ret =transport_generic_allocate_tasks (se_cmd, tl_cmd->sc->cmnd); +if (ret ==-1 ) { +/* Out of Resources */ +transport_send_check_condition_and_sense (se_cmd, +LOGICAL_UNIT_COMMUNICATION_FAILURE ,0 ); +return 0 ; + }else if (ret ==-2 ) { +/* +* Handle case for SAM_STAT_RESERVATION_CONFLICT +*/ +if (se_cmd->se_cmd_flags &SCF_SCSI_RESERVATION_CONFLICT ) { +tcm_loop_queue_status (se_cmd); +return 0 ; + } +/* +* Otherwise, return SAM_STAT_CHECK_CONDITION and return +* sense data. +*/ +transport_send_check_condition_and_sense (se_cmd, + se_cmd->scsi_sense_reason,0 ); +return 0 ; + } +/* +* Setup the struct scatterlist memory from the received +* struct scsi_cmnd. +*/ +if (scsi_sg_count (sc)) { + se_cmd->se_cmd_flags |=SCF_PASSTHROUGH_SG_TO_MEM ; + mem_ptr = (void *)scsi_sglist (sc); + }else { +/* +* Used for DMA_NONE +*/ + mem_ptr =NULL ; + } +/* +* Map the SG memory into struct se_mem->page linked list using the same +* physical memory at sg->page_link. +*/ + ret =transport_generic_map_mem_to_cmd (se_cmd, mem_ptr, +scsi_sg_count (sc)); +if (ret <0 ) { +transport_send_check_condition_and_sense (se_cmd, +LOGICAL_UNIT_COMMUNICATION_FAILURE ,0 ); +return 0 ; + } +/* +* Queue up the struct se_cmd + tasks to be processed by the +* TCM storage object. +*/ +return transport_generic_handle_cdb (se_cmd); +} + +/* +* Called from struct target_core_fabric_ops->check_stop_free() +*/ +void tcm_loop_check_stop_free (struct se_cmd *se_cmd) +{ +/* +* Release the struct se_cmd, which will make a callback to release +* struct tcm_loop_cmd * in tcm_loop_deallocate_core_cmd() +*/ +transport_generic_free_cmd (se_cmd,0 ,1 ,0 ); +} + +/* +* Called from struct target_core_fabric_ops->releastruct se_cmdo_pool() +*/ +void tcm_loop_deallocate_core_cmd (struct se_cmd *se_cmd) +{ +struct tcm_loop_cmd *tl_cmd = + (struct tcm_loop_cmd *)se_cmd->se_fabric_cmd_ptr; + +kmem_cache_free (tcm_loop_cmd_cache, tl_cmd); +}
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 254
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +10
-0+ +void tcm_loop_scsi_forget_host (struct Scsi_Host *shost) +{ +struct scsi_device *sdev, *tmp; +unsigned long flags; + +spin_lock_irqsave (shost->host_lock, flags); +list_for_each_entry_safe (sdev, tmp, &shost->__devices, siblings) { +spin_unlock_irqrestore (shost->host_lock, flags); +scsi_remove_device (sdev);
You may miss some entries this way; you need to restart from the head.
Quoted 88 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +88
-0+spin_lock_irqsave (shost->host_lock, flags); + } +spin_unlock_irqrestore (shost->host_lock, flags); +} + +static int tcm_loop_proc_info (struct Scsi_Host *host,char *buffer, +char **start,off_t offset, +int length,int inout) +{ +return sprintf (buffer,"tcm_loop_proc_info()\n" ); +} + +static int tcm_loop_driver_probe (struct device *); +static int tcm_loop_driver_remove (struct device *); + +static int pseudo_lld_bus_match (struct device *dev, +struct device_driver *dev_driver) +{ +return 1 ; +} + +static struct bus_type tcm_loop_lld_bus = { + .name ="tcm_loop_bus" , + .match = pseudo_lld_bus_match, + .probe = tcm_loop_driver_probe, + .remove = tcm_loop_driver_remove, +}; + +static struct device_driver tcm_loop_driverfs = { + .name ="tcm_loop" , + .bus = &tcm_loop_lld_bus, +}; + +static void tcm_loop_primary_release (struct device *dev) +{ +return ; +} + +static struct device tcm_loop_primary = { + .init_name ="tcm_loop_0" , + .release = tcm_loop_primary_release, +}; + +/* +* Copied from drivers/scsi/libfc/fc_fcp.c:fc_change_queue_depth() and +* drivers/scsi/libiscsi.c:iscsi_change_queue_depth() +*/ +static int tcm_loop_change_queue_depth ( +struct scsi_device *sdev, +int depth, +int reason) +{ +switch (reason) { +case SCSI_QDEPTH_DEFAULT : +scsi_adjust_queue_depth (sdev,scsi_get_tag_type (sdev), depth); +break ; +case SCSI_QDEPTH_QFULL : +scsi_track_queue_full (sdev, depth); +break ; +case SCSI_QDEPTH_RAMP_UP : +scsi_adjust_queue_depth (sdev,scsi_get_tag_type (sdev), depth); +break ; +default : +return -EOPNOTSUPP ; + } +return sdev->queue_depth; +} + +static inline struct tcm_loop_hba *tcm_loop_get_hba (struct scsi_cmnd *sc) +{ +return (struct tcm_loop_hba *)sc->device->host->hostdata[0 ]; +} + +/* +* Main entry point from struct scsi_host_template for incoming SCSI CDB+Data +* from Linux/SCSI subsystem for SCSI low level device drivers (LLDs) +*/ +static int tcm_loop_queuecommand ( +struct scsi_cmnd *sc, +void (*done)(struct scsi_cmnd *)) +{ +struct se_portal_group *se_tpg; +struct Scsi_Host *host = sc->device->host; +struct tcm_loop_cmd *tl_cmd; +struct tcm_loop_hba *tl_hba; +struct tcm_loop_tpg *tl_tpg; +int data_direction; +
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 352
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +10
-0+ sc->scsi_done = done; + +TL_CDB_DEBUG ("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x" +" scsi_buf_len: %u\n" , sc->device->host->host_no, + sc->device->id, sc->device->channel, sc->device->lun, + sc->cmnd[0 ],scsi_bufflen (sc)); + +spin_unlock_irq (host->host_lock); +/* +* Locate the tcm_loop_hba_t pointer
Whistepace.
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 360
Quoted 8 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +8
-0+*/ + tl_hba =tcm_loop_get_hba (sc); +if (!(tl_hba)) { +printk (KERN_ERR "Unable to locate struct tcm_loop_hba from" +" struct scsi_cmnd\n" ); + sc->result =host_byte (DID_ERROR ); + (*done)(sc); +return 0 ;
Whistepace.
Quoted 4 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +4
-0+ } + tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; + se_tpg = &tl_tpg->tl_se_tpg; +
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 374
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +10
-0+if (sc->sc_data_direction ==DMA_TO_DEVICE ) + data_direction =SE_DIRECTION_WRITE ; +else if (sc->sc_data_direction ==DMA_FROM_DEVICE ) + data_direction =SE_DIRECTION_READ ; +else if (sc->sc_data_direction ==DMA_NONE ) + data_direction =SE_DIRECTION_NONE ; +else { +spin_lock_irq (host->host_lock); +printk (KERN_ERR "Unsupported sc->sc_data_direction: %d\n" , + sc->sc_data_direction);
Whistepace.
Quoted 108 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +108
-0+ sc->result =host_byte (DID_ERROR ); + (*done)(sc); +return 0 ; + } +/* +* Determine the SAM Task Attribute and allocate tl_cmd and +* tl_cmd->tl_se_cmd from TCM infrastructure +*/ + tl_cmd =tcm_loop_allocate_core_cmd (tl_hba, se_tpg, sc, data_direction); +if (!(tl_cmd)) { +spin_lock_irq (host->host_lock); + sc->result =host_byte (DID_ERROR ); + (*done)(sc); +return 0 ; + } +/* +* Queue the tl_cmd to be executed in process context by the +* tcm_loop kernel thread +*/ +if (tcm_loop_queue_core_cmd (tl_hba->tl_hba_qobj, tl_cmd) <0 ) { +/* +* Will free both struct tcm_loop_cmd and struct se_cmd +*/ +transport_release_cmd_to_pool (tl_cmd->tl_se_cmd); +/* +* Reaquire the struct scsi_host->host_lock, and +* complete the struct scsi_cmnd +*/ +spin_lock_irq (host->host_lock); + sc->result =host_byte (DID_ERROR ); + (*done)(sc); +return 0 ; + } +/* +* Reaquire the the struct scsi_host->host_lock before returning +*/ +spin_lock_irq (host->host_lock); +return 0 ; +} + +/* +* Called from SCSI EH process context to issue a LUN_RESET TMR +* to struct scsi_device +*/ +static int tcm_loop_device_reset (struct scsi_cmnd *sc) +{ +struct se_cmd *se_cmd =NULL ; +struct se_portal_group *se_tpg; +struct se_session *se_sess; +struct tcm_loop_cmd *tl_cmd =NULL ; +struct tcm_loop_hba *tl_hba; +struct tcm_loop_nexus *tl_nexus; +struct tcm_loop_tmr *tl_tmr =NULL ; +struct tcm_loop_tpg *tl_tpg; +int ret =FAILED ; +/* +* Locate the tcm_loop_hba_t pointer +*/ + tl_hba =tcm_loop_get_hba (sc); +if (!(tl_hba)) { +printk (KERN_ERR "Unable to locate struct tcm_loop_hba from" +" struct scsi_cmnd\n" ); +return FAILED ; + } +/* +* Locate the tl_nexus and se_sess pointers +*/ + tl_nexus = tl_hba->tl_nexus; +if (!(tl_nexus)) { +printk (KERN_ERR "Unable to perform device reset without" +" active I_T Nexus\n" ); +return FAILED ; + } + se_sess = tl_nexus->se_sess; +/* +* Locate the tl_tpg and se_tpg pointers from TargetID in sc->device->id +*/ + tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; + se_tpg = &tl_tpg->tl_se_tpg; + + tl_cmd =kmem_cache_zalloc (tcm_loop_cmd_cache,GFP_KERNEL ); +if (!(tl_cmd)) { +printk (KERN_ERR "Unable to allocate memory for tl_cmd\n" ); +return FAILED ; + } + + tl_tmr =kzalloc (sizeof (struct tcm_loop_tmr ),GFP_KERNEL ); +if (!(tl_tmr)) { +printk (KERN_ERR "Unable to allocate memory for tl_tmr\n" ); + goto release; + } +init_waitqueue_head (&tl_tmr->tl_tmr_wait); +/* +* Allocate the struct se_cmd for a LUN_RESET TMR +*/ + tl_cmd->tl_se_cmd =transport_alloc_se_cmd (se_tpg->se_tpg_tfo, + se_sess, (void *)tl_cmd,0 ,SE_DIRECTION_NONE , +TASK_ATTR_SIMPLE ); +if (!(tl_cmd->tl_se_cmd)) + goto release; + se_cmd = tl_cmd->tl_se_cmd; +/* +* Allocate the LUN_RESET TMR +*/ + se_cmd->se_tmr_req =core_tmr_alloc_req (se_cmd, (void *)tl_tmr, +LUN_RESET ); +if (!(se_cmd->se_tmr_req)) + goto release;
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 492
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +10
-0+/* +* Locate the underlying TCM struct se_lun from sc->device->lun +*/ +if (transport_get_lun_for_tmr (se_cmd, sc->device->lun) <0 ) + goto release; +/* +* Queue the TMR to TCM Core and sleep waiting for tcm_loop_queue_tm_rsp() +* to wake us up. +*/ +transport_generic_handle_tmr (se_cmd);
Stray whitespace at the end.
Quoted 126 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +126
-0+wait_event (tl_tmr->tl_tmr_wait,atomic_read (&tl_tmr->tmr_complete)); +/* +* The TMR LUN_RESET has completed, check the response status and +* then release allocations. +*/ + ret = (se_cmd->se_tmr_req->response ==TMR_FUNCTION_COMPLETE ) ? +SUCCESS :FAILED ; +release: +if (se_cmd) +transport_generic_free_cmd (se_cmd,1 ,1 ,0 ); +else +kmem_cache_free (tcm_loop_cmd_cache, tl_cmd); +kfree (tl_tmr); +return ret; +} + +static struct scsi_host_template tcm_loop_driver_template = { + .proc_info = tcm_loop_proc_info, + .proc_name ="tcm_loopback" , + .name ="TCM_Loopback" , + .info =NULL , + .slave_alloc =NULL , + .slave_configure =NULL , + .slave_destroy =NULL , + .ioctl =NULL , + .queuecommand = tcm_loop_queuecommand, + .change_queue_depth = tcm_loop_change_queue_depth, + .eh_abort_handler =NULL , + .eh_bus_reset_handler =NULL , + .eh_device_reset_handler = tcm_loop_device_reset, + .eh_host_reset_handler =NULL , + .bios_param =NULL , + .can_queue =TL_SCSI_CAN_QUEUE , + .this_id =-1 , + .sg_tablesize =TL_SCSI_SG_TABLESIZE , + .cmd_per_lun =TL_SCSI_CMD_PER_LUN , + .max_sectors =TL_SCSI_MAX_SECTORS , + .use_clustering =DISABLE_CLUSTERING , + .module =THIS_MODULE , +}; + +static int tcm_loop_driver_probe (struct device *dev) +{ +struct tcm_loop_hba *tl_hba; +struct Scsi_Host *sh; +int error; + + tl_hba =to_tcm_loop_hba (dev); + + sh =scsi_host_alloc (&tcm_loop_driver_template, +sizeof (struct tcm_loop_hba )); +if (!(sh)) { +printk (KERN_ERR "Unable to allocate struct scsi_host\n" ); +return -ENODEV ; + } + tl_hba->sh = sh; + +/* +* Assign the struct tcm_loop_hba pointer to struct Scsi_Host->hostdata +*/ + sh->hostdata[0 ] = (unsigned long )tl_hba; +/* +* Setup single ID, Channel and LUN for now.. +*/ + sh->max_id =2 ; + sh->max_lun =0 ; + sh->max_channel =0 ; + sh->max_cmd_len =TL_SCSI_MAX_CMD_LEN ; + + error =scsi_add_host (sh, &tl_hba->dev); +if (error) { +printk (KERN_ERR "%s: scsi_add_host failed\n" , __func__); +scsi_host_put (sh); +return -ENODEV ; + } +return 0 ; +} + +static int tcm_loop_driver_remove (struct device *dev) +{ +struct tcm_loop_hba *tl_hba; +struct Scsi_Host *sh; + + tl_hba =to_tcm_loop_hba (dev); + sh = tl_hba->sh; + +scsi_remove_host (sh); +scsi_host_put (sh); +return 0 ; +} + +static void tcm_loop_release_adapter (struct device *dev) +{ +struct tcm_loop_hba *tl_hba =to_tcm_loop_hba (dev); + +kfree (tl_hba->tl_hba_qobj); +kfree (tl_hba); +} + +/* +* Called from tcm_loop_make_scsi_hba() in tcm_loop_configfs.c +*/ +int tcm_loop_setup_hba_bus (struct tcm_loop_hba *tl_hba,int tcm_loop_host_id) +{ +int ret; + + tl_hba->dev.bus = &tcm_loop_lld_bus; + tl_hba->dev.parent = &tcm_loop_primary; + tl_hba->dev.release = &tcm_loop_release_adapter; +dev_set_name (&tl_hba->dev,"tcm_loop_adapter_%d" , tcm_loop_host_id); + + ret =device_register (&tl_hba->dev); +if (ret) { +printk (KERN_ERR "device_register() failed for" +" tl_hba->dev: %d\n" , ret); +return -ENODEV ; + } + +return 0 ; +} + +/* +* Called from tcm_loop_fabric_init() in tcl_loop_fabric.c to load the emulated +* tcm_loop SCSI bus. +*/ +int tcm_loop_alloc_core_bus (void )
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c line 628
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +10
-0+{ +int ret; + + ret =device_register (&tcm_loop_primary); +if (ret) { +printk (KERN_ERR "device_register() failed for" +" tcm_loop_primary\n" ); +return ret; + } +
Whistepace.
Quoted 37 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.c new file +31
-0+ ret =bus_register (&tcm_loop_lld_bus); +if (ret) { +printk (KERN_ERR "bus_register() failed for tcm_loop_lld_bus\n" ); + goto dev_unreg; + } + + ret =driver_register (&tcm_loop_driverfs); +if (ret) { +printk (KERN_ERR "driver_register() failed for" +"tcm_loop_driverfs\n" ); + goto bus_unreg; + } + +printk (KERN_INFO "Initialized TCM Loop Core Bus\n" ); +return ret; + +bus_unreg: +bus_unregister (&tcm_loop_lld_bus); +dev_unreg: +device_unregister (&tcm_loop_primary); +return ret; +} + +void tcm_loop_release_core_bus (void ) +{ +driver_unregister (&tcm_loop_driverfs); +bus_unregister (&tcm_loop_lld_bus); +device_unregister (&tcm_loop_primary); + +printk (KERN_INFO "Releasing TCM Loop Core BUS\n" ); +}drivers/target/tcm_loop/tcm_loop_fabric_scsi.h new file +0
-0
drivers/target/tcm_loop/tcm_loop_fabric_scsi.h line 10
Quoted 10 lines
drivers/target/tcm_loop/tcm_loop_fabric_scsi.h new file +10
-0@@ -0,0 +1,10 @@ +extern struct kmem_cache *tcm_loop_cmd_cache; + +extern int tcm_loop_execute_core_cmd (struct tcm_loop_cmd *,struct scsi_cmnd *); +extern void tcm_loop_check_stop_free (struct se_cmd *); +extern void tcm_loop_deallocate_core_cmd (struct se_cmd *); +extern void tcm_loop_scsi_forget_host (struct Scsi_Host *); +extern void tcm_loop_deallocate_core_cmd (struct se_cmd *); +extern int tcm_loop_setup_hba_bus (struct tcm_loop_hba *,int ); +extern int tcm_loop_alloc_core_bus (void ); +extern void tcm_loop_release_core_bus (void );
Thanks.
Signature · 1 line
Dmitry