Great threads handling and performance improvements:
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 25 Mar 2010 19:43:41 +0000 (19:43 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Thu, 25 Mar 2010 19:43:41 +0000 (19:43 +0000)
 - io_context not needed anymore

 - Now threads pool can be per session

 - Each device now can have own type of threads pool with own threads count

+ docs updates and some cleanups and minor fixes.

git-svn-id: https://scst.svn.sourceforge.net/svnroot/scst/trunk@1563 d57e44dd-8a1f-0410-8b47-8ef2f437770f

25 files changed:
iscsi-scst/README
iscsi-scst/README_in-tree
iscsi-scst/usr/iscsi_scstd.c
iscsi-scst/usr/iscsid.c
qla2x00t/qla2x00-target/README
scst/README
scst/README_in-tree
scst/include/scst.h
scst/include/scst_const.h
scst/kernel/io_context-2.6.26.patch [deleted file]
scst/kernel/io_context-2.6.27.patch [deleted file]
scst/kernel/io_context-2.6.28.patch [deleted file]
scst/kernel/io_context-2.6.29.patch [deleted file]
scst/kernel/io_context-2.6.30.patch [deleted file]
scst/kernel/io_context-2.6.31.patch [deleted file]
scst/kernel/io_context-2.6.32.patch [deleted file]
scst/kernel/io_context-2.6.33.patch [deleted file]
scst/src/dev_handlers/scst_user.c
scst/src/dev_handlers/scst_vdisk.c
scst/src/scst_lib.c
scst/src/scst_main.c
scst/src/scst_priv.h
scst/src/scst_proc.c
scst/src/scst_sysfs.c
scst/src/scst_targ.c

index 6fc2b3b..6c0ebd5 100644 (file)
@@ -352,6 +352,8 @@ both iSCSI-SCST targets will look like:
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   `-- usn
 |   |-- cdrom
@@ -361,6 +363,8 @@ both iSCSI-SCST targets will look like:
 |   |   |-- handler -> ../../handlers/vcdrom
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   `-- usn
 |   |-- disk1
@@ -376,6 +380,8 @@ both iSCSI-SCST targets will look like:
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   |-- usn
 |   |   `-- write_through
@@ -392,6 +398,8 @@ both iSCSI-SCST targets will look like:
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   |-- usn
 |   |   `-- write_through
@@ -404,6 +412,8 @@ both iSCSI-SCST targets will look like:
 |       |-- removable
 |       |-- size_mb
 |       |-- t10_dev_id
+|       |-- threads_num
+|       |-- threads_pool_type
 |       |-- type
 |       `-- usn
 |-- handlers
@@ -608,6 +618,17 @@ as well.
 3. ISCSI initiators from pre-CentOS/RHEL 5 reported to have some
 performance problems. If you use it, it is strongly advised to upgrade.
 
+4. Pay attention to have mpio_type option set correctly. See SCST core's
+README for more details.
+
+5. If you are going to use your target in an VM environment, for
+instance as a shared storage with VMware, make sure all your VMs
+connected to the target via *separate* sessions, i.e. each VM has own
+connection to the target, not all VMs connected using a single
+connection. You can check it using SCST proc or sysfs interface. If you
+miss it, you can greatly loose performance of parallel access to your
+target from different VMs.
+
 
 Compilation options
 -------------------
index 8888013..4aec409 100644 (file)
@@ -263,6 +263,8 @@ both iSCSI-SCST targets will look like:
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   `-- usn
 |   |-- cdrom
@@ -272,6 +274,8 @@ both iSCSI-SCST targets will look like:
 |   |   |-- handler -> ../../handlers/vcdrom
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   `-- usn
 |   |-- disk1
@@ -303,6 +307,10 @@ both iSCSI-SCST targets will look like:
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   |-- usn
 |   |   `-- write_through
@@ -315,6 +323,8 @@ both iSCSI-SCST targets will look like:
 |       |-- removable
 |       |-- size_mb
 |       |-- t10_dev_id
+|       |-- threads_num
+|       |-- threads_pool_type
 |       |-- type
 |       `-- usn
 |-- handlers
@@ -517,6 +527,17 @@ as well.
 3. ISCSI initiators built in pre-CentOS/RHEL 5 reported to have some
 performance problems. If you use it, it is strongly advised to upgrade.
 
+4. Pay attention to have mpio_type option set correctly. See SCST core's
+README for more details.
+
+5. If you are going to use your target in an VM environment, for
+instance as a shared storage with VMware, make sure all your VMs
+connected to the target via *separate* sessions, i.e. each VM has own
+connection to the target, not all VMs connected using a single
+connection. You can check it using SCST proc or sysfs interface. If you
+miss it, you can greatly loose performance of parallel access to your
+target from different VMs.
+
 
 Compilation options
 -------------------
index e1ab45d..10dd5f0 100644 (file)
@@ -583,11 +583,11 @@ static void init_max_params(void)
        }
 
        /* QueuedCommands */
-       target_keys[key_queued_cmnds].local_def = min(target_keys[key_queued_cmnds].local_def,
+       target_keys[key_queued_cmnds].local_def = min((int)target_keys[key_queued_cmnds].local_def,
                                                      iscsi_init_params.max_queued_cmds);
-       target_keys[key_queued_cmnds].max = min(target_keys[key_queued_cmnds].max,
+       target_keys[key_queued_cmnds].max = min((int)target_keys[key_queued_cmnds].max,
                                                iscsi_init_params.max_queued_cmds);
-       target_keys[key_queued_cmnds].min = min(target_keys[key_queued_cmnds].min,
+       target_keys[key_queued_cmnds].min = min((int)target_keys[key_queued_cmnds].min,
                                                iscsi_init_params.max_queued_cmds);
 
        /* MaxRecvDataSegmentLength */
index cf9edcb..ab254db 100644 (file)
@@ -730,6 +730,8 @@ static void cmnd_exec_login(struct connection *conn)
                        conn->state = STATE_LOGIN;
 
                        login_start(conn);
+                       if (rsp->status_class)
+                               return;
                        if (!accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING))
                                goto auth_err;
                        if (rsp->status_class)
index c92cbad..575c5f0 100644 (file)
@@ -282,6 +282,8 @@ The resulting overall SCST sysfs hierarchy with initiator
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   `-- usn
 |   |-- cdrom
@@ -291,6 +293,8 @@ The resulting overall SCST sysfs hierarchy with initiator
 |   |   |-- handler -> ../../handlers/vcdrom
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   `-- usn
 |   |-- disk1
@@ -306,6 +310,8 @@ The resulting overall SCST sysfs hierarchy with initiator
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   |-- usn
 |   |   `-- write_through
@@ -322,6 +328,8 @@ The resulting overall SCST sysfs hierarchy with initiator
 |   |   |-- resync_size
 |   |   |-- size_mb
 |   |   |-- t10_dev_id
+|   |   |-- threads_num
+|   |   |-- threads_pool_type
 |   |   |-- type
 |   |   |-- usn
 |   |   `-- write_through
@@ -334,6 +342,8 @@ The resulting overall SCST sysfs hierarchy with initiator
 |       |-- removable
 |       |-- size_mb
 |       |-- t10_dev_id
+|       |-- threads_num
+|       |-- threads_pool_type
 |       |-- type
 |       `-- usn
 |-- handlers
@@ -411,6 +421,21 @@ The resulting overall SCST sysfs hierarchy with initiator
 `-- version
 
 
+Performance advices
+-------------------
+
+1. Pay attention to have mpio_type option set correctly. See SCST core's
+README for more details.
+
+2. If you are going to use your target in an VM environment, for
+instance as a shared storage with VMware, make sure all your VMs
+connected to the target via *separate* sessions. You can check it using
+SCST proc or sysfs interface. You should use available facilities, like
+NPIV, to make separate sessions for each VM. If you miss it, you can
+greatly loose performance of parallel access to your target from
+different VMs.
+
+
 Credits
 -------
 
index 1e74ddd..92f4b96 100644 (file)
@@ -701,6 +701,24 @@ Every target should have at least the following entries:
    initiators security groups, so you can assign the addressing method
    on per-initiator basis.
 
+ - mpio_type - defines how I/O from sessions to this target are grouped
+   together. This I/O grouping is very important for performance. If
+   you have this attribute set in a wrong value, you can considerably
+   decrease performance of your setup. Possible values: "enable",
+   "disable" and "auto". When "enable" all I/O from all sessions will be
+   grouped together. When "disable", I/O from different sessions will
+   not be grouped together, i.e. all sessions will have separate
+   dedicated group. When "auto" (default), all I/O from initiators with
+   the same name (iSCSI initiator name, for instance) will be grouped
+   together with a separate dedicated group for each initiator name. For
+   iSCSI it works well, but other transports usually use different
+   names, so using them in MPIO configurations, you should instead use
+   value "enable". This attribute is also available in the initiators
+   security groups, so you can assign the addressing method on
+   per-initiator basis. Recommended practice is to create a separate
+   initiator security group for each MPIO sessions and set mpio_type in
+   "enable".
+
  - rel_tgt_id - allows to read or write SCSI Relative Target Port
    Identifier attribute. This identifier is used to identify SCSI Target
    Ports by some SCSI commands, mainly by Persistent Reservations
@@ -710,7 +728,7 @@ Every target should have at least the following entries:
    until rel_tgt_id becomes unique. This attribute initialized unique by
    SCST by default.
 
-A target driver may have also the follwoing entries:
+A target driver may have also the following entries:
 
  - "hw_target" - if the target driver supports both hardware and virtual
     targets (for instance, an FC adapter supporting NPIV, which has
@@ -1087,6 +1105,18 @@ cache. The following parameters possible for vdisk_fileio:
  - removable - with this flag set the device is reported to remote
    initiators as removable.
 
+ - threads_num - sets number of threads in this device's threads
+   pool. If 0 - no threads will be created, and global SCST threads pool
+   will be used. If <0 - creation of the threads pool will be
+   prohibited.
+
+ - threads_pool_type - sets threads pool type. Possible values:
+   "per_initiator" and "shared". When "per_initiator" (default), each
+   session from each initiator will use separate dedicated pool of
+   threads. When "shared", all sessions from all initiators will share
+   the same per-device pool of threads. Valid only if threads_num
+   attribute >0.
+
 Handler vdisk_blockio provides BLOCKIO mode to create virtual devices.
 This mode performs direct block I/O with a block device, bypassing the
 page cache for all operations. This mode works ideally with high-end
@@ -1170,26 +1200,31 @@ For example:
 |-- resync_size
 |-- size_mb
 |-- t10_dev_id
+|-- threads_num
+|-- threads_pool_type
 |-- type
 |-- usn
 `-- write_through
 
 Each vdisk_blockio's device has the following attributes in
 /sys/kernel/scst_tgt/devices/device_name: blocksize, filename,
-read_only, removable, resync_size, size_mb, t10_dev_id, type, usn. See
-description of vdisk_fileio's device for description of those parameters.
+read_only, removable, resync_size, size_mb, t10_dev_id, threads_num,
+threads_pool_type, type, usn. See description of vdisk_fileio's device
+for description of those parameters.
 
 Each vdisk_nullio's device has the following attributes in
 /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only,
-removable, size_mb, t10_dev_id, type, usn. See description
-of vdisk_fileio's device for description of those parameters.
+removable, size_mb, t10_dev_id, threads_num, threads_pool_type, type,
+usn. See description of vdisk_fileio's device for description of those
+parameters.
 
 Each vcdrom's device has the following attributes in
 /sys/kernel/scst_tgt/devices/device_name: filename,  size_mb,
-t10_dev_id, type, usn. See description of vdisk_fileio's device for
-description of those parameters. Exception is filename attribute. For
-vcdrom it is writable. Writing to it allows to virtually insert or
-change virtual CD media in the virtual CDROM device. For example:
+t10_dev_id, threads_num, threads_pool_type, type, usn. See description
+of vdisk_fileio's device for description of those parameters. Exception
+is filename attribute. For vcdrom it is writable. Writing to it allows
+to virtually insert or change virtual CD media in the virtual CDROM
+device. For example:
 
  - echo "/image.iso" >/sys/kernel/scst_tgt/devices/cdrom/filename - will
    insert file /image.iso as virtual media to the virtual CDROM cdrom.
@@ -1406,13 +1441,12 @@ fileio_tgt's README file for more details.
 Performance
 -----------
 
-Before doing any performance measurements note that:
+Before doing any performance measurements note that performance results
+are very much dependent from your type of load, so it is crucial that
+you choose access mode (FILEIO, BLOCKIO, O_DIRECT, pass-through), which
+suits your needs the best.
 
-I. Performance results are very much dependent from your type of load,
-so it is crucial that you choose access mode (FILEIO, BLOCKIO,
-O_DIRECT, pass-through), which suits your needs the best.
-
-II. In order to get the maximum performance you should:
+In order to get the maximum performance you should:
 
 1. For SCST:
 
@@ -1440,7 +1474,17 @@ You can set the above options, except
 CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ, in the needed values
 using debug2perf root Makefile target.
 
-4. For other target and initiator software parts:
+4. If you are going to use your target in an VM environment, for
+instance as a shared storage with VMware, make sure all your VMs
+connected to the target via *separate* sessions. For instance, for iSCSI
+it means that each VM has own connection to the target, not all VMs
+connected using a single connection. You can check it using SCST proc or
+sysfs interface. For other transports you should use available
+facilities, like NPIV for Fibre Channel, to make separate sessions for
+each VM. If you miss it, you can greatly loose performance of parallel
+access to your target from different VMs.
+
+5. For other target and initiator software parts:
 
  - Make sure you applied on your kernel all available SCST patches,
    especially io_context-2.6.X.patch. If for your kernel version this
@@ -1501,7 +1545,7 @@ using debug2perf root Makefile target.
    files, because it allows considerably better linear write throughput,
    than ext3.
 
-5. For hardware on target.
+6. For hardware on target.
 
  - Make sure that your target hardware (e.g. target FC or network card)
    and underlaying IO hardware (e.g. IO card, like SATA, SCSI or RAID to
@@ -1518,7 +1562,7 @@ using debug2perf root Makefile target.
    you have no choice, but PCI bus sharing, set in the BIOS PCI latency
    as low as possible.
 
-6. If you use VDISK IO module in FILEIO mode, NV_CACHE option will
+7. If you use VDISK IO module in FILEIO mode, NV_CACHE option will
 provide you the best performance. But using it make sure you use a good
 UPS with ability to shutdown the target on the power failure.
 
@@ -1542,7 +1586,7 @@ IMPORTANT: If you use on initiator some versions of Windows (at least W2K)
           for VDISK FILEIO devices above.
 
 
-In some cases, for instance working with SSD devices, which consume 100%
+8. In some cases, for instance working with SSD devices, which consume 100%
 of a single CPU load for data transfers in their internal threads, to
 maximize IOPS it can be needed to assign for those threads dedicated
 CPUs using Linux CPU affinity facilities. No IRQ processing should be
index 16365fd..25108fd 100644 (file)
@@ -624,6 +624,24 @@ Every target should have at least the following entries:
    initiators security groups, so you can assign the addressing method
    on per-initiator basis.
 
+ - mpio_type - defines how I/O from sessions to this target are grouped
+   together. This I/O grouping is very important for performance. If
+   you have this attribute set in a wrong value, you can considerably
+   decrease performance of your setup. Possible values: "enable",
+   "disable" and "auto". When "enable" all I/O from all sessions will be
+   grouped together. When "disable", I/O from different sessions will
+   not be grouped together, i.e. all sessions will have separate
+   dedicated group. When "auto" (default), all I/O from initiators with
+   the same name (iSCSI initiator name, for instance) will be grouped
+   together with a separate dedicated group for each initiator name. For
+   iSCSI it works well, but other transports usually use different
+   names, so using them in MPIO configurations, you should instead use
+   value "enable". This attribute is also available in the initiators
+   security groups, so you can assign the addressing method on
+   per-initiator basis. Recommended practice is to create a separate
+   initiator security group for each MPIO sessions and set mpio_type in
+   "enable".
+
  - rel_tgt_id - allows to read or write SCSI Relative Target Port
    Identifier attribute. This identifier is used to identify SCSI Target
    Ports by some SCSI commands, mainly by Persistent Reservations
@@ -633,7 +651,7 @@ Every target should have at least the following entries:
    until rel_tgt_id becomes unique. This attribute initialized unique by
    SCST by default.
 
-A target driver may have also the follwoing entries:
+A target driver may have also the following entries:
 
  - "hw_target" - if the target driver supports both hardware and virtual
     targets (for instance, an FC adapter supporting NPIV, which has
@@ -1011,6 +1029,18 @@ cache. The following parameters possible for vdisk_fileio:
  - removable - with this flag set the device is reported to remote
    initiators as removable.
 
+ - threads_num - sets number of threads in this device's threads
+   pool. If 0 - no threads will be created, and global SCST threads pool
+   will be used. If <0 - creation of the threads pool will be
+   prohibited.
+
+ - threads_pool_type - sets threads pool type. Possible values:
+   "per_initiator" and "shared". When "per_initiator" (default), each
+   session from each initiator will use separate dedicated pool of
+   threads. When "shared", all sessions from all initiators will share
+   the same per-device pool of threads. Valid only if threads_num
+   attribute >0.
+
 Handler vdisk_blockio provides BLOCKIO mode to create virtual devices.
 This mode performs direct block I/O with a block device, bypassing the
 page cache for all operations. This mode works ideally with high-end
@@ -1094,26 +1124,31 @@ For example:
 |-- resync_size
 |-- size_mb
 |-- t10_dev_id
+|-- threads_num
+|-- threads_pool_type
 |-- type
 |-- usn
 `-- write_through
 
 Each vdisk_blockio's device has the following attributes in
 /sys/kernel/scst_tgt/devices/device_name: blocksize, filename,
-read_only, removable, resync_size, size_mb, t10_dev_id, type, usn. See
-description of vdisk_fileio's device for description of those parameters.
+read_only, removable, resync_size, size_mb, t10_dev_id, threads_num,
+threads_pool_type, type, usn. See description of vdisk_fileio's device
+for description of those parameters.
 
 Each vdisk_nullio's device has the following attributes in
 /sys/kernel/scst_tgt/devices/device_name: blocksize, read_only,
-removable, size_mb, t10_dev_id, type, usn. See description
-of vdisk_fileio's device for description of those parameters.
+removable, size_mb, t10_dev_id, threads_num, threads_pool_type, type,
+usn. See description of vdisk_fileio's device for description of those
+parameters.
 
 Each vcdrom's device has the following attributes in
 /sys/kernel/scst_tgt/devices/device_name: filename,  size_mb,
-t10_dev_id, type, usn. See description of vdisk_fileio's device for
-description of those parameters. Exception is filename attribute. For
-vcdrom it is writable. Writing to it allows to virtually insert or
-change virtual CD media in the virtual CDROM device. For example:
+t10_dev_id, threads_num, threads_pool_type, type, usn. See description
+of vdisk_fileio's device for description of those parameters. Exception
+is filename attribute. For vcdrom it is writable. Writing to it allows
+to virtually insert or change virtual CD media in the virtual CDROM
+device. For example:
 
  - echo "/image.iso" >/sys/kernel/scst_tgt/devices/cdrom/filename - will
    insert file /image.iso as virtual media to the virtual CDROM cdrom.
@@ -1330,13 +1365,12 @@ fileio_tgt's README file for more details.
 Performance
 -----------
 
-Before doing any performance measurements note that:
+Before doing any performance measurements note that performance results
+are very much dependent from your type of load, so it is crucial that
+you choose access mode (FILEIO, BLOCKIO, O_DIRECT, pass-through), which
+suits your needs the best.
 
-I. Performance results are very much dependent from your type of load,
-so it is crucial that you choose access mode (FILEIO, BLOCKIO,
-O_DIRECT, pass-through), which suits your needs the best.
-
-II. In order to get the maximum performance you should:
+In order to get the maximum performance you should:
 
 1. For SCST:
 
@@ -1364,7 +1398,17 @@ You can set the above options, except
 CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ, in the needed values
 using debug2perf root Makefile target.
 
-4. For other target and initiator software parts:
+4. If you are going to use your target in an VM environment, for
+instance as a shared storage with VMware, make sure all your VMs
+connected to the target via *separate* sessions. For instance, for iSCSI
+it means that each VM has own connection to the target, not all VMs
+connected using a single connection. You can check it using SCST proc or
+sysfs interface. For other transports you should use available
+facilities, like NPIV for Fibre Channel, to make separate sessions for
+each VM. If you miss it, you can greatly loose performance of parallel
+access to your target from different VMs.
+
+5. For other target and initiator software parts:
 
  - Make sure you applied on your kernel all available SCST patches,
    especially io_context-2.6.X.patch. If for your kernel version this
@@ -1425,7 +1469,7 @@ using debug2perf root Makefile target.
    files, because it allows considerably better linear write throughput,
    than ext3.
 
-5. For hardware on target.
+6. For hardware on target.
 
  - Make sure that your target hardware (e.g. target FC or network card)
    and underlaying IO hardware (e.g. IO card, like SATA, SCSI or RAID to
@@ -1442,7 +1486,7 @@ using debug2perf root Makefile target.
    you have no choice, but PCI bus sharing, set in the BIOS PCI latency
    as low as possible.
 
-6. If you use VDISK IO module in FILEIO mode, NV_CACHE option will
+7. If you use VDISK IO module in FILEIO mode, NV_CACHE option will
 provide you the best performance. But using it make sure you use a good
 UPS with ability to shutdown the target on the power failure.
 
@@ -1465,7 +1509,7 @@ IMPORTANT: If you use on initiator some versions of Windows (at least W2K)
           See also important notes about setting block sizes >512 bytes
           for VDISK FILEIO devices above.
 
-In some cases, for instance working with SSD devices, which consume 100%
+8. In some cases, for instance working with SSD devices, which consume 100%
 of a single CPU load for data transfers in their internal threads, to
 maximize IOPS it can be needed to assign for those threads dedicated
 CPUs using Linux CPU affinity facilities. No IRQ processing should be
index 5588de7..9b4607e 100644 (file)
@@ -935,6 +935,21 @@ struct scst_tgt_template {
 #endif
 };
 
+/* 
+ * Threads pool types. Changing them don't forget to change
+ * the corresponding *_STR values in scst_const.h!
+ */
+enum scst_dev_type_threads_pool_type {
+       /* Each initiator will have dedicated threads pool. */
+       SCST_THREADS_POOL_PER_INITIATOR = 0,
+
+       /* All connected initiators will use shared threads pool */
+       SCST_THREADS_POOL_SHARED,
+
+       /* Invalid value for scst_parse_threads_pool_type() */
+       SCST_THREADS_POOL_TYPE_INVALID,
+};
+
 struct scst_dev_type {
        /* SCSI type of the supported device. MUST HAVE */
        int type;
@@ -1122,11 +1137,15 @@ struct scst_dev_type {
        char name[SCST_MAX_NAME + 10];
 
        /*
-        * Number of dedicated threads. If 0 - no dedicated threads will
-        * be created, if <0 - creation of dedicated threads is prohibited.
+        * Number of threads in this handler's devices' threads pools.
+        * If 0 - no threads will be created, if <0 - creation of the threads
+        * pools is prohibited. Also pay attention to threads_pool_type below.
         */
        int threads_num;
 
+       /* Threads pool type. Valid only if threads_num > 0. */
+       enum scst_dev_type_threads_pool_type threads_pool_type;
+
        /* Optional default log flags */
        const unsigned long default_trace_flags;
 
@@ -1403,19 +1422,25 @@ struct scst_session {
 #endif
 };
 
-struct scst_cmd_lists {
+struct scst_cmd_threads {
        spinlock_t cmd_list_lock;
        struct list_head active_cmd_list;
        wait_queue_head_t cmd_list_waitQ;
+
+       struct io_context *io_context;
+
+       int nr_threads;
+       struct list_head threads_list;
+
        struct list_head lists_list_entry;
 };
 
 struct scst_cmd {
-       /* List entry for below *_cmd_lists */
+       /* List entry for below *_cmd_threads */
        struct list_head cmd_list_entry;
 
        /* Pointer to lists of commands with the lock */
-       struct scst_cmd_lists *cmd_lists;
+       struct scst_cmd_threads *cmd_threads;
 
        atomic_t cmd_ref;
 
@@ -1836,14 +1861,8 @@ struct scst_device {
        /* Corresponding real SCSI device, could be NULL for virtual devices */
        struct scsi_device *scsi_dev;
 
-       /* Pointer to lists of commands with the lock */
-       struct scst_cmd_lists *p_cmd_lists;
-
        /* Lists of commands with lock, if dedicated threads are used */
-       struct scst_cmd_lists cmd_lists;
-
-       /* Per-device dedicated IO context */
-       struct io_context *dev_io_ctx;
+       struct scst_cmd_threads dev_cmd_threads;
 
        /* How many cmds alive on this dev */
        atomic_t dev_cmd_count;
@@ -1891,11 +1910,11 @@ struct scst_device {
        /* List of acg_dev's, one per acg, protected by scst_mutex */
        struct list_head dev_acg_dev_list;
 
-       /* List of dedicated threads, protected by scst_mutex */
-       struct list_head threads_list;
+       /* Number of threads in the device's threads pools */
+       int threads_num;
 
-       /* Device number */
-       int dev_num;
+       /* Threads pool type of the device. Valid only if threads_num > 0. */
+       enum scst_dev_type_threads_pool_type threads_pool_type;
 
        /* Set if tgt_kobj was initialized */
        unsigned int dev_kobj_initialized:1;
@@ -1977,8 +1996,11 @@ struct scst_tgt_dev {
        spinlock_t thr_data_lock;
        struct list_head thr_data_list;
 
-       /* Per-(device, session) dedicated IO context */
-       struct io_context *tgt_dev_io_ctx;
+       /* Pointer to lists of commands with the lock */
+       struct scst_cmd_threads *active_cmd_threads;
+
+       /* Lists of commands with lock, if dedicated threads are used */
+       struct scst_cmd_threads tgt_dev_cmd_threads;
 
        spinlock_t tgt_dev_lock;        /* per-session device lock */
 
@@ -2043,6 +2065,25 @@ struct scst_acg_dev {
        struct kobject acg_dev_kobj;
 };
 
+/* 
+ * ACG MPIO types. Changing them don't forget to change
+ * the corresponding *_STR values in scst_const.h!
+ */
+enum scst_acg_mpio {
+       /*
+        * All initiators with the same name connected to this group will have
+        * shared IO context, for each name own context. All initiators with
+        * different names will have own IO context.
+        */
+       SCST_ACG_MPIO_AUTO = 0,
+
+       /* All initiators connected to this group will have shared IO context */
+       SCST_ACG_MPIO_ENABLE,
+
+       /* Each initiator connected to this group will have own IO context */
+       SCST_ACG_MPIO_DISABLE,
+};
+
 /*
  * ACG - access control group. Used to store group related
  * control information.
@@ -2068,6 +2109,9 @@ struct scst_acg {
        struct proc_dir_entry *acg_proc_root;
 #endif
 
+       /* Type of MPIO initiators groupping */
+       enum scst_acg_mpio acg_mpio_type;
+
        unsigned int acg_kobj_initialized:1;
        unsigned int in_tgt_acg_list:1;
 
@@ -3479,13 +3523,6 @@ static inline const char *scst_get_tgt_name(const struct scst_tgt *tgt)
        return tgt->tgt_name;
 }
 
-/*
- * Adds and deletes (stops) num of global SCST's threads. Returns 0 on
- * success, error code otherwise.
- */
-int scst_add_global_threads(int num);
-void scst_del_global_threads(int num);
-
 int scst_alloc_sense(struct scst_cmd *cmd, int atomic);
 int scst_alloc_set_sense(struct scst_cmd *cmd, int atomic,
        const uint8_t *sense, unsigned int len);
@@ -3748,4 +3785,17 @@ void scst_restore_token_str(char *prev_lexem, char *token_str);
  */
 char *scst_get_next_token_str(char **input_str);
 
+/*
+ * Converts string presentation of threads pool type to enum.
+ * Returns SCST_THREADS_POOL_TYPE_INVALID if the string is invalid.
+ */
+enum scst_dev_type_threads_pool_type scst_parse_threads_pool_type(
+       const char *p, int len);
+
+/* Initializes scst_cmd_threads structure */
+void scst_init_threads(struct scst_cmd_threads *cmd_threads);
+
+/* Deinitializes scst_cmd_threads structure */
+void scst_deinit_threads(struct scst_cmd_threads *cmd_threads);
+
 #endif /* __SCST_H */
index 3f79e79..7c748e1 100644 (file)
@@ -348,12 +348,26 @@ enum scst_cdb_flags {
 
 #define SCST_MAX_OTHER_TIMEOUT                 (14000 * HZ)
 
+/*************************************************************
+ ** ACG MPIO attribute values. Must match scst_acg_mpio!
+ *************************************************************/
+#define SCST_ACG_MPIO_AUTO_STR                 "auto"
+#define SCST_ACG_MPIO_ENABLE_STR               "enable"
+#define SCST_ACG_MPIO_DISABLE_STR              "disable"
+
+/*************************************************************
+ ** Threads pool type attribute values.
+ ** Must match scst_dev_type_threads_pool_type!
+ *************************************************************/
+#define SCST_THREADS_POOL_PER_INITIATOR_STR    "per_initiator"
+#define SCST_THREADS_POOL_SHARED_STR           "shared"
+
 /*************************************************************
  ** Misc constants
  *************************************************************/
-#define SCST_SYSFS_BLOCK_SIZE  PAGE_SIZE
+#define SCST_SYSFS_BLOCK_SIZE                  PAGE_SIZE
 
-#define SCST_SYSFS_KEY_MARK    "[key]"
+#define SCST_SYSFS_KEY_MARK                    "[key]"
 
 #define SCST_MIN_REL_TGT_ID                    1
 #define SCST_MAX_REL_TGT_ID                    65535
diff --git a/scst/kernel/io_context-2.6.26.patch b/scst/kernel/io_context-2.6.26.patch
deleted file mode 100644 (file)
index 045780c..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-diff -rup linux-source-2.6.26.orig/block/blk-ioc.c linux-source-2.6.26/block/blk-ioc.c
---- linux-source-2.6.26.orig/block/blk-ioc.c   2008-07-13 17:51:29.000000000 -0400
-+++ linux-source-2.6.26/block/blk-ioc.c        2009-08-26 13:31:49.000000000 -0400
-@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              if (ioc->aic && ioc->aic->exit)
-+                      ioc->aic->exit(ioc->aic);
-+              cfq_exit(ioc);
-+
-+              put_io_context(ioc);
-+      }
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(void)
- {
-@@ -75,13 +90,7 @@ void exit_io_context(void)
-       current->io_context = NULL;
-       task_unlock(current);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              if (ioc->aic && ioc->aic->exit)
--                      ioc->aic->exit(ioc->aic);
--              cfq_exit(ioc);
--
--              put_io_context(ioc);
--      }
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -105,7 +114,8 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
--
-+EXPORT_SYMBOL(alloc_io_context);
-+
- /*
-  * If the current task has no IO context then create one and initialise it.
-  * Otherwise, return its existing IO context.
-diff -rup linux-source-2.6.26.orig/include/linux/iocontext.h linux-source-2.6.26/include/linux/iocontext.h
---- linux-source-2.6.26.orig/include/linux/iocontext.h 2008-07-13 17:51:29.000000000 -0400
-+++ linux-source-2.6.26/include/linux/iocontext.h      2009-08-26 13:34:11.000000000 -0400
-@@ -4,6 +4,8 @@
- #include <linux/radix-tree.h>
- #include <linux/rcupdate.h>
-+#define SCST_IO_CONTEXT
-+
- /*
-  * This is the per-process anticipatory I/O scheduler state.
-  */
-@@ -99,4 +101,6 @@ static inline struct io_context *ioc_tas
-       return NULL;
- }
-+void __exit_io_context(struct io_context *ioc);
-+
- #endif
diff --git a/scst/kernel/io_context-2.6.27.patch b/scst/kernel/io_context-2.6.27.patch
deleted file mode 100644 (file)
index e20a8d0..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-diff -upkr linux-2.6.27.2/block/blk-ioc.c linux-2.6.27.2/block/blk-ioc.c
---- linux-2.6.27.2/block/blk-ioc.c     2008-10-10 02:13:53.000000000 +0400
-+++ linux-2.6.27.2/block/blk-ioc.c     2009-03-23 21:32:37.000000000 +0300
-@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              if (ioc->aic && ioc->aic->exit)
-+                      ioc->aic->exit(ioc->aic);
-+              cfq_exit(ioc);
-+
-+              put_io_context(ioc);
-+      }
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(void)
- {
-@@ -75,13 +90,7 @@ void exit_io_context(void)
-       current->io_context = NULL;
-       task_unlock(current);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              if (ioc->aic && ioc->aic->exit)
--                      ioc->aic->exit(ioc->aic);
--              cfq_exit(ioc);
--
--              put_io_context(ioc);
--      }
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -105,6 +114,7 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
-+EXPORT_SYMBOL(alloc_io_context);
- /*
-  * If the current task has no IO context then create one and initialise it.
-diff -upkr linux-2.6.27.2/include/linux/iocontext.h linux-2.6.27.2/include/linux/iocontext.h
---- linux-2.6.27.2/include/linux/iocontext.h   2008-10-10 02:13:53.000000000 +0400
-+++ linux-2.6.27.2/include/linux/iocontext.h   2009-03-23 21:32:37.000000000 +0300
-@@ -103,7 +103,9 @@ static inline struct io_context *ioc_tas
- int put_io_context(struct io_context *ioc);
- void exit_io_context(void);
- struct io_context *get_io_context(gfp_t gfp_flags, int node);
-+#define SCST_IO_CONTEXT
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-+void __exit_io_context(struct io_context *ioc);
- void copy_io_context(struct io_context **pdst, struct io_context **psrc);
- #else
- static inline void exit_io_context(void)
diff --git a/scst/kernel/io_context-2.6.28.patch b/scst/kernel/io_context-2.6.28.patch
deleted file mode 100644 (file)
index 28558cd..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-diff -upkr linux-2.6.28/block/blk-ioc.c linux-2.6.28/block/blk-ioc.c
---- linux-2.6.28/block/blk-ioc.c       2008-12-25 02:26:37.000000000 +0300
-+++ linux-2.6.28/block/blk-ioc.c       2009-03-23 14:28:48.000000000 +0300
-@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              if (ioc->aic && ioc->aic->exit)
-+                      ioc->aic->exit(ioc->aic);
-+              cfq_exit(ioc);
-+
-+              put_io_context(ioc);
-+      }
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(void)
- {
-@@ -75,13 +90,7 @@ void exit_io_context(void)
-       current->io_context = NULL;
-       task_unlock(current);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              if (ioc->aic && ioc->aic->exit)
--                      ioc->aic->exit(ioc->aic);
--              cfq_exit(ioc);
--
--              put_io_context(ioc);
--      }
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -105,6 +114,7 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
-+EXPORT_SYMBOL(alloc_io_context);
- /*
-  * If the current task has no IO context then create one and initialise it.
-diff -upkr linux-2.6.28/include/linux/iocontext.h linux-2.6.28/include/linux/iocontext.h
---- linux-2.6.28/include/linux/iocontext.h     2008-12-25 02:26:37.000000000 +0300
-+++ linux-2.6.28/include/linux/iocontext.h     2009-03-23 14:05:01.000000000 +0300
-@@ -103,7 +103,9 @@ static inline struct io_context *ioc_tas
- int put_io_context(struct io_context *ioc);
- void exit_io_context(void);
- struct io_context *get_io_context(gfp_t gfp_flags, int node);
-+#define SCST_IO_CONTEXT
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-+void __exit_io_context(struct io_context *ioc);
- void copy_io_context(struct io_context **pdst, struct io_context **psrc);
- #else
- static inline void exit_io_context(void)
diff --git a/scst/kernel/io_context-2.6.29.patch b/scst/kernel/io_context-2.6.29.patch
deleted file mode 100644 (file)
index 830b7fb..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-diff -upkr linux-2.6.29/block/blk-ioc.c linux-2.6.29/block/blk-ioc.c
---- linux-2.6.29/block/blk-ioc.c       2008-12-25 02:26:37.000000000 +0300
-+++ linux-2.6.29/block/blk-ioc.c       2009-03-23 14:28:48.000000000 +0300
-@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              if (ioc->aic && ioc->aic->exit)
-+                      ioc->aic->exit(ioc->aic);
-+              cfq_exit(ioc);
-+
-+              put_io_context(ioc);
-+      }
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(void)
- {
-@@ -75,13 +90,7 @@ void exit_io_context(void)
-       current->io_context = NULL;
-       task_unlock(current);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              if (ioc->aic && ioc->aic->exit)
--                      ioc->aic->exit(ioc->aic);
--              cfq_exit(ioc);
--
--              put_io_context(ioc);
--      }
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -105,6 +114,7 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
-+EXPORT_SYMBOL(alloc_io_context);
- /*
-  * If the current task has no IO context then create one and initialise it.
-diff -upkr linux-2.6.29/include/linux/iocontext.h linux-2.6.29/include/linux/iocontext.h
---- linux-2.6.29/include/linux/iocontext.h     2008-12-25 02:26:37.000000000 +0300
-+++ linux-2.6.29/include/linux/iocontext.h     2009-03-23 14:05:01.000000000 +0300
-@@ -103,7 +103,9 @@ static inline struct io_context *ioc_tas
- int put_io_context(struct io_context *ioc);
- void exit_io_context(void);
- struct io_context *get_io_context(gfp_t gfp_flags, int node);
-+#define SCST_IO_CONTEXT
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-+void __exit_io_context(struct io_context *ioc);
- void copy_io_context(struct io_context **pdst, struct io_context **psrc);
- #else
- static inline void exit_io_context(void)
diff --git a/scst/kernel/io_context-2.6.30.patch b/scst/kernel/io_context-2.6.30.patch
deleted file mode 100644 (file)
index 7946ea3..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-diff -upkr linux-2.6.30.1/block/blk-ioc.c linux-2.6.30.1/block/blk-ioc.c
---- linux-2.6.30.1/block/blk-ioc.c     2009-06-10 07:05:27.000000000 +0400
-+++ linux-2.6.30.1/block/blk-ioc.c     2009-07-01 15:55:08.000000000 +0400
-@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              if (ioc->aic && ioc->aic->exit)
-+                      ioc->aic->exit(ioc->aic);
-+              cfq_exit(ioc);
-+
-+              put_io_context(ioc);
-+      }
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(void)
- {
-@@ -75,13 +90,7 @@ void exit_io_context(void)
-       current->io_context = NULL;
-       task_unlock(current);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              if (ioc->aic && ioc->aic->exit)
--                      ioc->aic->exit(ioc->aic);
--              cfq_exit(ioc);
--
--              put_io_context(ioc);
--      }
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -105,6 +114,7 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
-+EXPORT_SYMBOL(alloc_io_context);
- /*
-  * If the current task has no IO context then create one and initialise it.
-diff -upkr linux-2.6.30.1/include/linux/iocontext.h linux-2.6.30.1/include/linux/iocontext.h
---- linux-2.6.30.1/include/linux/iocontext.h   2009-06-10 07:05:27.000000000 +0400
-+++ linux-2.6.30.1/include/linux/iocontext.h   2009-07-01 15:20:24.000000000 +0400
-@@ -103,7 +103,9 @@ static inline struct io_context *ioc_tas
- int put_io_context(struct io_context *ioc);
- void exit_io_context(void);
- struct io_context *get_io_context(gfp_t gfp_flags, int node);
-+#define SCST_IO_CONTEXT
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-+void __exit_io_context(struct io_context *ioc);
- void copy_io_context(struct io_context **pdst, struct io_context **psrc);
- #else
- static inline void exit_io_context(void)
diff --git a/scst/kernel/io_context-2.6.31.patch b/scst/kernel/io_context-2.6.31.patch
deleted file mode 100644 (file)
index dc703c3..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-diff -upkr linux-2.6.31/block/blk-ioc.c linux-2.6.31/block/blk-ioc.c
---- linux-2.6.31/block/blk-ioc.c       2009-09-10 02:13:59.000000000 +0400
-+++ linux-2.6.31/block/blk-ioc.c       2009-09-23 14:17:17.000000000 +0400
-@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              if (ioc->aic && ioc->aic->exit)
-+                      ioc->aic->exit(ioc->aic);
-+              cfq_exit(ioc);
-+
-+              put_io_context(ioc);
-+      }
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(void)
- {
-@@ -75,13 +90,7 @@ void exit_io_context(void)
-       current->io_context = NULL;
-       task_unlock(current);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              if (ioc->aic && ioc->aic->exit)
--                      ioc->aic->exit(ioc->aic);
--              cfq_exit(ioc);
--
--              put_io_context(ioc);
--      }
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -105,6 +114,7 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
-+EXPORT_SYMBOL(alloc_io_context);
- /*
-  * If the current task has no IO context then create one and initialise it.
-diff -upkr linux-2.6.31/include/linux/iocontext.h linux-2.6.31/include/linux/iocontext.h
---- linux-2.6.31/include/linux/iocontext.h     2009-09-10 02:13:59.000000000 +0400
-+++ linux-2.6.31/include/linux/iocontext.h     2009-09-23 14:17:17.000000000 +0400
-@@ -103,7 +103,9 @@ static inline struct io_context *ioc_tas
- int put_io_context(struct io_context *ioc);
- void exit_io_context(void);
- struct io_context *get_io_context(gfp_t gfp_flags, int node);
-+#define SCST_IO_CONTEXT
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-+void __exit_io_context(struct io_context *ioc);
- void copy_io_context(struct io_context **pdst, struct io_context **psrc);
- #else
- static inline void exit_io_context(void)
diff --git a/scst/kernel/io_context-2.6.32.patch b/scst/kernel/io_context-2.6.32.patch
deleted file mode 100644 (file)
index bd855dc..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-diff -upkr linux-2.6.32.1/block/blk-ioc.c linux-2.6.32.1/block/blk-ioc.c
---- linux-2.6.32.1/block/blk-ioc.c     2009-12-03 06:51:21.000000000 +0300
-+++ linux-2.6.32.1/block/blk-ioc.c     2009-12-16 15:20:36.000000000 +0300
-@@ -65,6 +65,21 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              if (ioc->aic && ioc->aic->exit)
-+                      ioc->aic->exit(ioc->aic);
-+              cfq_exit(ioc);
-+
-+              put_io_context(ioc);
-+      }
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(void)
- {
-@@ -75,13 +90,7 @@ void exit_io_context(void)
-       current->io_context = NULL;
-       task_unlock(current);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              if (ioc->aic && ioc->aic->exit)
--                      ioc->aic->exit(ioc->aic);
--              cfq_exit(ioc);
--
--              put_io_context(ioc);
--      }
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -105,6 +114,7 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
-+EXPORT_SYMBOL(alloc_io_context);
- /*
-  * If the current task has no IO context then create one and initialise it.
-diff -upkr linux-2.6.32.1/include/linux/iocontext.h linux-2.6.32.1/include/linux/iocontext.h
---- linux-2.6.32.1/include/linux/iocontext.h   2009-12-03 06:51:21.000000000 +0300
-+++ linux-2.6.32.1/include/linux/iocontext.h   2009-12-16 15:20:36.000000000 +0300
-@@ -103,7 +103,9 @@ static inline struct io_context *ioc_tas
- int put_io_context(struct io_context *ioc);
- void exit_io_context(void);
- struct io_context *get_io_context(gfp_t gfp_flags, int node);
-+#define SCST_IO_CONTEXT
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-+void __exit_io_context(struct io_context *ioc);
- void copy_io_context(struct io_context **pdst, struct io_context **psrc);
- #else
- static inline void exit_io_context(void)
diff --git a/scst/kernel/io_context-2.6.33.patch b/scst/kernel/io_context-2.6.33.patch
deleted file mode 100644 (file)
index 52c4a5b..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-diff -upkr linux-2.6.33/block/blk-ioc.c linux-2.6.33/block/blk-ioc.c
---- linux-2.6.33/block/blk-ioc.c       2010-02-24 21:52:17.000000000 +0300
-+++ linux-2.6.33/block/blk-ioc.c       2010-03-01 15:39:49.000000000 +0300
-@@ -63,6 +63,19 @@ static void cfq_exit(struct io_context *
-       rcu_read_unlock();
- }
-+void __exit_io_context(struct io_context *ioc)
-+{
-+      if (ioc == NULL)
-+              return;
-+
-+      if (atomic_dec_and_test(&ioc->nr_tasks)) {
-+              cfq_exit(ioc);
-+
-+      }
-+      put_io_context(ioc);
-+}
-+EXPORT_SYMBOL(__exit_io_context);
-+
- /* Called by the exitting task */
- void exit_io_context(struct task_struct *task)
- {
-@@ -73,11 +86,7 @@ void exit_io_context(struct task_struct 
-       task->io_context = NULL;
-       task_unlock(task);
--      if (atomic_dec_and_test(&ioc->nr_tasks)) {
--              cfq_exit(ioc);
--
--      }
--      put_io_context(ioc);
-+      __exit_io_context(ioc);
- }
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node)
-@@ -100,6 +109,7 @@ struct io_context *alloc_io_context(gfp_
-       return ret;
- }
-+EXPORT_SYMBOL(alloc_io_context);
- /*
-  * If the current task has no IO context then create one and initialise it.
-diff -upkr linux-2.6.33/include/linux/iocontext.h linux-2.6.33/include/linux/iocontext.h
---- linux-2.6.33/include/linux/iocontext.h     2010-02-24 21:52:17.000000000 +0300
-+++ linux-2.6.33/include/linux/iocontext.h     2010-03-01 15:38:54.000000000 +0300
-@@ -76,7 +76,9 @@ struct task_struct;
- int put_io_context(struct io_context *ioc);
- void exit_io_context(struct task_struct *task);
- struct io_context *get_io_context(gfp_t gfp_flags, int node);
-+#define SCST_IO_CONTEXT
- struct io_context *alloc_io_context(gfp_t gfp_flags, int node);
-+void __exit_io_context(struct io_context *ioc);
- void copy_io_context(struct io_context **pdst, struct io_context **psrc);
- #else
- static inline void exit_io_context(struct task_struct *task)
index fd8f597..cf09d18 100644 (file)
 struct scst_user_dev {
        struct rw_semaphore dev_rwsem;
 
-       struct scst_cmd_lists cmd_lists;
+       /*
+        * Must be kept here, because it's needed on the cleanup time,
+        * when corresponding scst_dev is already dead.
+        */
+       struct scst_cmd_threads udev_cmd_threads;
 
-       /* Protected by cmd_lists.cmd_list_lock */
+       /* Protected by udev_cmd_threads.cmd_list_lock */
        struct list_head ready_cmd_list;
 
        /* Protected by dev_rwsem or don't need any protection */
@@ -74,7 +78,7 @@ struct scst_user_dev {
 
        struct scst_dev_type devtype;
 
-       /* Both protected by cmd_lists.cmd_list_lock */
+       /* Both protected by udev_cmd_threads.cmd_list_lock */
        unsigned int handle_counter;
        struct list_head ucmd_hash[1 << DEV_USER_CMD_HASH_ORDER];
 
@@ -111,7 +115,7 @@ struct scst_user_cmd {
 
        /*
         * Special flags, which can be accessed asynchronously (hence "long").
-        * Protected by cmd_lists.cmd_list_lock.
+        * Protected by udev_cmd_threads.cmd_list_lock.
         */
        unsigned long sent_to_user:1;
        unsigned long jammed:1;
@@ -245,7 +249,7 @@ static struct task_struct *cleanup_thread;
 
 /*
  * Skip this command if result is not 0. Must be called under
- * cmd_lists.cmd_list_lock and IRQ off.
+ * udev_cmd_threads.cmd_list_lock and IRQ off.
  */
 static inline bool ucmd_get_check(struct scst_user_cmd *ucmd)
 {
@@ -345,14 +349,14 @@ static void cmd_insert_hash(struct scst_user_cmd *ucmd)
        struct scst_user_cmd *u;
        unsigned long flags;
 
-       spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, flags);
+       spin_lock_irqsave(&dev->udev_cmd_threads.cmd_list_lock, flags);
        do {
                ucmd->h = dev->handle_counter++;
                u = __ucmd_find_hash(dev, ucmd->h);
        } while (u != NULL);
        head = &dev->ucmd_hash[scst_user_cmd_hashfn(ucmd->h)];
        list_add_tail(&ucmd->hash_list_entry, head);
-       spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, flags);
+       spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock, flags);
 
        TRACE_DBG("Inserted ucmd %p, h=%d (dev %s)", ucmd, ucmd->h, dev->name);
        return;
@@ -362,9 +366,9 @@ static inline void cmd_remove_hash(struct scst_user_cmd *ucmd)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
+       spin_lock_irqsave(&ucmd->dev->udev_cmd_threads.cmd_list_lock, flags);
        list_del(&ucmd->hash_list_entry);
-       spin_unlock_irqrestore(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
+       spin_unlock_irqrestore(&ucmd->dev->udev_cmd_threads.cmd_list_lock, flags);
 
        TRACE_DBG("Removed ucmd %p, h=%d", ucmd, ucmd->h);
        return;
@@ -1073,7 +1077,7 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
        if (ucmd->cmd)
                do_wake |= ucmd->cmd->preprocessing_only;
 
-       spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, flags);
+       spin_lock_irqsave(&dev->udev_cmd_threads.cmd_list_lock, flags);
 
        ucmd->this_state_unjammed = 0;
 
@@ -1118,10 +1122,10 @@ static void dev_user_add_to_ready(struct scst_user_cmd *ucmd)
 
        if (do_wake) {
                TRACE_DBG("Waking up dev %p", dev);
-               wake_up(&dev->cmd_lists.cmd_list_waitQ);
+               wake_up(&dev->udev_cmd_threads.cmd_list_waitQ);
        }
 
-       spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, flags);
+       spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock, flags);
 
        TRACE_EXIT();
        return;
@@ -1504,7 +1508,7 @@ static int dev_user_process_reply(struct scst_user_dev *dev,
 
        TRACE_ENTRY();
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        ucmd = __ucmd_find_hash(dev, reply->cmd_h);
        if (unlikely(ucmd == NULL)) {
@@ -1549,7 +1553,7 @@ static int dev_user_process_reply(struct scst_user_dev *dev,
        ucmd->sent_to_user = 0;
 
 unlock_process:
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        switch (state) {
        case UCMD_STATE_PARSING:
@@ -1602,11 +1606,11 @@ out_wrong_state:
        dev_user_unjam_cmd(ucmd, 0, NULL);
 
 out_unlock_put:
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
        goto out_put;
 
 out_unlock:
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
        goto out;
 }
 
@@ -1682,7 +1686,7 @@ static int dev_user_get_ext_cdb(struct file *file, void __user *arg)
 
        TRACE_BUFFER("Get ext cdb", &get, sizeof(get));
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        ucmd = __ucmd_find_hash(dev, get.cmd_h);
        if (unlikely(ucmd == NULL)) {
@@ -1708,7 +1712,7 @@ static int dev_user_get_ext_cdb(struct file *file, void __user *arg)
                goto out_unlock;
        }
 
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        if (cmd == NULL)
                goto out_put;
@@ -1739,27 +1743,27 @@ out:
        return res;
 
 out_unlock:
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
        goto out_up;
 }
 
 static int dev_user_process_scst_commands(struct scst_user_dev *dev)
-       __releases(&dev->cmd_lists.cmd_list_lock)
-       __acquires(&dev->cmd_lists.cmd_list_lock)
+       __releases(&dev->udev_cmd_threads.cmd_list_lock)
+       __acquires(&dev->udev_cmd_threads.cmd_list_lock)
 {
        int res = 0;
 
        TRACE_ENTRY();
 
-       while (!list_empty(&dev->cmd_lists.active_cmd_list)) {
+       while (!list_empty(&dev->udev_cmd_threads.active_cmd_list)) {
                struct scst_cmd *cmd = list_entry(
-                       dev->cmd_lists.active_cmd_list.next, typeof(*cmd),
+                       dev->udev_cmd_threads.active_cmd_list.next, typeof(*cmd),
                        cmd_list_entry);
                TRACE_DBG("Deleting cmd %p from active cmd list", cmd);
                list_del(&cmd->cmd_list_entry);
-               spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+               spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                scst_process_active_cmd(cmd, false);
-               spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+               spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                res++;
        }
 
@@ -1767,10 +1771,10 @@ static int dev_user_process_scst_commands(struct scst_user_dev *dev)
        return res;
 }
 
-/* Called under cmd_lists.cmd_list_lock and IRQ off */
+/* Called under udev_cmd_threads.cmd_list_lock and IRQ off */
 static struct scst_user_cmd *__dev_user_get_next_cmd(struct list_head *cmd_list)
-       __releases(&dev->cmd_lists.cmd_list_lock)
-       __acquires(&dev->cmd_lists.cmd_list_lock)
+       __releases(&dev->udev_cmd_threads.cmd_list_lock)
+       __acquires(&dev->udev_cmd_threads.cmd_list_lock)
 {
        struct scst_user_cmd *u;
 
@@ -1792,7 +1796,7 @@ again:
 
                                EXTRACHECKS_BUG_ON(u->jammed);
 
-                               spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+                               spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
                                rc = scst_check_local_events(u->cmd);
                                if (unlikely(rc != 0)) {
@@ -1804,11 +1808,11 @@ again:
                                         * !! already freed                !!
                                         */
                                        spin_lock_irq(
-                                               &dev->cmd_lists.cmd_list_lock);
+                                               &dev->udev_cmd_threads.cmd_list_lock);
                                        goto again;
                                }
 
-                               spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+                               spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                        } else if (unlikely(test_bit(SCST_CMD_ABORTED,
                                        &u->cmd->cmd_flags))) {
                                switch (u->state) {
@@ -1828,16 +1832,16 @@ again:
        return u;
 }
 
-static inline int test_cmd_lists(struct scst_user_dev *dev)
+static inline int test_cmd_threads(struct scst_user_dev *dev)
 {
-       int res = !list_empty(&dev->cmd_lists.active_cmd_list) ||
+       int res = !list_empty(&dev->udev_cmd_threads.active_cmd_list) ||
                  !list_empty(&dev->ready_cmd_list) ||
                  !dev->blocking || dev->cleanup_done ||
                  signal_pending(current);
        return res;
 }
 
-/* Called under cmd_lists.cmd_list_lock and IRQ off */
+/* Called under udev_cmd_threads.cmd_list_lock and IRQ off */
 static int dev_user_get_next_cmd(struct scst_user_dev *dev,
        struct scst_user_cmd **ucmd)
 {
@@ -1849,20 +1853,20 @@ static int dev_user_get_next_cmd(struct scst_user_dev *dev,
        init_waitqueue_entry(&wait, current);
 
        while (1) {
-               if (!test_cmd_lists(dev)) {
+               if (!test_cmd_threads(dev)) {
                        add_wait_queue_exclusive_head(
-                               &dev->cmd_lists.cmd_list_waitQ,
+                               &dev->udev_cmd_threads.cmd_list_waitQ,
                                &wait);
                        for (;;) {
                                set_current_state(TASK_INTERRUPTIBLE);
-                               if (test_cmd_lists(dev))
+                               if (test_cmd_threads(dev))
                                        break;
-                               spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+                               spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                                schedule();
-                               spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+                               spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                        }
                        set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&dev->cmd_lists.cmd_list_waitQ,
+                       remove_wait_queue(&dev->udev_cmd_threads.cmd_list_waitQ,
                                &wait);
                }
 
@@ -1948,7 +1952,7 @@ static int dev_user_reply_get_cmd(struct file *file, void __user *arg)
 
        kmem_cache_free(user_get_cmd_cachep, cmd);
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 again:
        res = dev_user_get_next_cmd(dev, &ucmd);
        if (res == 0) {
@@ -1963,7 +1967,7 @@ again:
                        /* Oops, this ucmd is already being destroyed. Retry. */
                        goto again;
                }
-               spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+               spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
                EXTRACHECKS_BUG_ON(ucmd->user_cmd_payload_len == 0);
 
@@ -1977,10 +1981,10 @@ again:
                                "%p back to head of ready cmd list", rc, ucmd);
                        res = -EFAULT;
                        /* Requeue ucmd back */
-                       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                        list_add(&ucmd->ready_cmd_list_entry,
                                &dev->ready_cmd_list);
-                       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                }
 #ifdef CONFIG_SCST_EXTRACHECKS
                else
@@ -1988,7 +1992,7 @@ again:
 #endif
                ucmd_put(ucmd);
        } else
-               spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+               spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
 out_up:
        up_read(&dev->dev_rwsem);
@@ -2118,30 +2122,30 @@ static unsigned int dev_user_poll(struct file *file, poll_table *wait)
        down_read(&dev->dev_rwsem);
        mutex_unlock(&dev_priv_mutex);
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        if (!list_empty(&dev->ready_cmd_list) ||
-           !list_empty(&dev->cmd_lists.active_cmd_list)) {
+           !list_empty(&dev->udev_cmd_threads.active_cmd_list)) {
                res |= POLLIN | POLLRDNORM;
                goto out_unlock;
        }
 
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        TRACE_DBG("Before poll_wait() (dev %s)", dev->name);
-       poll_wait(file, &dev->cmd_lists.cmd_list_waitQ, wait);
+       poll_wait(file, &dev->udev_cmd_threads.cmd_list_waitQ, wait);
        TRACE_DBG("After poll_wait() (dev %s)", dev->name);
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        if (!list_empty(&dev->ready_cmd_list) ||
-           !list_empty(&dev->cmd_lists.active_cmd_list)) {
+           !list_empty(&dev->udev_cmd_threads.active_cmd_list)) {
                res |= POLLIN | POLLRDNORM;
                goto out_unlock;
        }
 
 out_unlock:
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        up_read(&dev->dev_rwsem);
 
@@ -2151,12 +2155,13 @@ out:
 }
 
 /*
- * Called under cmd_lists.cmd_list_lock, but can drop it inside, then reacquire.
+ * Called under udev_cmd_threads.cmd_list_lock, but can drop it inside,
+ * then reacquire.
  */
 static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
        unsigned long *flags)
-       __releases(&dev->cmd_lists.cmd_list_lock)
-       __acquires(&dev->cmd_lists.cmd_list_lock)
+       __releases(&dev->udev_cmd_threads.cmd_list_lock)
+       __acquires(&dev->udev_cmd_threads.cmd_list_lock)
 {
        int state = ucmd->state;
        struct scst_user_dev *dev = ucmd->dev;
@@ -2189,16 +2194,16 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
 
                TRACE_MGMT_DBG("Adding ucmd %p to active list", ucmd);
                list_add(&ucmd->cmd->cmd_list_entry,
-                       &ucmd->cmd->cmd_lists->active_cmd_list);
-               wake_up(&ucmd->dev->cmd_lists.cmd_list_waitQ);
+                       &ucmd->cmd->cmd_threads->active_cmd_list);
+               wake_up(&ucmd->cmd->cmd_threads->cmd_list_waitQ);
                break;
 
        case UCMD_STATE_EXECING:
                if (flags != NULL)
-                       spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock,
+                       spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock,
                                               *flags);
                else
-                       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
                TRACE_MGMT_DBG("EXEC: unjamming ucmd %p", ucmd);
 
@@ -2217,10 +2222,10 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
                /* !! At this point cmd and ucmd can be already freed !! */
 
                if (flags != NULL)
-                       spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock,
+                       spin_lock_irqsave(&dev->udev_cmd_threads.cmd_list_lock,
                                          *flags);
                else
-                       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                break;
 
        case UCMD_STATE_ON_FREEING:
@@ -2229,10 +2234,10 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
        case UCMD_STATE_ATTACH_SESS:
        case UCMD_STATE_DETACH_SESS:
                if (flags != NULL)
-                       spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock,
+                       spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock,
                                               *flags);
                else
-                       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
                switch (state) {
                case UCMD_STATE_ON_FREEING:
@@ -2255,10 +2260,10 @@ static void dev_user_unjam_cmd(struct scst_user_cmd *ucmd, int busy,
                }
 
                if (flags != NULL)
-                       spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock,
+                       spin_lock_irqsave(&dev->udev_cmd_threads.cmd_list_lock,
                                          *flags);
                else
-                       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                break;
 
        default:
@@ -2273,8 +2278,8 @@ out:
 }
 
 static int dev_user_unjam_dev(struct scst_user_dev *dev)
-       __releases(&dev->cmd_lists.cmd_list_lock)
-       __acquires(&dev->cmd_lists.cmd_list_lock)
+       __releases(&dev->udev_cmd_threads.cmd_list_lock)
+       __acquires(&dev->udev_cmd_threads.cmd_list_lock)
 {
        int i, res = 0;
        struct scst_user_cmd *ucmd;
@@ -2286,7 +2291,7 @@ static int dev_user_unjam_dev(struct scst_user_dev *dev)
        sgv_pool_flush(dev->pool);
        sgv_pool_flush(dev->pool_clust);
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
 repeat:
        for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++) {
@@ -2306,9 +2311,9 @@ repeat:
 
                        dev_user_unjam_cmd(ucmd, 0, NULL);
 
-                       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
                        ucmd_put(ucmd);
-                       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+                       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
                        goto repeat;
                }
@@ -2317,7 +2322,7 @@ repeat:
        if (dev_user_process_scst_commands(dev) != 0)
                goto repeat;
 
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        TRACE_EXIT_RES(res);
        return res;
@@ -2359,7 +2364,7 @@ static void dev_user_abort_ready_commands(struct scst_user_dev *dev)
 
        TRACE_ENTRY();
 
-       spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, flags);
+       spin_lock_irqsave(&dev->udev_cmd_threads.cmd_list_lock, flags);
 again:
        list_for_each_entry(ucmd, &dev->ready_cmd_list, ready_cmd_list_entry) {
                if ((ucmd->cmd != NULL) && !ucmd->seen_by_user &&
@@ -2376,7 +2381,7 @@ again:
                }
        }
 
-       spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, flags);
+       spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock, flags);
 
        TRACE_EXIT();
        return;
@@ -2489,7 +2494,6 @@ static int dev_user_attach(struct scst_device *sdev)
                goto out;
        }
 
-       sdev->p_cmd_lists = &dev->cmd_lists;
        sdev->dh_priv = dev;
        sdev->tst = dev->tst;
        sdev->queue_alg = dev->queue_alg;
@@ -2536,7 +2540,7 @@ static int dev_user_process_reply_sess(struct scst_user_cmd *ucmd, int status)
 
        TRACE_MGMT_DBG("ucmd %p, cmpl %p, status %d", ucmd, ucmd->cmpl, status);
 
-       spin_lock_irqsave(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
+       spin_lock_irqsave(&ucmd->dev->udev_cmd_threads.cmd_list_lock, flags);
 
        if (ucmd->state == UCMD_STATE_ATTACH_SESS) {
                TRACE_MGMT_DBG("%s", "ATTACH_SESS finished");
@@ -2549,7 +2553,7 @@ static int dev_user_process_reply_sess(struct scst_user_cmd *ucmd, int status)
        if (ucmd->cmpl != NULL)
                complete_all(ucmd->cmpl);
 
-       spin_unlock_irqrestore(&ucmd->dev->cmd_lists.cmd_list_lock, flags);
+       spin_unlock_irqrestore(&ucmd->dev->udev_cmd_threads.cmd_list_lock, flags);
 
        ucmd_put(ucmd);
 
@@ -2567,6 +2571,8 @@ static int dev_user_attach_tgt(struct scst_tgt_dev *tgt_dev)
 
        TRACE_ENTRY();
 
+       tgt_dev->active_cmd_threads = &dev->udev_cmd_threads;
+
        /*
         * We can't replace tgt_dev->pool, because it can be used to allocate
         * memory for SCST local commands, like REPORT LUNS, where there is no
@@ -2621,9 +2627,9 @@ static int dev_user_attach_tgt(struct scst_tgt_dev *tgt_dev)
 
        sBUG_ON(irqs_disabled());
 
-       spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
        ucmd->cmpl = NULL;
-       spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+       spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
        ucmd_put(ucmd);
 
@@ -2801,9 +2807,6 @@ static int dev_user_register_dev(struct file *file,
        }
 
        init_rwsem(&dev->dev_rwsem);
-       spin_lock_init(&dev->cmd_lists.cmd_list_lock);
-       INIT_LIST_HEAD(&dev->cmd_lists.active_cmd_list);
-       init_waitqueue_head(&dev->cmd_lists.cmd_list_waitQ);
        INIT_LIST_HEAD(&dev->ready_cmd_list);
        if (file->f_flags & O_NONBLOCK) {
                TRACE_DBG("%s", "Non-blocking operations");
@@ -2813,6 +2816,8 @@ static int dev_user_register_dev(struct file *file,
        for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++)
                INIT_LIST_HEAD(&dev->ucmd_hash[i]);
 
+       scst_init_threads(&dev->udev_cmd_threads);
+
        strlcpy(dev->name, dev_desc->name, sizeof(dev->name)-1);
 
        scst_init_mem_lim(&dev->udev_mem_lim);
@@ -2980,7 +2985,7 @@ static int dev_user_unregister_dev(struct file *file)
        }
 
        dev->blocking = 0;
-       wake_up_all(&dev->cmd_lists.cmd_list_waitQ);
+       wake_up_all(&dev->udev_cmd_threads.cmd_list_waitQ);
 
        down_write(&dev->dev_rwsem);
        file->private_data = NULL;
@@ -3333,7 +3338,7 @@ static int dev_user_exit_dev(struct scst_user_dev *dev)
        spin_unlock(&dev_list_lock);
 
        dev->blocking = 0;
-       wake_up_all(&dev->cmd_lists.cmd_list_waitQ);
+       wake_up_all(&dev->udev_cmd_threads.cmd_list_waitQ);
 
        spin_lock(&cleanup_lock);
        list_add_tail(&dev->cleanup_list_entry, &cleanup_list);
@@ -3352,13 +3357,15 @@ static int dev_user_exit_dev(struct scst_user_dev *dev)
        dev->cleanup_done = 1;
 
        wake_up(&cleanup_list_waitQ);
-       wake_up(&dev->cmd_lists.cmd_list_waitQ);
+       wake_up(&dev->udev_cmd_threads.cmd_list_waitQ);
 
        wait_for_completion(&dev->cleanup_cmpl);
 
        sgv_pool_del(dev->pool_clust);
        sgv_pool_del(dev->pool);
 
+       scst_deinit_threads(&dev->udev_cmd_threads);
+
        TRACE_MGMT_DBG("Releasing completed (dev %p)", dev);
 
        module_put(THIS_MODULE);
@@ -3410,7 +3417,7 @@ static int dev_user_process_cleanup(struct scst_user_dev *dev)
        TRACE_ENTRY();
 
        sBUG_ON(dev->blocking);
-       wake_up_all(&dev->cmd_lists.cmd_list_waitQ); /* just in case */
+       wake_up_all(&dev->udev_cmd_threads.cmd_list_waitQ); /* just in case */
 
        while (1) {
                int rc1;
@@ -3421,13 +3428,13 @@ static int dev_user_process_cleanup(struct scst_user_dev *dev)
                if ((rc1 == 0) && (rc == -EAGAIN) && dev->cleanup_done)
                        break;
 
-               spin_lock_irq(&dev->cmd_lists.cmd_list_lock);
+               spin_lock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
                rc = dev_user_get_next_cmd(dev, &ucmd);
                if (rc == 0)
                        dev_user_unjam_cmd(ucmd, 1, NULL);
 
-               spin_unlock_irq(&dev->cmd_lists.cmd_list_lock);
+               spin_unlock_irq(&dev->udev_cmd_threads.cmd_list_lock);
 
                if (rc == -EAGAIN) {
                        if (!dev->cleanup_done) {
@@ -3478,7 +3485,7 @@ static ssize_t dev_user_sysfs_commands_show(struct kobject *kobj,
        dev = container_of(kobj, struct scst_device, dev_kobj);
        udev = (struct scst_user_dev *)dev->dh_priv;
 
-       spin_lock_irqsave(&udev->cmd_lists.cmd_list_lock, flags);
+       spin_lock_irqsave(&udev->udev_cmd_threads.cmd_list_lock, flags);
        for (i = 0; i < (int)ARRAY_SIZE(udev->ucmd_hash); i++) {
                struct list_head *head = &udev->ucmd_hash[i];
                struct scst_user_cmd *ucmd;
@@ -3501,7 +3508,7 @@ static ssize_t dev_user_sysfs_commands_show(struct kobject *kobj,
                        }
                }
        }
-       spin_unlock_irqrestore(&udev->cmd_lists.cmd_list_lock, flags);
+       spin_unlock_irqrestore(&udev->udev_cmd_threads.cmd_list_lock, flags);
 
        TRACE_EXIT_RES(pos);
        return pos;
@@ -3525,7 +3532,7 @@ static int dev_user_read_proc(struct seq_file *seq, struct scst_dev_type *dev_ty
        list_for_each_entry(dev, &dev_list, dev_list_entry) {
                int i;
                seq_printf(seq, "Device %s commands:\n", dev->name);
-               spin_lock_irqsave(&dev->cmd_lists.cmd_list_lock, flags);
+               spin_lock_irqsave(&dev->udev_cmd_threads.cmd_list_lock, flags);
                for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++) {
                        struct list_head *head = &dev->ucmd_hash[i];
                        struct scst_user_cmd *ucmd;
@@ -3539,7 +3546,7 @@ static int dev_user_read_proc(struct seq_file *seq, struct scst_dev_type *dev_ty
                                        ucmd->aborted, ucmd->jammed, ucmd->cmd);
                        }
                }
-               spin_unlock_irqrestore(&dev->cmd_lists.cmd_list_lock, flags);
+               spin_unlock_irqrestore(&dev->udev_cmd_threads.cmd_list_lock, flags);
        }
        spin_unlock(&dev_list_lock);
 
index 444c354..7d9e550 100644 (file)
@@ -214,11 +214,15 @@ struct scst_vdisk_dev {
                                   must be <= SCSI Model + 1 */
        char *filename;         /* File name, protected by
                                   scst_mutex and suspended activities */
+       unsigned int t10_dev_id_set:1; /* true if t10_dev_id manually set */
        char t10_dev_id[16+8+2]; /* T10 device ID */
        char usn[MAX_USN_LEN];
        struct scst_device *dev;
        struct list_head vdev_list_entry;
 
+       int threads_num;
+       enum scst_dev_type_threads_pool_type threads_pool_type;
+
        struct mutex vdev_sysfs_mutex;
        struct scst_dev_type *vdev_devt;
 };
@@ -443,7 +447,8 @@ static struct scst_dev_type vdisk_file_devtype = {
        .del_device =           vdisk_del_device,
        .dev_attrs =            vdisk_fileio_attrs,
        .add_device_parameters_help = "filename, blocksize, write_through, "
-               "nv_cache, o_direct, read_only, removable",
+               "nv_cache, o_direct, read_only, removable, threads_num, "
+               "threads_pool_type",
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
@@ -479,7 +484,7 @@ static struct scst_dev_type vdisk_blk_devtype = {
        .del_device =           vdisk_del_device,
        .dev_attrs =            vdisk_blockio_attrs,
        .add_device_parameters_help = "filename, blocksize, read_only, "
-               "removable",
+               "removable, threads_num, threads_pool_type",
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
@@ -512,7 +517,8 @@ static struct scst_dev_type vdisk_null_devtype = {
        .add_device =           vdisk_add_nullio_device,
        .del_device =           vdisk_del_device,
        .dev_attrs =            vdisk_nullio_attrs,
-       .add_device_parameters_help = "blocksize, read_only, removable",
+       .add_device_parameters_help = "blocksize, read_only, removable, "
+               "threads_num, threads_pool_type",
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
@@ -546,6 +552,7 @@ static struct scst_dev_type vcdrom_devtype = {
        .add_device =           vcdrom_add_device,
        .del_device =           vcdrom_del_device,
        .dev_attrs =            vcdrom_attrs,
+       .add_device_parameters_help = "threads_num, threads_pool_type",
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
@@ -693,6 +700,8 @@ static int vdisk_attach(struct scst_device *dev)
        virt_dev->dev = dev;
 
        dev->rd_only = virt_dev->rd_only;
+       dev->threads_num = virt_dev->threads_num;
+       dev->threads_pool_type = virt_dev->threads_pool_type;
 
        if (!virt_dev->cdrom_empty) {
                if (virt_dev->nullio)
@@ -2952,6 +2961,8 @@ static int vdev_create(struct scst_dev_type *devt,
        spin_lock_init(&virt_dev->flags_lock);
        mutex_init(&virt_dev->vdev_sysfs_mutex);
        virt_dev->vdev_devt = devt;
+       virt_dev->threads_num = devt->threads_num;
+       virt_dev->threads_pool_type = devt->threads_pool_type;
 
        virt_dev->rd_only = DEF_RD_ONLY;
        virt_dev->removable = DEF_REMOVABLE;
@@ -3018,24 +3029,15 @@ static struct scst_vdisk_dev *vdev_find(const char *name)
 
 #ifndef CONFIG_SCST_PROC
 
-/* scst_vdisk_mutex supposed to be held */
-static int vdev_fileio_add_device(const char *device_name, char *params)
+static int vdev_parse_add_dev_params(struct scst_vdisk_dev *virt_dev,
+       char *params, const char *allowed_params[])
 {
        int res = 0;
        unsigned long val;
        char *param, *p, *pp;
-       struct scst_vdisk_dev *virt_dev;
 
        TRACE_ENTRY();
 
-       res = vdev_create(&vdisk_file_devtype, device_name, &virt_dev);
-       if (res != 0)
-               goto out;
-
-       virt_dev->wt_flag = DEF_WRITE_THROUGH;
-       virt_dev->nv_cache = DEF_NV_CACHE;
-       virt_dev->o_direct_flag = DEF_O_DIRECT;
-
        while (1) {
                param = scst_get_next_token_str(&params);
                if (param == NULL)
@@ -3044,49 +3046,79 @@ static int vdev_fileio_add_device(const char *device_name, char *params)
                p = scst_get_next_lexem(&param);
                if (*p == '\0') {
                        PRINT_ERROR("Syntax error at %s (device %s)",
-                               param, device_name);
+                               param, virt_dev->name);
                        res = -EINVAL;
-                       goto out_destroy;
+                       goto out;
                }
 
                pp = scst_get_next_lexem(&param);
                if (*pp == '\0') {
                        PRINT_ERROR("Parameter %s value missed for device %s",
-                               p, device_name);
+                               p, virt_dev->name);
                        res = -EINVAL;
-                       goto out_destroy;
+                       goto out;
                }
 
                if (scst_get_next_lexem(&param)[0] != '\0') {
                        PRINT_ERROR("Too many parameter's %s values (device %s)",
-                               p, device_name);
+                               p, virt_dev->name);
                        res = -EINVAL;
-                       goto out_destroy;
+                       goto out;
+               }
+
+               if (allowed_params != NULL) {
+                       const char **a = allowed_params;
+                       bool allowed = false;
+
+                       while (*a != NULL) {
+                               if (!strcasecmp(*a, p)) {
+                                       allowed = true;
+                                       break;
+                               }
+                               a++;
+                       }
+
+                       if (!allowed) {
+                               PRINT_ERROR("Unknown parameter %s (device %s)", p,
+                                       virt_dev->name);
+                               res = -EINVAL;
+                               goto out;
+                       }
                }
 
                if (!strcasecmp("filename", p)) {
                        if (*pp != '/') {
                                PRINT_ERROR("Filename %s must be global "
-                                       "(device %s)", pp, device_name);
+                                       "(device %s)", pp, virt_dev->name);
                                res = -EINVAL;
-                               goto out_destroy;
+                               goto out;
                        }
 
                        virt_dev->filename = kstrdup(pp, GFP_KERNEL);
                        if (virt_dev->filename == NULL) {
                                PRINT_ERROR("Unable to duplicate file name %s "
-                                       "(device %s)", pp, device_name);
+                                       "(device %s)", pp, virt_dev->name);
                                res = -ENOMEM;
-                               goto out_destroy;
+                               goto out;
+                       }
+                       continue;
+               } else if (!strcasecmp("threads_pool_type", p)) {
+                       virt_dev->threads_pool_type = scst_parse_threads_pool_type(pp,
+                                                       strlen(pp));
+                       if (virt_dev->threads_pool_type == SCST_THREADS_POOL_TYPE_INVALID) {
+                               res = virt_dev->threads_pool_type;
+                               goto out;
                        }
+                       TRACE_DBG("threads_pool_type %d",
+                               virt_dev->threads_pool_type);
                        continue;
                }
 
                res = strict_strtoul(pp, 0, &val);
                if (res != 0) {
                        PRINT_ERROR("strict_strtoul() for %s failed: %d "
-                               "(device %s)", pp, res, device_name);
-                       goto out_destroy;
+                               "(device %s)", pp, res, virt_dev->name);
+                       goto out;
                }
 
                if (!strcasecmp("write_through", p)) {
@@ -3102,7 +3134,7 @@ static int vdev_fileio_add_device(const char *device_name, char *params)
 #else
                        PRINT_INFO("O_DIRECT flag doesn't currently"
                                " work, ignoring it, use fileio_tgt "
-                               "in O_DIRECT mode instead (device %s)", device_name);
+                               "in O_DIRECT mode instead (device %s)", virt_dev->name);
 #endif
                } else if (!strcasecmp("read_only", p)) {
                        virt_dev->rd_only = val;
@@ -3110,25 +3142,53 @@ static int vdev_fileio_add_device(const char *device_name, char *params)
                } else if (!strcasecmp("removable", p)) {
                        virt_dev->removable = val;
                        TRACE_DBG("REMOVABLE %d", virt_dev->removable);
+               } else if (!strcasecmp("threads_num", p)) {
+                       virt_dev->threads_num = val;
+                       TRACE_DBG("threads_num %d", virt_dev->threads_num);
                } else if (!strcasecmp("blocksize", p)) {
                        virt_dev->block_size = val;
                        virt_dev->block_shift = scst_calc_block_shift(
                                                        virt_dev->block_size);
                        if (virt_dev->block_shift < 9) {
                                res = -EINVAL;
-                               goto out_destroy;
+                               goto out;
                        }
                        TRACE_DBG("block_size %d, block_shift %d",
                                virt_dev->block_size,
                                virt_dev->block_shift);
                } else {
                        PRINT_ERROR("Unknown parameter %s (device %s)", p,
-                               device_name);
+                               virt_dev->name);
                        res = -EINVAL;
-                       goto out_destroy;
+                       goto out;
                }
        }
 
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static int vdev_fileio_add_device(const char *device_name, char *params)
+{
+       int res = 0;
+       struct scst_vdisk_dev *virt_dev;
+
+       TRACE_ENTRY();
+
+       res = vdev_create(&vdisk_file_devtype, device_name, &virt_dev);
+       if (res != 0)
+               goto out;
+
+       virt_dev->wt_flag = DEF_WRITE_THROUGH;
+       virt_dev->nv_cache = DEF_NV_CACHE;
+       virt_dev->o_direct_flag = DEF_O_DIRECT;
+
+       res = vdev_parse_add_dev_params(virt_dev, params, NULL);
+       if (res != 0)
+               goto out_destroy;
+
        if (virt_dev->rd_only && (virt_dev->wt_flag || virt_dev->nv_cache)) {
                PRINT_ERROR("Write options on read only device %s",
                        virt_dev->name);
@@ -3172,8 +3232,8 @@ out_destroy:
 static int vdev_blockio_add_device(const char *device_name, char *params)
 {
        int res = 0;
-       unsigned long val;
-       char *param, *p, *pp;
+       const char *allowed_params[] = { "filename", "threads_pool_type",
+               "read_only", "removable", "threads_num", "blocksize", NULL };
        struct scst_vdisk_dev *virt_dev;
 
        TRACE_ENTRY();
@@ -3184,83 +3244,9 @@ static int vdev_blockio_add_device(const char *device_name, char *params)
 
        virt_dev->blockio = 1;
 
-       while (1) {
-               param = scst_get_next_token_str(&params);
-               if (param == NULL)
-                       break;
-
-               p = scst_get_next_lexem(&param);
-               if (*p == '\0') {
-                       PRINT_ERROR("Syntax error at %s (device %s)",
-                               param, device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-
-               pp = scst_get_next_lexem(&param);
-               if (*pp == '\0') {
-                       PRINT_ERROR("Parameter %s value missed for device %s",
-                               p, device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-
-               if (scst_get_next_lexem(&param)[0] != '\0') {
-                       PRINT_ERROR("Too many parameter's %s values (device %s)",
-                               p, device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-
-               if (!strcasecmp("filename", p)) {
-                       if (*pp != '/') {
-                               PRINT_ERROR("Filename %s must be global "
-                                       "(device %s)", pp, device_name);
-                               res = -EINVAL;
-                               goto out_destroy;
-                       }
-
-                       virt_dev->filename = kstrdup(pp, GFP_KERNEL);
-                       if (virt_dev->filename == NULL) {
-                               PRINT_ERROR("Unable to duplicate file name %s "
-                                       "(device %s)", pp, device_name);
-                               res = -ENOMEM;
-                               goto out_destroy;
-                       }
-                       continue;
-               }
-
-               res = strict_strtoul(pp, 0, &val);
-               if (res != 0) {
-                       PRINT_ERROR("strict_strtoul() for %s failed: %d "
-                               "(device %s)", pp, res, device_name);
-                       goto out_destroy;
-               }
-
-               if (!strcasecmp("read_only", p)) {
-                       virt_dev->rd_only = val;
-                       TRACE_DBG("READ ONLY %d", virt_dev->rd_only);
-               } else if (!strcasecmp("removable", p)) {
-                       virt_dev->removable = val;
-                       TRACE_DBG("REMOVABLE %d", virt_dev->removable);
-               } else if (!strcasecmp("blocksize", p)) {
-                       virt_dev->block_size = val;
-                       virt_dev->block_shift = scst_calc_block_shift(
-                                                       virt_dev->block_size);
-                       if (virt_dev->block_shift < 9) {
-                               res = -EINVAL;
-                               goto out_destroy;
-                       }
-                       TRACE_DBG("block_size %d, block_shift %d",
-                               virt_dev->block_size,
-                               virt_dev->block_shift);
-               } else {
-                       PRINT_ERROR("Unknown parameter %s (device %s)", p,
-                               device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-       }
+       res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
+       if (res != 0)
+               goto out_destroy;
 
        if (virt_dev->filename == NULL) {
                PRINT_ERROR("File name required (device %s)", virt_dev->name);
@@ -3298,8 +3284,8 @@ out_destroy:
 static int vdev_nullio_add_device(const char *device_name, char *params)
 {
        int res = 0;
-       unsigned long val;
-       char *param, *p, *pp;
+       const char *allowed_params[] = { "threads_pool_type",
+               "read_only", "removable", "threads_num", "blocksize", NULL };
        struct scst_vdisk_dev *virt_dev;
 
        TRACE_ENTRY();
@@ -3310,65 +3296,9 @@ static int vdev_nullio_add_device(const char *device_name, char *params)
 
        virt_dev->nullio = 1;
 
-       while (1) {
-               param = scst_get_next_token_str(&params);
-               if (param == NULL)
-                       break;
-
-               p = scst_get_next_lexem(&param);
-               if (*p == '\0') {
-                       PRINT_ERROR("Syntax error at %s (device %s)",
-                               param, device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-
-               pp = scst_get_next_lexem(&param);
-               if (*pp == '\0') {
-                       PRINT_ERROR("Parameter %s value missed for device %s",
-                               p, device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-
-               if (scst_get_next_lexem(&param)[0] != '\0') {
-                       PRINT_ERROR("Too many parameter's %s values (device %s)",
-                               p, device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-
-               res = strict_strtoul(pp, 0, &val);
-               if (res != 0) {
-                       PRINT_ERROR("strict_strtoul() for %s failed: %d "
-                               "(device %s)", pp, res, device_name);
-                       goto out_destroy;
-               }
-
-               if (!strcasecmp("read_only", p)) {
-                       virt_dev->rd_only = val;
-                       TRACE_DBG("READ ONLY %d", virt_dev->rd_only);
-               } else if (!strcasecmp("removable", p)) {
-                       virt_dev->removable = val;
-                       TRACE_DBG("REMOVABLE %d", virt_dev->removable);
-               } else if (!strcasecmp("blocksize", p)) {
-                       virt_dev->block_size = val;
-                       virt_dev->block_shift = scst_calc_block_shift(
-                                                       virt_dev->block_size);
-                       if (virt_dev->block_shift < 9) {
-                               res = -EINVAL;
-                               goto out_destroy;
-                       }
-                       TRACE_DBG("block_size %d, block_shift %d",
-                               virt_dev->block_size,
-                               virt_dev->block_shift);
-               } else {
-                       PRINT_ERROR("Unknown parameter %s (device %s)", p,
-                               device_name);
-                       res = -EINVAL;
-                       goto out_destroy;
-               }
-       }
+       res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
+       if (res != 0)
+               goto out_destroy;
 
        list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
 
@@ -3512,19 +3442,12 @@ out:
 static int __vcdrom_add_device(const char *device_name, char *params)
 {
        int res = 0;
-       char *p;
+       const char *allowed_params[] = { "threads_pool_type",
+                       "threads_num", NULL };
        struct scst_vdisk_dev *virt_dev;
 
        TRACE_ENTRY();
 
-       p = scst_get_next_token_str(&params);
-       if (p != NULL) {
-               PRINT_ERROR("No parameters extected for device %s",
-                       device_name);
-               res = -EINVAL;
-               goto out;
-       }
-
        res = vdev_create(&vcdrom_devtype, device_name, &virt_dev);
        if (res != 0)
                goto out;
@@ -3536,6 +3459,10 @@ static int __vcdrom_add_device(const char *device_name, char *params)
        virt_dev->block_size = DEF_CDROM_BLOCKSIZE;
        virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT;
 
+       res = vdev_parse_add_dev_params(virt_dev, params, allowed_params);
+       if (res != 0)
+               goto out_destroy;
+
        list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
 
        vdisk_report_registering(virt_dev);
@@ -3557,6 +3484,7 @@ out:
 out_del:
        list_del(&virt_dev->vdev_list_entry);
 
+out_destroy:
        vdev_destroy(virt_dev);
        goto out;
 }
@@ -4007,6 +3935,9 @@ static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj,
                }
                i++;
        }
+
+       virt_dev->t10_dev_id_set = 1;
+
        res = count;
 
        PRINT_INFO("T10 device id for device %s changed to %s", virt_dev->name,
@@ -4032,7 +3963,8 @@ static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj,
        virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
 
        read_lock_bh(&vdisk_t10_dev_id_rwlock);
-       pos = sprintf(buf, "%s\n", virt_dev->t10_dev_id);
+       pos = sprintf(buf, "%s\n%s", virt_dev->t10_dev_id,
+               virt_dev->t10_dev_id_set ? SCST_SYSFS_KEY_MARK "\n" : "");
        read_unlock_bh(&vdisk_t10_dev_id_rwlock);
 
        TRACE_EXIT_RES(pos);
index 337972f..e3e473f 100644 (file)
@@ -562,12 +562,10 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess,
 static void scst_tgt_retry_timer_fn(unsigned long arg);
 
 #ifdef CONFIG_SCST_DEBUG_TM
-static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev,
-       struct scst_acg_dev *acg_dev);
+static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev);
 static void tm_dbg_deinit_tgt_dev(struct scst_tgt_dev *tgt_dev);
 #else
-static inline void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev,
-       struct scst_acg_dev *acg_dev) {}
+static inline void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev) {}
 static inline void tm_dbg_deinit_tgt_dev(struct scst_tgt_dev *tgt_dev) {}
 #endif /* CONFIG_SCST_DEBUG_TM */
 
@@ -1997,7 +1995,6 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev)
 {
        struct scst_device *dev;
        int res = 0;
-       static int dev_num; /* protected by scst_mutex */
 
        TRACE_ENTRY();
 
@@ -2010,7 +2007,6 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev)
        }
 
        dev->handler = &scst_null_devtype;
-       dev->p_cmd_lists = &scst_main_cmd_lists;
        atomic_set(&dev->dev_cmd_count, 0);
        atomic_set(&dev->write_cmd_count, 0);
        scst_init_mem_lim(&dev->dev_mem_lim);
@@ -2019,23 +2015,11 @@ int scst_alloc_device(gfp_t gfp_mask, struct scst_device **out_dev)
        INIT_LIST_HEAD(&dev->blocked_cmd_list);
        INIT_LIST_HEAD(&dev->dev_tgt_dev_list);
        INIT_LIST_HEAD(&dev->dev_acg_dev_list);
-       INIT_LIST_HEAD(&dev->threads_list);
        init_waitqueue_head(&dev->on_dev_waitQ);
        dev->dev_double_ua_possible = 1;
        dev->queue_alg = SCST_CONTR_MODE_QUEUE_ALG_UNRESTRICTED_REORDER;
-       dev->dev_num = dev_num++;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) && defined(SCST_IO_CONTEXT)
-#if defined(CONFIG_BLOCK)
-       dev->dev_io_ctx = alloc_io_context(GFP_KERNEL, -1);
-       if (dev->dev_io_ctx == NULL) {
-               TRACE(TRACE_OUT_OF_MEM, "%s", "Failed to alloc dev IO context");
-               res = -ENOMEM;
-               kfree(dev);
-               goto out;
-       }
-#endif
-#endif
+       scst_init_threads(&dev->dev_cmd_threads);
 
        *out_dev = dev;
 
@@ -2057,9 +2041,9 @@ void scst_free_device(struct scst_device *dev)
        }
 #endif
 
-       kfree(dev->virt_name);
-       __exit_io_context(dev->dev_io_ctx);
+       scst_deinit_threads(&dev->dev_cmd_threads);
 
+       kfree(dev->virt_name);
        kfree(dev);
 
        TRACE_EXIT();
@@ -2284,6 +2268,227 @@ struct scst_acg *scst_tgt_find_acg(struct scst_tgt *tgt, const char *name)
        return acg_ret;
 }
 
+/* scst_mutex supposed to be held */
+static struct io_context *scst_find_shared_io_context(
+       struct scst_tgt_dev *tgt_dev)
+{
+       struct io_context *res = NULL;
+       struct scst_acg *acg = tgt_dev->acg_dev->acg;
+       struct scst_tgt_dev *t;
+
+       TRACE_ENTRY();
+
+       switch (acg->acg_mpio_type) {
+       case SCST_ACG_MPIO_AUTO:
+               if (tgt_dev->sess->initiator_name == NULL)
+                       goto out;
+
+               list_for_each_entry(t, &tgt_dev->dev->dev_tgt_dev_list,
+                               dev_tgt_dev_list_entry) {
+                       if ((t == tgt_dev) ||
+                           (t->sess->initiator_name == NULL) ||
+                           (t->active_cmd_threads == NULL))
+                               continue;
+
+                       TRACE_DBG("t name %s (tgt_dev name %s)",
+                               t->sess->initiator_name,
+                               tgt_dev->sess->initiator_name);
+
+                       /* We check other ACG's as well */
+
+                       if (strcmp(t->sess->initiator_name,
+                                       tgt_dev->sess->initiator_name) == 0)
+                               goto found;
+               }
+               break;
+
+       case SCST_ACG_MPIO_ENABLE:
+               list_for_each_entry(t, &tgt_dev->dev->dev_tgt_dev_list,
+                               dev_tgt_dev_list_entry) {
+                       if ((t == tgt_dev) || (t->active_cmd_threads == NULL))
+                               continue;
+
+                       TRACE_DBG("t name %s (tgt_dev name %s)",
+                               t->sess->initiator_name,
+                               tgt_dev->sess->initiator_name);
+
+                       goto found;
+               }
+               break;
+
+       case SCST_ACG_MPIO_DISABLE:
+               goto out;
+
+       default:
+               PRINT_CRIT_ERROR("Unknown MPIO type %d (acg %s)",
+                       acg->acg_mpio_type, acg->acg_name);
+               sBUG();
+               break;
+       }
+
+out:
+       TRACE_EXIT_HRES((unsigned long)res);
+       return res;
+
+found:
+       if (t->active_cmd_threads == &scst_main_cmd_threads) {
+               res = t->tgt_dev_cmd_threads.io_context;
+               TRACE_DBG("Going to share async IO context %p (t %p, ini %s, "
+                       "dev %s, cmd_threads %p)", res, t,
+                       t->sess->initiator_name, t->dev->virt_name,
+                       &t->tgt_dev_cmd_threads);
+       } else {
+               res = t->active_cmd_threads->io_context;
+               if (res == NULL) {
+                       TRACE_MGMT_DBG("IO context for t %p not yet "
+                               "initialized, waiting...", t);
+                       msleep(100);
+                       barrier();
+                       goto found;
+               }
+               TRACE_DBG("Going to share IO context %p (t %p, ini %s, "
+                       "dev %s, cmd_threads %p)", res, t,
+                       t->sess->initiator_name, t->dev->virt_name,
+                       t->active_cmd_threads);
+       }
+       goto out;
+}
+
+enum scst_dev_type_threads_pool_type scst_parse_threads_pool_type(const char *p,
+       int len)
+{
+       enum scst_dev_type_threads_pool_type res;
+
+       if (strncasecmp(p, SCST_THREADS_POOL_PER_INITIATOR_STR,
+                       min_t(int, strlen(SCST_THREADS_POOL_PER_INITIATOR_STR),
+                               len)) == 0)
+               res = SCST_THREADS_POOL_PER_INITIATOR;
+       else if (strncasecmp(p, SCST_THREADS_POOL_SHARED_STR,
+                       min_t(int, strlen(SCST_THREADS_POOL_SHARED_STR),
+                               len)) == 0)
+               res = SCST_THREADS_POOL_SHARED;
+       else {
+               PRINT_ERROR("Unknown threads pool type %s", p);
+               res = SCST_THREADS_POOL_TYPE_INVALID;
+       }
+
+       return res;
+}
+EXPORT_SYMBOL(scst_parse_threads_pool_type);
+
+/* scst_mutex supposed to be held */
+int scst_tgt_dev_setup_threads(struct scst_tgt_dev *tgt_dev)
+{
+       int res = 0;
+       struct scst_device *dev = tgt_dev->dev;
+
+       TRACE_ENTRY();
+
+       if (dev->threads_num <= 0) {
+               tgt_dev->active_cmd_threads = &scst_main_cmd_threads;
+
+               if (dev->threads_num == 0) {
+                       struct io_context *shared_io_context;
+
+                       shared_io_context = scst_find_shared_io_context(tgt_dev);
+                       if (shared_io_context != NULL) {
+                               TRACE_DBG("Linking async io context %p for "
+                                       "shared tgt_dev %p (cmd_threads %p, "
+                                       "dev %s)", shared_io_context, tgt_dev,
+                                       &tgt_dev->tgt_dev_cmd_threads,
+                                       tgt_dev->dev->virt_name);
+                               tgt_dev->tgt_dev_cmd_threads.io_context = 
+                                       ioc_task_link(shared_io_context);
+                       } else {
+                               /* Create new context */
+                               struct io_context *io_context = current->io_context;
+                               current->io_context = NULL;
+                               tgt_dev->tgt_dev_cmd_threads.io_context =
+                                       ioc_task_link(get_io_context(GFP_KERNEL, -1));
+                               current->io_context = io_context;
+                               TRACE_DBG("Created async io context %p for "
+                                       "not shared tgt_dev %p (cmd_threads %p, "
+                                       "dev %s)", tgt_dev->tgt_dev_cmd_threads.io_context,
+                                       tgt_dev, &tgt_dev->tgt_dev_cmd_threads,
+                                       tgt_dev->dev->virt_name);
+                       }
+               }
+
+               res = scst_add_threads(tgt_dev->active_cmd_threads, NULL, NULL,
+                               tgt_dev->sess->tgt->tgtt->threads_num);
+               goto out;
+       }
+
+       switch (dev->threads_pool_type) {
+       case SCST_THREADS_POOL_PER_INITIATOR:
+       {
+               struct io_context *shared_io_context;
+
+               tgt_dev->active_cmd_threads = &tgt_dev->tgt_dev_cmd_threads;
+
+               shared_io_context = scst_find_shared_io_context(tgt_dev);
+               if (shared_io_context != NULL) {
+                       TRACE_DBG("Linking io context %p for "
+                               "shared tgt_dev %p (cmd_threads %p)",
+                               shared_io_context, tgt_dev,
+                               tgt_dev->active_cmd_threads);
+                       tgt_dev->active_cmd_threads->io_context = 
+                               ioc_task_link(shared_io_context);
+               }
+
+               res = scst_add_threads(tgt_dev->active_cmd_threads, NULL,
+                       tgt_dev,
+                       dev->threads_num + tgt_dev->sess->tgt->tgtt->threads_num);
+               break;
+       }
+       case SCST_THREADS_POOL_SHARED:
+               tgt_dev->active_cmd_threads = &dev->dev_cmd_threads;
+
+               res = scst_add_threads(tgt_dev->active_cmd_threads, dev, NULL,
+                       tgt_dev->sess->tgt->tgtt->threads_num);
+               break;
+       default:
+               PRINT_CRIT_ERROR("Unknown threads pool type %d (dev %s)",
+                       dev->threads_pool_type, dev->virt_name);
+               sBUG();
+               break;
+       }
+
+out:
+       if (res == 0)
+               tm_dbg_init_tgt_dev(tgt_dev);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+/* scst_mutex supposed to be held */
+void scst_tgt_dev_stop_threads(struct scst_tgt_dev *tgt_dev)
+{
+       TRACE_ENTRY();
+
+       if (tgt_dev->active_cmd_threads == &scst_main_cmd_threads) {
+               /* Global async threads */
+               scst_del_threads(tgt_dev->active_cmd_threads,
+                       tgt_dev->sess->tgt->tgtt->threads_num);
+               put_io_context(tgt_dev->tgt_dev_cmd_threads.io_context);
+               tgt_dev->tgt_dev_cmd_threads.io_context = NULL;
+       } else if (tgt_dev->active_cmd_threads == &tgt_dev->dev->dev_cmd_threads) {
+               /* Per device shared threads */
+               scst_del_threads(tgt_dev->active_cmd_threads,
+                       tgt_dev->sess->tgt->tgtt->threads_num);
+       } else if (tgt_dev->active_cmd_threads == &tgt_dev->tgt_dev_cmd_threads) {
+               /* Per tgt_dev threads */
+               scst_del_threads(tgt_dev->active_cmd_threads, -1);
+       } /* else no threads (not yet initialized, e.g.) */
+
+       tm_dbg_deinit_tgt_dev(tgt_dev);
+       tgt_dev->active_cmd_threads = NULL;
+
+       TRACE_EXIT();
+       return;
+}
+
 /*
  * scst_mutex supposed to be held, there must not be parallel activity in this
  * session.
@@ -2292,12 +2497,10 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess,
        struct scst_acg_dev *acg_dev)
 {
        int ini_sg, ini_unchecked_isa_dma, ini_use_clustering;
-       struct scst_tgt_dev *tgt_dev, *t = NULL;
+       struct scst_tgt_dev *tgt_dev;
        struct scst_device *dev = acg_dev->dev;
        struct list_head *sess_tgt_dev_list_head;
-       struct scst_tgt_template *vtt = sess->tgt->tgtt;
        int rc, i, sl;
-       bool share_io_ctx = false;
        uint8_t sense_buffer[SCST_STANDARD_SENSE_LEN];
 
        TRACE_ENTRY();
@@ -2360,6 +2563,8 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess,
        for (i = 0; i < (int)ARRAY_SIZE(tgt_dev->sn_slots); i++)
                atomic_set(&tgt_dev->sn_slots[i], 0);
 
+       scst_init_threads(&tgt_dev->tgt_dev_cmd_threads);
+
        if (dev->handler->parse_atomic &&
            (sess->tgt->tgtt->preprocessing_done == NULL)) {
                if (sess->tgt->tgtt->rdy_to_xfer_atomic)
@@ -2388,64 +2593,18 @@ static struct scst_tgt_dev *scst_alloc_add_tgt_dev(struct scst_session *sess,
                dev->d_sense, SCST_LOAD_SENSE(scst_sense_reset_UA));
        scst_alloc_set_UA(tgt_dev, sense_buffer, sl, 0);
 
-       tm_dbg_init_tgt_dev(tgt_dev, acg_dev);
-
-       if (tgt_dev->sess->initiator_name != NULL) {
-               spin_lock_bh(&dev->dev_lock);
-               list_for_each_entry(t, &dev->dev_tgt_dev_list,
-                               dev_tgt_dev_list_entry) {
-                       TRACE_DBG("t name %s (tgt_dev name %s)",
-                               t->sess->initiator_name,
-                               tgt_dev->sess->initiator_name);
-                       if (t->sess->initiator_name == NULL)
-                               continue;
-                       if (strcmp(t->sess->initiator_name,
-                                       tgt_dev->sess->initiator_name) == 0) {
-                               share_io_ctx = true;
-                               break;
-                       }
-               }
-               spin_unlock_bh(&dev->dev_lock);
-       }
-
-       if (share_io_ctx) {
-               TRACE_MGMT_DBG("Sharing IO context %p (tgt_dev %p, ini %s)",
-                       t->tgt_dev_io_ctx, tgt_dev,
-                       tgt_dev->sess->initiator_name);
-               tgt_dev->tgt_dev_io_ctx = ioc_task_link(t->tgt_dev_io_ctx);
-       } else {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) && defined(SCST_IO_CONTEXT)
-#if defined(CONFIG_BLOCK)
-               tgt_dev->tgt_dev_io_ctx = alloc_io_context(GFP_KERNEL, -1);
-               if (tgt_dev->tgt_dev_io_ctx == NULL) {
-                       TRACE(TRACE_OUT_OF_MEM, "Failed to alloc tgt_dev IO "
-                               "context for dev %s (initiator %s)",
-                               dev->virt_name, sess->initiator_name);
-                       goto out_free;
-               }
-#endif
-#endif
-       }
-
-       if (vtt->threads_num > 0) {
-               rc = 0;
-               if (dev->handler->threads_num > 0)
-                       rc = scst_add_dev_threads(dev, vtt->threads_num);
-               else if (dev->handler->threads_num == 0)
-                       rc = scst_add_global_threads(vtt->threads_num);
-               if (rc != 0)
-                       goto out_free;
-       }
+       rc = scst_tgt_dev_setup_threads(tgt_dev);
+       if (rc != 0)
+               goto out_free;
 
        if (dev->handler && dev->handler->attach_tgt) {
-               TRACE_DBG("Calling dev handler's attach_tgt(%p)",
-                     tgt_dev);
+               TRACE_DBG("Calling dev handler's attach_tgt(%p)", tgt_dev);
                rc = dev->handler->attach_tgt(tgt_dev);
                TRACE_DBG("%s", "Dev handler's attach_tgt() returned");
                if (rc != 0) {
                        PRINT_ERROR("Device handler's %s attach_tgt() "
                            "failed: %d", dev->handler->name, rc);
-                       goto out_thr_free;
+                       goto out_stop_threads;
                }
        }
 
@@ -2464,17 +2623,11 @@ out:
        TRACE_EXIT();
        return tgt_dev;
 
-out_thr_free:
-       if (vtt->threads_num > 0) {
-               if (dev->handler->threads_num > 0)
-                       scst_del_dev_threads(dev, vtt->threads_num);
-               else if (dev->handler->threads_num == 0)
-                       scst_del_global_threads(vtt->threads_num);
-       }
+out_stop_threads:
+       scst_tgt_dev_stop_threads(tgt_dev);
 
 out_free:
        scst_free_all_UA(tgt_dev);
-       __exit_io_context(tgt_dev->tgt_dev_io_ctx);
 
        kmem_cache_free(scst_tgtd_cachep, tgt_dev);
        tgt_dev = NULL;
@@ -2513,12 +2666,9 @@ void scst_nexus_loss(struct scst_tgt_dev *tgt_dev, bool queue_UA)
 static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev)
 {
        struct scst_device *dev = tgt_dev->dev;
-       struct scst_tgt_template *vtt = tgt_dev->sess->tgt->tgtt;
 
        TRACE_ENTRY();
 
-       tm_dbg_deinit_tgt_dev(tgt_dev);
-
        spin_lock_bh(&dev->dev_lock);
        list_del(&tgt_dev->dev_tgt_dev_list_entry);
        spin_unlock_bh(&dev->dev_lock);
@@ -2535,14 +2685,11 @@ static void scst_free_tgt_dev(struct scst_tgt_dev *tgt_dev)
                TRACE_DBG("%s", "Dev handler's detach_tgt() returned");
        }
 
-       if (vtt->threads_num > 0) {
-               if (dev->handler->threads_num > 0)
-                       scst_del_dev_threads(dev, vtt->threads_num);
-               else if (dev->handler->threads_num == 0)
-                       scst_del_global_threads(vtt->threads_num);
-       }
+       scst_tgt_dev_stop_threads(tgt_dev);
+
+       scst_deinit_threads(&tgt_dev->tgt_dev_cmd_threads);
 
-       __exit_io_context(tgt_dev->tgt_dev_io_ctx);
+       sBUG_ON(!list_empty(&tgt_dev->thr_data_list));
 
        kmem_cache_free(scst_tgtd_cachep, tgt_dev);
 
@@ -2837,7 +2984,7 @@ static struct scst_cmd *scst_create_prepare_internal_cmd(
        if (res == NULL)
                goto out;
 
-       res->cmd_lists = orig_cmd->cmd_lists;
+       res->cmd_threads = orig_cmd->cmd_threads;
        res->sess = orig_cmd->sess;
        res->atomic = scst_cmd_atomic(orig_cmd);
        res->internal = 1;
@@ -2894,10 +3041,10 @@ int scst_prepare_request_sense(struct scst_cmd *orig_cmd)
 
        TRACE_MGMT_DBG("Adding REQUEST SENSE cmd %p to head of active "
                "cmd list", rs_cmd);
-       spin_lock_irq(&rs_cmd->cmd_lists->cmd_list_lock);
-       list_add(&rs_cmd->cmd_list_entry, &rs_cmd->cmd_lists->active_cmd_list);
-       wake_up(&rs_cmd->cmd_lists->cmd_list_waitQ);
-       spin_unlock_irq(&rs_cmd->cmd_lists->cmd_list_lock);
+       spin_lock_irq(&rs_cmd->cmd_threads->cmd_list_lock);
+       list_add(&rs_cmd->cmd_list_entry, &rs_cmd->cmd_threads->active_cmd_list);
+       wake_up(&rs_cmd->cmd_threads->cmd_list_waitQ);
+       spin_unlock_irq(&rs_cmd->cmd_threads->cmd_list_lock);
 
 out:
        TRACE_EXIT_RES(res);
@@ -2938,10 +3085,10 @@ static void scst_complete_request_sense(struct scst_cmd *req_cmd)
 
        TRACE_MGMT_DBG("Adding orig cmd %p to head of active "
                "cmd list", orig_cmd);
-       spin_lock_irq(&orig_cmd->cmd_lists->cmd_list_lock);
-       list_add(&orig_cmd->cmd_list_entry, &orig_cmd->cmd_lists->active_cmd_list);
-       wake_up(&orig_cmd->cmd_lists->cmd_list_waitQ);
-       spin_unlock_irq(&orig_cmd->cmd_lists->cmd_list_lock);
+       spin_lock_irq(&orig_cmd->cmd_threads->cmd_list_lock);
+       list_add(&orig_cmd->cmd_list_entry, &orig_cmd->cmd_threads->active_cmd_list);
+       wake_up(&orig_cmd->cmd_threads->cmd_list_waitQ);
+       spin_unlock_irq(&orig_cmd->cmd_threads->cmd_list_lock);
 
        TRACE_EXIT();
        return;
@@ -3293,7 +3440,7 @@ struct scst_cmd *scst_alloc_cmd(gfp_t gfp_mask)
        cmd->state = SCST_CMD_STATE_INIT_WAIT;
        cmd->start_time = jiffies;
        atomic_set(&cmd->cmd_ref, 1);
-       cmd->cmd_lists = &scst_main_cmd_lists;
+       cmd->cmd_threads = &scst_main_cmd_threads;
        INIT_LIST_HEAD(&cmd->mgmt_cmd_list);
        cmd->queue_type = SCST_CMD_QUEUE_SIMPLE;
        cmd->timeout = SCST_DEFAULT_TIMEOUT;
@@ -3446,11 +3593,11 @@ void scst_check_retries(struct scst_tgt *tgt)
                        TRACE_RETRY("Moving retry cmd %p to head of active "
                                "cmd list (retry_cmds left %d)",
                                c, tgt->retry_cmds);
-                       spin_lock(&c->cmd_lists->cmd_list_lock);
+                       spin_lock(&c->cmd_threads->cmd_list_lock);
                        list_move(&c->cmd_list_entry,
-                                 &c->cmd_lists->active_cmd_list);
-                       wake_up(&c->cmd_lists->cmd_list_waitQ);
-                       spin_unlock(&c->cmd_lists->cmd_list_lock);
+                                 &c->cmd_threads->active_cmd_list);
+                       wake_up(&c->cmd_threads->cmd_list_waitQ);
+                       spin_unlock(&c->cmd_threads->cmd_list_lock);
 
                        need_wake_up++;
                        if (need_wake_up >= 2) /* "slow start" */
@@ -5053,11 +5200,11 @@ void scst_process_reset(struct scst_device *dev,
                        list_del(&cmd->blocked_cmd_list_entry);
                        TRACE_MGMT_DBG("Adding aborted blocked cmd %p "
                                "to active cmd list", cmd);
-                       spin_lock_irq(&cmd->cmd_lists->cmd_list_lock);
+                       spin_lock_irq(&cmd->cmd_threads->cmd_list_lock);
                        list_add_tail(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
-                       wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-                       spin_unlock_irq(&cmd->cmd_lists->cmd_list_lock);
+                               &cmd->cmd_threads->active_cmd_list);
+                       wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+                       spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock);
                }
        }
 
@@ -5365,13 +5512,13 @@ restart:
                        if (res == NULL)
                                res = cmd;
                        else {
-                               spin_lock(&cmd->cmd_lists->cmd_list_lock);
+                               spin_lock(&cmd->cmd_threads->cmd_list_lock);
                                TRACE_SN("Adding cmd %p to active cmd list",
                                        cmd);
                                list_add_tail(&cmd->cmd_list_entry,
-                                       &cmd->cmd_lists->active_cmd_list);
-                               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-                               spin_unlock(&cmd->cmd_lists->cmd_list_lock);
+                                       &cmd->cmd_threads->active_cmd_list);
+                               wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+                               spin_unlock(&cmd->cmd_threads->cmd_list_lock);
                        }
                }
        }
@@ -5462,12 +5609,12 @@ void scst_dev_del_all_thr_data(struct scst_device *dev)
 }
 EXPORT_SYMBOL(scst_dev_del_all_thr_data);
 
-struct scst_thr_data_hdr *__scst_find_thr_data(struct scst_tgt_dev *tgt_dev,
-       struct task_struct *tsk)
+/* thr_data_lock supposed to be held */
+static struct scst_thr_data_hdr *__scst_find_thr_data_locked(
+       struct scst_tgt_dev *tgt_dev, struct task_struct *tsk)
 {
        struct scst_thr_data_hdr *res = NULL, *d;
 
-       spin_lock(&tgt_dev->thr_data_lock);
        list_for_each_entry(d, &tgt_dev->thr_data_list, thr_data_list_entry) {
                if (d->owner_thr == tsk) {
                        res = d;
@@ -5475,11 +5622,47 @@ struct scst_thr_data_hdr *__scst_find_thr_data(struct scst_tgt_dev *tgt_dev,
                        break;
                }
        }
+       return res;
+}
+
+struct scst_thr_data_hdr *__scst_find_thr_data(struct scst_tgt_dev *tgt_dev,
+       struct task_struct *tsk)
+{
+       struct scst_thr_data_hdr *res;
+
+       spin_lock(&tgt_dev->thr_data_lock);
+       res = __scst_find_thr_data_locked(tgt_dev, tsk);
        spin_unlock(&tgt_dev->thr_data_lock);
+
        return res;
 }
 EXPORT_SYMBOL(__scst_find_thr_data);
 
+bool scst_del_thr_data(struct scst_tgt_dev *tgt_dev, struct task_struct *tsk)
+{
+       bool res;
+       struct scst_thr_data_hdr *td;
+
+       spin_lock(&tgt_dev->thr_data_lock);
+
+       td = __scst_find_thr_data_locked(tgt_dev, tsk);
+       if (td != NULL) {
+               list_del(&td->thr_data_list_entry);
+               res = true;
+       } else
+               res = false;
+
+       spin_unlock(&tgt_dev->thr_data_lock);
+
+       if (td != NULL) {
+               /* the find() fn also gets it */
+               scst_thr_data_put(td);
+               scst_thr_data_put(td);
+       }
+
+       return res;
+}
+
 /* dev_lock supposed to be held and BH disabled */
 void __scst_block_dev(struct scst_device *dev)
 {
@@ -5623,15 +5806,15 @@ static void scst_unblock_cmds(struct scst_device *dev)
                                 blocked_cmd_list_entry) {
                list_del(&cmd->blocked_cmd_list_entry);
                TRACE_MGMT_DBG("Adding blocked cmd %p to active cmd list", cmd);
-               spin_lock(&cmd->cmd_lists->cmd_list_lock);
+               spin_lock(&cmd->cmd_threads->cmd_list_lock);
                if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))
                        list_add(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
+                               &cmd->cmd_threads->active_cmd_list);
                else
                        list_add_tail(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
-               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-               spin_unlock(&cmd->cmd_lists->cmd_list_lock);
+                               &cmd->cmd_threads->active_cmd_list);
+               wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+               spin_unlock(&cmd->cmd_threads->cmd_list_lock);
        }
        local_irq_restore(flags);
 
@@ -5948,10 +6131,9 @@ static struct scst_tgt_dev *tm_dbg_tgt_dev;
 
 static const int tm_dbg_on_state_num_passes[] = { 5, 1, 0x7ffffff };
 
-static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev,
-       struct scst_acg_dev *acg_dev)
+static void tm_dbg_init_tgt_dev(struct scst_tgt_dev *tgt_dev)
 {
-       if (acg_dev->lun == 6) {
+       if (tgt_dev->lun == 6) {
                unsigned long flags;
 
                if (tm_dbg_tgt_dev != NULL)
@@ -5990,7 +6172,7 @@ static void tm_dbg_timer_fn(unsigned long arg)
        tm_dbg_flags.tm_dbg_release = 1;
        /* Used to make sure that all woken up threads see the new value */
        smp_wmb();
-       wake_up_all(&tm_dbg_tgt_dev->dev->p_cmd_lists->cmd_list_waitQ);
+       wake_up_all(&tm_dbg_tgt_dev->active_cmd_threads->cmd_list_waitQ);
        return;
 }
 
@@ -6033,9 +6215,9 @@ static void tm_dbg_delay_cmd(struct scst_cmd *cmd)
                sBUG();
        }
        /* IRQs already off */
-       spin_lock(&cmd->cmd_lists->cmd_list_lock);
+       spin_lock(&cmd->cmd_threads->cmd_list_lock);
        list_add_tail(&cmd->cmd_list_entry, &tm_dbg_delayed_cmd_list);
-       spin_unlock(&cmd->cmd_lists->cmd_list_lock);
+       spin_unlock(&cmd->cmd_threads->cmd_list_lock);
        cmd->tm_dbg_delayed = 1;
        tm_dbg_delayed_cmds_count++;
        return;
@@ -6052,10 +6234,10 @@ void tm_dbg_check_released_cmds(void)
                        TRACE_MGMT_DBG("Releasing timed cmd %p (tag %llu), "
                                "delayed_cmds_count=%d", cmd, cmd->tag,
                                tm_dbg_delayed_cmds_count);
-                       spin_lock(&cmd->cmd_lists->cmd_list_lock);
+                       spin_lock(&cmd->cmd_threads->cmd_list_lock);
                        list_move(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
-                       spin_unlock(&cmd->cmd_lists->cmd_list_lock);
+                               &cmd->cmd_threads->active_cmd_list);
+                       spin_unlock(&cmd->cmd_threads->cmd_list_lock);
                }
                tm_dbg_flags.tm_dbg_release = 0;
                spin_unlock_irq(&scst_tm_dbg_lock);
@@ -6161,11 +6343,11 @@ void tm_dbg_release_cmd(struct scst_cmd *cmd)
                                }
                        }
 
-                       spin_lock(&cmd->cmd_lists->cmd_list_lock);
+                       spin_lock(&cmd->cmd_threads->cmd_list_lock);
                        list_move(&c->cmd_list_entry,
-                               &c->cmd_lists->active_cmd_list);
-                       wake_up(&c->cmd_lists->cmd_list_waitQ);
-                       spin_unlock(&cmd->cmd_lists->cmd_list_lock);
+                               &c->cmd_threads->active_cmd_list);
+                       wake_up(&c->cmd_threads->cmd_list_waitQ);
+                       spin_unlock(&cmd->cmd_threads->cmd_list_lock);
                        break;
                }
        }
@@ -6198,7 +6380,7 @@ void tm_dbg_task_mgmt(struct scst_device *dev, const char *fn, int force)
                 */
                smp_wmb();
                if (tm_dbg_tgt_dev != NULL)
-                       wake_up_all(&tm_dbg_tgt_dev->dev->p_cmd_lists->cmd_list_waitQ);
+                       wake_up_all(&tm_dbg_tgt_dev->active_cmd_threads->cmd_list_waitQ);
        } else {
                TRACE_MGMT_DBG("%s: while OFFLINE state, doing nothing", fn);
        }
index 5d5d688..b6a0a37 100644 (file)
 #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
 #endif /* !defined(SCSI_EXEC_REQ_FIFO_DEFINED) */
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
-#if !defined(SCST_IO_CONTEXT)
-#warning "Patch io_context-<kernel-version> was not applied\
- on your kernel. SCST will be working with not the best performance."
-#endif
-#else
-#warning "There is no patch io_context-<kernel-version>\
- for your kernel version. For performance reasons it is strongly recommended\
- to upgrade your kernel to version >= 2.6.26.x."
-#endif
-
 /**
  ** SCST global variables. They are all uninitialized to have their layout in
  ** memory be exactly as specified. Otherwise compiler puts zero-initialized
@@ -123,7 +112,7 @@ unsigned long scst_trace_flag;
 unsigned long scst_flags;
 atomic_t scst_cmd_count;
 
-struct scst_cmd_lists scst_main_cmd_lists;
+struct scst_cmd_threads scst_main_cmd_threads;
 
 struct scst_tasklet scst_tasklets[NR_CPUS];
 
@@ -141,12 +130,9 @@ wait_queue_head_t scst_dev_cmd_waitQ;
 
 static struct mutex scst_suspend_mutex;
 /* protected by scst_suspend_mutex */
-static struct list_head scst_cmd_lists_list;
+static struct list_head scst_cmd_threads_list;
 
-static int scst_threads;
-struct mutex scst_global_threads_mutex;
-u32 scst_nr_global_threads;
-static struct list_head scst_global_threads_list;
+int scst_threads;
 static struct task_struct *scst_init_cmd_thread;
 static struct task_struct *scst_mgmt_thread;
 static struct task_struct *scst_mgmt_cmd_thread;
@@ -685,7 +671,7 @@ EXPORT_SYMBOL(scst_suspend_activity);
 
 static void __scst_resume_activity(void)
 {
-       struct scst_cmd_lists *l;
+       struct scst_cmd_threads *l;
 
        TRACE_ENTRY();
 
@@ -701,7 +687,7 @@ static void __scst_resume_activity(void)
         */
        smp_mb__after_clear_bit();
 
-       list_for_each_entry(l, &scst_cmd_lists_list, lists_list_entry) {
+       list_for_each_entry(l, &scst_cmd_threads_list, lists_list_entry) {
                wake_up_all(&l->cmd_list_waitQ);
        }
        wake_up_all(&scst_init_cmd_list_waitQ);
@@ -1324,155 +1310,177 @@ void scst_unregister_virtual_dev_driver(struct scst_dev_type *dev_type)
 }
 EXPORT_SYMBOL(scst_unregister_virtual_dev_driver);
 
-/* Called under scst_mutex */
-int scst_add_dev_threads(struct scst_device *dev, int num)
+/* scst_mutex supposed to be held */
+int scst_add_threads(struct scst_cmd_threads *cmd_threads,
+       struct scst_device *dev, struct scst_tgt_dev *tgt_dev, int num)
 {
-       int i, res = 0;
-       int n = 0;
+       int res, i;
        struct scst_cmd_thread_t *thr;
-       char nm[12];
+       int n = 0, tgt_dev_num = 0;
 
        TRACE_ENTRY();
 
-       list_for_each_entry(thr, &dev->threads_list, thread_list_entry) {
+       list_for_each_entry(thr, &cmd_threads->threads_list, thread_list_entry) {
                n++;
        }
 
+       if (tgt_dev != NULL) {
+               struct scst_tgt_dev *t;
+               list_for_each_entry(t, &tgt_dev->dev->dev_tgt_dev_list,
+                               dev_tgt_dev_list_entry) {
+                       if (t == tgt_dev)
+                               break;
+                       tgt_dev_num++;
+               }
+       }
+
        for (i = 0; i < num; i++) {
+               struct scst_cmd_thread_t *thr;
+
                thr = kmalloc(sizeof(*thr), GFP_KERNEL);
                if (!thr) {
                        res = -ENOMEM;
-                       PRINT_ERROR("Failed to allocate thr %d", res);
-                       goto out_del;
+                       PRINT_ERROR("fail to allocate thr %d", res);
+                       goto out_error;
                }
-               strlcpy(nm, dev->handler->name, ARRAY_SIZE(nm));
 
-               thr->cmd_thread = kthread_create(scst_cmd_thread,
-                       &dev->cmd_lists, "%sd%d_%d", nm, dev->dev_num, n++);
+               if (dev != NULL) {
+                       char nm[14]; /* to limit the name's len */
+                       strlcpy(nm, dev->virt_name, ARRAY_SIZE(nm));
+                       thr->cmd_thread = kthread_create(scst_cmd_thread,
+                               cmd_threads, "%s%d", nm, n++);
+               } else if (tgt_dev != NULL) {
+                       char nm[11]; /* to limit the name's len */
+                       strlcpy(nm, tgt_dev->dev->virt_name, ARRAY_SIZE(nm));
+                       thr->cmd_thread = kthread_create(scst_cmd_thread,
+                               cmd_threads, "%s%d_%d", nm, tgt_dev_num, n++);
+               } else
+                       thr->cmd_thread = kthread_create(scst_cmd_thread,
+                               cmd_threads, "scsi_tgt%d", n++);
+
                if (IS_ERR(thr->cmd_thread)) {
                        res = PTR_ERR(thr->cmd_thread);
                        PRINT_ERROR("kthread_create() failed: %d", res);
                        kfree(thr);
-                       goto out_del;
+                       goto out_error;
                }
 
-               list_add(&thr->thread_list_entry, &dev->threads_list);
-
-               /*
-                * ToDo: better to use tgt_dev_io_context instead, but we
-                * are not ready for that yet.
-                */
-               __exit_io_context(thr->cmd_thread->io_context);
-               thr->cmd_thread->io_context = ioc_task_link(dev->dev_io_ctx);
-               TRACE_DBG("Setting dev io ctx %p on thr %d", dev->dev_io_ctx,
-                       thr->cmd_thread->pid);
+               list_add(&thr->thread_list_entry, &cmd_threads->threads_list);
+               cmd_threads->nr_threads++;
 
                wake_up_process(thr->cmd_thread);
        }
 
+       res = 0;
+
 out:
        TRACE_EXIT_RES(res);
        return res;
 
-out_del:
-       scst_del_dev_threads(dev, i);
+out_error:
+       scst_del_threads(cmd_threads, i);
        goto out;
 }
 
-/* Called under scst_mutex and suspended activity */
-static int scst_create_dev_threads(struct scst_device *dev)
-{
-       int res = 0;
-       int threads_num;
-
-       TRACE_ENTRY();
-
-       if (dev->handler->threads_num <= 0)
-               goto out;
-
-       threads_num = dev->handler->threads_num;
-
-       spin_lock_init(&dev->cmd_lists.cmd_list_lock);
-       INIT_LIST_HEAD(&dev->cmd_lists.active_cmd_list);
-       init_waitqueue_head(&dev->cmd_lists.cmd_list_waitQ);
-
-       res = scst_add_dev_threads(dev, threads_num);
-       if (res != 0)
-               goto out;
-
-       mutex_lock(&scst_suspend_mutex);
-       list_add_tail(&dev->cmd_lists.lists_list_entry,
-               &scst_cmd_lists_list);
-       mutex_unlock(&scst_suspend_mutex);
-
-       dev->p_cmd_lists = &dev->cmd_lists;
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-/* Called under scst_mutex */
-void scst_del_dev_threads(struct scst_device *dev, int num)
+/* scst_mutex supposed to be held */
+void scst_del_threads(struct scst_cmd_threads *cmd_threads, int num)
 {
        struct scst_cmd_thread_t *ct, *tmp;
-       int i = 0;
 
        TRACE_ENTRY();
 
        if (num == 0)
                goto out;
 
-       list_for_each_entry_safe_reverse(ct, tmp, &dev->threads_list,
+       list_for_each_entry_safe_reverse(ct, tmp, &cmd_threads->threads_list,
                                thread_list_entry) {
                int rc;
-               struct scst_tgt_dev *tgt_dev;
-
-               list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
-                               dev_tgt_dev_list_entry) {
-                       struct scst_thr_data_hdr *td;
-                       td = __scst_find_thr_data(tgt_dev, ct->cmd_thread);
-                       if (td != NULL) {
-                               scst_thr_data_put(td);
-                               break;
-                       }
-               }
+               struct scst_device *dev;
 
                rc = kthread_stop(ct->cmd_thread);
                if (rc < 0)
                        TRACE_MGMT_DBG("kthread_stop() failed: %d", rc);
 
                list_del(&ct->thread_list_entry);
+
+               list_for_each_entry(dev, &scst_dev_list, dev_list_entry) {
+                       struct scst_tgt_dev *tgt_dev;
+                       list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
+                                       dev_tgt_dev_list_entry) {
+                               if (scst_del_thr_data(tgt_dev, ct->cmd_thread))
+                                       break;
+                       }
+               }
+
                kfree(ct);
 
-               if ((num > 0) && (++i >= num))
+               cmd_threads->nr_threads--;
+
+               --num;
+               if (num == 0)
                        break;
        }
 
+       if (cmd_threads->nr_threads == 0) {
+               put_io_context(cmd_threads->io_context);
+               cmd_threads->io_context = NULL;
+       }
+
 out:
        TRACE_EXIT();
        return;
 }
 
-/* Called under scst_mutex and suspended activity */
-static void scst_stop_dev_threads(struct scst_device *dev)
+/* The activity supposed to be suspended and scst_mutex held */
+void scst_stop_dev_threads(struct scst_device *dev)
 {
+       struct scst_tgt_dev *tgt_dev;
+
        TRACE_ENTRY();
 
-       if (list_empty(&dev->threads_list))
-               goto out;
+       list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
+                               dev_tgt_dev_list_entry) {
+               scst_tgt_dev_stop_threads(tgt_dev);
+       }
 
-       scst_del_dev_threads(dev, -1);
+       if ((dev->threads_num > 0) &&
+           (dev->threads_pool_type == SCST_THREADS_POOL_SHARED))
+               scst_del_threads(&dev->dev_cmd_threads, -1);
 
-       if (dev->p_cmd_lists == &dev->cmd_lists) {
-               mutex_lock(&scst_suspend_mutex);
-               list_del(&dev->cmd_lists.lists_list_entry);
-               mutex_unlock(&scst_suspend_mutex);
+       TRACE_EXIT();
+       return;
+}
+
+/* The activity supposed to be suspended and scst_mutex held */
+int scst_create_dev_threads(struct scst_device *dev)
+{
+       int res = 0;
+       struct scst_tgt_dev *tgt_dev;
+
+       TRACE_ENTRY();
+
+       list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
+                       dev_tgt_dev_list_entry) {
+               res = scst_tgt_dev_setup_threads(tgt_dev);
+               if (res != 0)
+                       goto out_err;
+       }
+
+       if ((dev->threads_num > 0) &&
+           (dev->threads_pool_type == SCST_THREADS_POOL_SHARED)) {
+               res = scst_add_threads(&dev->dev_cmd_threads, dev, NULL,
+                       dev->threads_num);
+               if (res != 0)
+                       goto out_err;
        }
 
 out:
-       TRACE_EXIT();
-       return;
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_err:
+       scst_stop_dev_threads(dev);
+       goto out;
 }
 
 /* The activity supposed to be suspended and scst_mutex held */
@@ -1519,14 +1527,13 @@ assign:
        if (handler == NULL)
                goto out;
 
+       dev->threads_num = handler->threads_num;
+       dev->threads_pool_type = handler->threads_pool_type;
+
        res = scst_create_devt_dev_sysfs(dev);
        if (res != 0)
                goto out_null;
 
-       res = scst_create_dev_threads(dev);
-       if (res != 0)
-               goto out_remove_sysfs;
-
        if (handler->attach) {
                TRACE_DBG("Calling new dev handler's attach(%p)", dev);
                res = handler->attach(dev);
@@ -1534,7 +1541,7 @@ assign:
                if (res != 0) {
                        PRINT_ERROR("New device handler's %s attach() "
                                "failed: %d", handler->name, res);
-                       goto out_thr_null;
+                       goto out_remove_sysfs;
                }
        }
 
@@ -1555,6 +1562,10 @@ assign:
                }
        }
 
+       res = scst_create_dev_threads(dev);
+       if (res != 0)
+               goto out_err_detach_tgt;
+
 out:
        TRACE_EXIT_RES(res);
        return res;
@@ -1575,9 +1586,6 @@ out_err_detach_tgt:
                TRACE_DBG("%s", "Handler's detach() returned");
        }
 
-out_thr_null:
-       scst_stop_dev_threads(dev);
-
 out_remove_sysfs:
        scst_devt_dev_sysfs_put(dev);
 
@@ -1586,139 +1594,56 @@ out_null:
        goto out;
 }
 
-int scst_global_threads_count(void)
-{
-       int i;
-
-       /*
-        * Just to lower the race window, when user can get just changed value
-        */
-       mutex_lock(&scst_global_threads_mutex);
-       i = scst_nr_global_threads;
-       mutex_unlock(&scst_global_threads_mutex);
-       return i;
-}
-
-static void scst_threads_info_init(void)
-{
-       mutex_init(&scst_global_threads_mutex);
-       INIT_LIST_HEAD(&scst_global_threads_list);
-}
-
-/* scst_global_threads_mutex supposed to be held */
-void __scst_del_global_threads(int num)
+void scst_init_threads(struct scst_cmd_threads *cmd_threads)
 {
-       struct scst_cmd_thread_t *ct, *tmp;
-
        TRACE_ENTRY();
 
-       if (num == 0)
-               goto out;
-
-       list_for_each_entry_safe(ct, tmp, &scst_global_threads_list,
-                               thread_list_entry) {
-               int res;
+       spin_lock_init(&cmd_threads->cmd_list_lock);
+       INIT_LIST_HEAD(&cmd_threads->active_cmd_list);
+       init_waitqueue_head(&cmd_threads->cmd_list_waitQ);
+       INIT_LIST_HEAD(&cmd_threads->threads_list);
 
-               res = kthread_stop(ct->cmd_thread);
-               if (res < 0)
-                       TRACE_MGMT_DBG("kthread_stop() failed: %d", res);
-               list_del(&ct->thread_list_entry);
-               kfree(ct);
-               scst_nr_global_threads--;
-               --num;
-               if (num == 0)
-                       break;
-       }
+       mutex_lock(&scst_suspend_mutex);
+       list_add_tail(&cmd_threads->lists_list_entry,
+               &scst_cmd_threads_list);
+       mutex_unlock(&scst_suspend_mutex);
 
-out:
        TRACE_EXIT();
        return;
 }
+EXPORT_SYMBOL(scst_init_threads);
 
-/* scst_global_threads_mutex supposed to be held */
-int __scst_add_global_threads(int num)
+void scst_deinit_threads(struct scst_cmd_threads *cmd_threads)
 {
-       int res = 0, i;
-       static int scst_thread_num;
-
        TRACE_ENTRY();
 
-       for (i = 0; i < num; i++) {
-               struct scst_cmd_thread_t *thr;
-
-               thr = kmalloc(sizeof(*thr), GFP_KERNEL);
-               if (!thr) {
-                       res = -ENOMEM;
-                       PRINT_ERROR("fail to allocate thr %d", res);
-                       goto out_error;
-               }
-               thr->cmd_thread = kthread_create(scst_cmd_thread,
-                       &scst_main_cmd_lists, "scsi_tgt%d",
-                       scst_thread_num++);
-               if (IS_ERR(thr->cmd_thread)) {
-                       res = PTR_ERR(thr->cmd_thread);
-                       PRINT_ERROR("kthread_create() failed: %d", res);
-                       kfree(thr);
-                       goto out_error;
-               }
-
-               list_add(&thr->thread_list_entry, &scst_global_threads_list);
-               scst_nr_global_threads++;
-
-               wake_up_process(thr->cmd_thread);
-       }
-       res = 0;
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-
-out_error:
-       __scst_del_global_threads(i);
-       goto out;
-}
-
-int scst_add_global_threads(int num)
-{
-       int res;
-
-       TRACE_ENTRY();
-
-       mutex_lock(&scst_global_threads_mutex);
-       res = __scst_add_global_threads(num);
-       mutex_unlock(&scst_global_threads_mutex);
-
-       TRACE_EXIT_RES(res);
-       return res;
-}
-EXPORT_SYMBOL(scst_add_global_threads);
-
-void scst_del_global_threads(int num)
-{
-       TRACE_ENTRY();
+       mutex_lock(&scst_suspend_mutex);
+       list_del(&cmd_threads->lists_list_entry);
+       mutex_unlock(&scst_suspend_mutex);
 
-       mutex_lock(&scst_global_threads_mutex);
-       __scst_del_global_threads(num);
-       mutex_unlock(&scst_global_threads_mutex);
+       sBUG_ON(cmd_threads->io_context);
 
        TRACE_EXIT();
        return;
 }
-EXPORT_SYMBOL(scst_del_global_threads);
+EXPORT_SYMBOL(scst_deinit_threads);
 
 static void scst_stop_all_threads(void)
 {
        TRACE_ENTRY();
 
-       mutex_lock(&scst_global_threads_mutex);
-       __scst_del_global_threads(-1);
+       mutex_lock(&scst_mutex);
+
+       scst_del_threads(&scst_main_cmd_threads, -1);
+
        if (scst_mgmt_cmd_thread)
                kthread_stop(scst_mgmt_cmd_thread);
        if (scst_mgmt_thread)
                kthread_stop(scst_mgmt_thread);
        if (scst_init_cmd_thread)
                kthread_stop(scst_init_cmd_thread);
-       mutex_unlock(&scst_global_threads_mutex);
+
+       mutex_unlock(&scst_mutex);
 
        TRACE_EXIT();
        return;
@@ -1730,10 +1655,11 @@ static int scst_start_all_threads(int num)
 
        TRACE_ENTRY();
 
-       mutex_lock(&scst_global_threads_mutex);
-       res = __scst_add_global_threads(num);
+       mutex_lock(&scst_mutex);
+
+       res = scst_add_threads(&scst_main_cmd_threads, NULL, NULL, num);
        if (res < 0)
-               goto out;
+               goto out_unlock;
 
        scst_init_cmd_thread = kthread_run(scst_init_thread,
                NULL, "scsi_tgt_init");
@@ -1741,7 +1667,7 @@ static int scst_start_all_threads(int num)
                res = PTR_ERR(scst_init_cmd_thread);
                PRINT_ERROR("kthread_create() for init cmd failed: %d", res);
                scst_init_cmd_thread = NULL;
-               goto out;
+               goto out_unlock;
        }
 
        scst_mgmt_cmd_thread = kthread_run(scst_tm_thread,
@@ -1750,7 +1676,7 @@ static int scst_start_all_threads(int num)
                res = PTR_ERR(scst_mgmt_cmd_thread);
                PRINT_ERROR("kthread_create() for TM failed: %d", res);
                scst_mgmt_cmd_thread = NULL;
-               goto out;
+               goto out_unlock;
        }
 
        scst_mgmt_thread = kthread_run(scst_global_mgmt_thread,
@@ -1759,11 +1685,12 @@ static int scst_start_all_threads(int num)
                res = PTR_ERR(scst_mgmt_thread);
                PRINT_ERROR("kthread_create() for mgmt failed: %d", res);
                scst_mgmt_thread = NULL;
-               goto out;
+               goto out_unlock;
        }
 
-out:
-       mutex_unlock(&scst_global_threads_mutex);
+out_unlock:
+       mutex_unlock(&scst_mutex);
+
        TRACE_EXIT_RES(res);
        return res;
 }
@@ -1940,18 +1867,6 @@ static int __init init_scst(void)
                BUILD_BUG_ON(sizeof(c->sn) != sizeof(t->expected_sn));
        }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
-#if !defined(SCST_IO_CONTEXT)
-       PRINT_WARNING("%s", "Patch io_context was not applied on "
-               "your kernel. SCST will be working with not the best "
-               "performance.");
-#endif
-#else
-       PRINT_WARNING("%s", "There is no patch io_context for your kernel "
-               "version. For performance reasons it is strongly recommended "
-               "to upgrade your kernel to version >= 2.6.27.x.");
-#endif
-
        mutex_init(&scst_mutex);
        INIT_LIST_HEAD(&scst_template_list);
        INIT_LIST_HEAD(&scst_dev_list);
@@ -1977,13 +1892,10 @@ static int __init init_scst(void)
        INIT_LIST_HEAD(&scst_sess_shut_list);
        init_waitqueue_head(&scst_dev_cmd_waitQ);
        mutex_init(&scst_suspend_mutex);
-       INIT_LIST_HEAD(&scst_cmd_lists_list);
+       INIT_LIST_HEAD(&scst_cmd_threads_list);
        scst_virt_dev_last_id = 1;
-       spin_lock_init(&scst_main_cmd_lists.cmd_list_lock);
-       INIT_LIST_HEAD(&scst_main_cmd_lists.active_cmd_list);
-       init_waitqueue_head(&scst_main_cmd_lists.cmd_list_waitQ);
-       list_add_tail(&scst_main_cmd_lists.lists_list_entry,
-               &scst_cmd_lists_list);
+
+       scst_init_threads(&scst_main_cmd_threads);
 
        res = scst_lib_init();
        if (res != 0)
@@ -2001,8 +1913,6 @@ static int __init init_scst(void)
                scst_threads = scst_num_cpus;
        }
 
-       scst_threads_info_init();
-
 #define INIT_CACHEP(p, s, o) do {                                      \
                p = KMEM_CACHE(s, SCST_SLAB_FLAGS);                     \
                TRACE_MEM("Slab create: %s at %p size %zd", #s, p,      \
@@ -2225,6 +2135,8 @@ static void __exit exit_scst(void)
 
        scst_stop_all_threads();
 
+       scst_deinit_threads(&scst_main_cmd_threads);
+
        scsi_unregister_interface(&scst_interface);
 #ifdef CONFIG_SCST_PROC
        scst_destroy_acg(scst_default_acg);
index 8e9a90e..91b8f28 100644 (file)
@@ -129,6 +129,8 @@ extern unsigned long scst_trace_flag;
 #define SCST_LUN_ADDR_METHOD_PERIPHERAL        0
 #define SCST_LUN_ADDR_METHOD_FLAT      1
 
+extern int scst_threads;
+
 extern unsigned int scst_max_dev_cmd_mem;
 
 extern mempool_t *scst_mgmt_mempool;
@@ -165,7 +167,7 @@ extern struct list_head scst_init_cmd_list;
 extern wait_queue_head_t scst_init_cmd_list_waitQ;
 extern unsigned int scst_init_poll_cnt;
 
-extern struct scst_cmd_lists scst_main_cmd_lists;
+extern struct scst_cmd_threads scst_main_cmd_threads;
 
 extern spinlock_t scst_mcmd_lock;
 /* The following lists protected by scst_mcmd_lock */
@@ -190,22 +192,22 @@ struct scst_cmd_thread_t {
        struct list_head thread_list_entry;
 };
 
-#if defined(SCST_IO_CONTEXT)
-
 static inline bool scst_set_io_context(struct scst_cmd *cmd,
        struct io_context **old)
 {
        bool res;
 
-       if (cmd->cmd_lists == &scst_main_cmd_lists) {
+       if (cmd->cmd_threads == &scst_main_cmd_threads) {
                EXTRACHECKS_BUG_ON(in_interrupt());
                /*
                 * No need to call ioc_task_link(), because io_context
                 * supposed to be cleared in the end of the caller function.
                 */
-               current->io_context = cmd->tgt_dev->tgt_dev_io_ctx;
+               current->io_context = cmd->tgt_dev->tgt_dev_cmd_threads.io_context;
                res = true;
-               TRACE_DBG("io_context %p", cmd->tgt_dev->tgt_dev_io_ctx);
+               TRACE_DBG("io_context %p (cmd_threads %p)", current->io_context,
+                       &cmd->tgt_dev->tgt_dev_cmd_threads);
+               EXTRACHECKS_BUG_ON(current->io_context == NULL);
        } else
                res = false;
 
@@ -220,31 +222,18 @@ static inline void scst_reset_io_context(struct scst_tgt_dev *tgt_dev,
        return;
 }
 
-#else
-
-static inline bool scst_set_io_context(struct scst_cmd *cmd,
-       struct io_context **old)
-{
-       return false;
-}
-static inline void scst_reset_io_context(struct scst_tgt_dev *tgt_dev,
-       struct io_context *old) {}
-static inline void __exit_io_context(struct io_context *ioc) {}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-static inline struct io_context *ioc_task_link(struct io_context *ioc)
-{
-       return NULL;
-}
-#endif
+extern int scst_add_threads(struct scst_cmd_threads *cmd_threads,
+       struct scst_device *dev, struct scst_tgt_dev *tgt_dev, int num);
+extern void scst_del_threads(struct scst_cmd_threads *cmd_threads, int num);
 
-#endif
+extern int scst_create_dev_threads(struct scst_device *dev);
+extern void scst_stop_dev_threads(struct scst_device *dev);
 
-extern struct mutex scst_global_threads_mutex;
-extern u32 scst_nr_global_threads;
+extern int scst_tgt_dev_setup_threads(struct scst_tgt_dev *tgt_dev);
+extern void scst_tgt_dev_stop_threads(struct scst_tgt_dev *tgt_dev);
 
-extern int scst_global_threads_count(void);
-extern int __scst_add_global_threads(int num);
-extern void __scst_del_global_threads(int num);
+extern bool scst_del_thr_data(struct scst_tgt_dev *tgt_dev,
+       struct task_struct *tsk);
 
 extern struct scst_dev_type scst_null_devtype;
 
@@ -269,11 +258,11 @@ static inline void scst_make_deferred_commands_active(
        c = __scst_check_deferred_commands(tgt_dev);
        if (c != NULL) {
                TRACE_SN("Adding cmd %p to active cmd list", c);
-               spin_lock_irq(&c->cmd_lists->cmd_list_lock);
+               spin_lock_irq(&c->cmd_threads->cmd_list_lock);
                list_add_tail(&c->cmd_list_entry,
-                       &c->cmd_lists->active_cmd_list);
-               wake_up(&c->cmd_lists->cmd_list_waitQ);
-               spin_unlock_irq(&c->cmd_lists->cmd_list_lock);
+                       &c->cmd_threads->active_cmd_list);
+               wake_up(&c->cmd_threads->cmd_list_waitQ);
+               spin_unlock_irq(&c->cmd_threads->cmd_list_lock);
        }
 
        return;
@@ -294,9 +283,6 @@ int scst_init_thread(void *arg);
 int scst_tm_thread(void *arg);
 int scst_global_mgmt_thread(void *arg);
 
-int scst_add_dev_threads(struct scst_device *dev, int num);
-void scst_del_dev_threads(struct scst_device *dev, int num);
-
 int scst_queue_retry_cmd(struct scst_cmd *cmd, int finished_cmds);
 
 static inline void scst_tgtt_cleanup(struct scst_tgt_template *tgtt) { }
index 7191427..880a9db 100644 (file)
@@ -1223,9 +1223,9 @@ static ssize_t scst_proc_threads_write(struct file *file,
                goto out_free;
        }
 
-       mutex_lock(&scst_global_threads_mutex);
+       mutex_lock(&scst_mutex);
 
-       oldtn = scst_nr_global_threads;
+       oldtn = scst_main_cmd_threads.nr_threads;
        newtn = simple_strtoul(buffer, NULL, 0);
        if (newtn <= 0) {
                PRINT_ERROR("Illegal threads num value %d", newtn);
@@ -1234,14 +1234,18 @@ static ssize_t scst_proc_threads_write(struct file *file,
        }
        delta = newtn - oldtn;
        if (delta < 0)
-               __scst_del_global_threads(-delta);
-       else
-               __scst_add_global_threads(delta);
+               scst_del_threads(&scst_main_cmd_threads, -delta);
+       else {
+               int rc = scst_add_threads(&scst_main_cmd_threads, NULL, NULL,
+                                       delta);
+               if (rc != 0)
+                       res = rc;
+       }
 
        PRINT_INFO("Changed cmd threads num: old %d, new %d", oldtn, newtn);
 
 out_up_thr_free:
-       mutex_unlock(&scst_global_threads_mutex);
+       mutex_unlock(&scst_mutex);
 
        mutex_unlock(&scst_proc_mutex);
 
@@ -2648,7 +2652,7 @@ static int scst_threads_info_show(struct seq_file *seq, void *v)
 {
        TRACE_ENTRY();
 
-       seq_printf(seq, "%d\n", scst_global_threads_count());
+       seq_printf(seq, "%d\n", scst_main_cmd_threads.nr_threads);
 
        TRACE_EXIT();
        return 0;
index 3872e6f..930f861 100644 (file)
@@ -112,6 +112,12 @@ static ssize_t scst_tgt_addr_method_show(struct kobject *kobj,
 static ssize_t scst_tgt_addr_method_store(struct kobject *kobj,
                                    struct kobj_attribute *attr,
                                    const char *buf, size_t count);
+static ssize_t scst_tgt_mpio_type_show(struct kobject *kobj,
+                                  struct kobj_attribute *attr,
+                                  char *buf);
+static ssize_t scst_tgt_mpio_type_store(struct kobject *kobj,
+                                   struct kobj_attribute *attr,
+                                   const char *buf, size_t count);
 static ssize_t scst_ini_group_mgmt_show(struct kobject *kobj,
                                   struct kobj_attribute *attr,
                                   char *buf);
@@ -139,6 +145,12 @@ static ssize_t scst_acg_addr_method_show(struct kobject *kobj,
 static ssize_t scst_acg_addr_method_store(struct kobject *kobj,
                                    struct kobj_attribute *attr,
                                    const char *buf, size_t count);
+static ssize_t scst_acg_mpio_type_show(struct kobject *kobj,
+                                  struct kobj_attribute *attr,
+                                  char *buf);
+static ssize_t scst_acg_mpio_type_store(struct kobject *kobj,
+                                   struct kobj_attribute *attr,
+                                   const char *buf, size_t count);
 static ssize_t scst_acn_file_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf);
 
@@ -529,6 +541,10 @@ static struct kobj_attribute scst_tgt_addr_method =
        __ATTR(addr_method, S_IRUGO | S_IWUSR, scst_tgt_addr_method_show,
               scst_tgt_addr_method_store);
 
+static struct kobj_attribute scst_tgt_mpio_type =
+       __ATTR(mpio_type, S_IRUGO | S_IWUSR, scst_tgt_mpio_type_show,
+              scst_tgt_mpio_type_store);
+
 static struct kobj_attribute scst_rel_tgt_id =
        __ATTR(rel_tgt_id, S_IRUGO | S_IWUSR, scst_rel_tgt_id_show,
               scst_rel_tgt_id_store);
@@ -537,6 +553,10 @@ static struct kobj_attribute scst_acg_addr_method =
        __ATTR(addr_method, S_IRUGO | S_IWUSR, scst_acg_addr_method_show,
                scst_acg_addr_method_store);
 
+static struct kobj_attribute scst_acg_mpio_type =
+       __ATTR(mpio_type, S_IRUGO | S_IWUSR, scst_acg_mpio_type_show,
+               scst_acg_mpio_type_store);
+
 static ssize_t scst_tgt_enable_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf)
 {
@@ -700,6 +720,14 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt)
                goto out;
        }
 
+       retval = sysfs_create_file(&tgt->tgt_kobj,
+                       &scst_tgt_mpio_type.attr);
+       if (retval != 0) {
+               PRINT_ERROR("Can't add attribute %s for tgt %s",
+                       scst_tgt_mpio_type.attr.name, tgt->tgt_name);
+               goto out;
+       }
+
        pattr = tgt->tgtt->tgt_attrs;
        if (pattr != NULL) {
                while (*pattr != NULL) {
@@ -787,8 +815,189 @@ ssize_t scst_device_sysfs_type_show(struct kobject *kobj,
 static struct kobj_attribute device_type_attr =
        __ATTR(type, S_IRUGO, scst_device_sysfs_type_show, NULL);
 
+static ssize_t scst_device_sysfs_threads_num_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       int pos = 0;
+       struct scst_device *dev;
+
+       TRACE_ENTRY();
+
+       dev = container_of(kobj, struct scst_device, dev_kobj);
+
+       pos = sprintf(buf, "%d\n%s", dev->threads_num,
+               (dev->threads_num != dev->handler->threads_num) ?
+                       SCST_SYSFS_KEY_MARK "\n" : "");
+
+       TRACE_EXIT_RES(pos);
+       return pos;
+}
+
+static ssize_t scst_device_sysfs_threads_data_store(struct scst_device *dev,
+       int threads_num, enum scst_dev_type_threads_pool_type threads_pool_type)
+{
+       int res = 0;
+
+       TRACE_ENTRY();
+
+       if ((threads_num == dev->threads_num) &&
+           (threads_pool_type == dev->threads_pool_type))
+               goto out;
+
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out;
+
+       if (mutex_lock_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out_resume;
+       }
+
+       scst_stop_dev_threads(dev);
+
+       dev->threads_num = threads_num;
+       dev->threads_pool_type = threads_pool_type;
+
+       res = scst_create_dev_threads(dev);
+       if (res != 0)
+               goto out_up;
+
+out_up:
+       mutex_unlock(&scst_mutex);
+
+out_resume:
+       scst_resume_activity();
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static ssize_t scst_device_sysfs_threads_num_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int res;
+       struct scst_device *dev;
+       long newtn;
+
+       TRACE_ENTRY();
+
+       dev = container_of(kobj, struct scst_device, dev_kobj);
+
+       res = strict_strtoul(buf, 0, &newtn);
+       if (res != 0) {
+               PRINT_ERROR("strict_strtoul() for %s failed: %d ", buf, res);
+               goto out;
+       }
+
+       if (newtn <= 0) {
+               PRINT_ERROR("Illegal threads num value %ld", newtn);
+               res = -EINVAL;
+               goto out;
+       }
+
+       res = scst_device_sysfs_threads_data_store(dev, newtn,
+               dev->threads_pool_type);
+       if (res != 0)
+               goto out;
+
+       PRINT_INFO("Changed cmd threads num to %ld", newtn);
+
+       res = count;
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+static struct kobj_attribute device_threads_num_attr =
+       __ATTR(threads_num, S_IRUGO | S_IWUSR,
+               scst_device_sysfs_threads_num_show,
+               scst_device_sysfs_threads_num_store);
+
+static ssize_t scst_device_sysfs_threads_pool_type_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       int pos = 0;
+       struct scst_device *dev;
+
+       TRACE_ENTRY();
+
+       dev = container_of(kobj, struct scst_device, dev_kobj);
+
+       if (dev->threads_num == 0) {
+               pos = sprintf(buf, "Async\n");
+               goto out;
+       } else if (dev->threads_num < 0) {
+               pos = sprintf(buf, "Not valid\n");
+               goto out;
+       }
+
+       switch (dev->threads_pool_type) {
+       case SCST_THREADS_POOL_PER_INITIATOR:
+               pos = sprintf(buf, "%s\n%s", SCST_THREADS_POOL_PER_INITIATOR_STR,
+                       (dev->threads_pool_type != dev->handler->threads_pool_type) ?
+                               SCST_SYSFS_KEY_MARK "\n" : "");
+               break;
+       case SCST_THREADS_POOL_SHARED:
+               pos = sprintf(buf, "%s\n%s", SCST_THREADS_POOL_SHARED_STR,
+                       (dev->threads_pool_type != dev->handler->threads_pool_type) ?
+                               SCST_SYSFS_KEY_MARK "\n" : "");
+               break;
+       default:
+               pos = sprintf(buf, "Unknown\n");
+               break;
+       }
+
+out:
+       TRACE_EXIT_RES(pos);
+       return pos;
+}
+
+static ssize_t scst_device_sysfs_threads_pool_type_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int res;
+       struct scst_device *dev;
+       enum scst_dev_type_threads_pool_type newtpt;
+
+       TRACE_ENTRY();
+
+       dev = container_of(kobj, struct scst_device, dev_kobj);
+
+       newtpt = scst_parse_threads_pool_type(buf, count);
+       if (newtpt == SCST_THREADS_POOL_TYPE_INVALID) {
+               PRINT_ERROR("Illegal threads pool type %s", buf);
+               res = -EINVAL;
+               goto out;
+       }
+
+       TRACE_DBG("buf %s, count %d, newtpt %d", buf, count, newtpt);
+
+       res = scst_device_sysfs_threads_data_store(dev, dev->threads_num,
+               newtpt);
+       if (res != 0)
+               goto out;
+
+       PRINT_INFO("Changed cmd threads pool type to %d", newtpt);
+
+       res = count;
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
+
+static struct kobj_attribute device_threads_pool_type_attr =
+       __ATTR(threads_pool_type, S_IRUGO | S_IWUSR,
+               scst_device_sysfs_threads_pool_type_show,
+               scst_device_sysfs_threads_pool_type_store);
+
 static struct attribute *scst_device_attrs[] = {
        &device_type_attr.attr,
+       &device_threads_num_attr.attr,
+       &device_threads_pool_type_attr.attr,
        NULL,
 };
 
@@ -1750,6 +1959,109 @@ static ssize_t scst_tgt_addr_method_store(struct kobject *kobj,
        return res;
 }
 
+static ssize_t __scst_acg_mpio_type_show(struct scst_acg *acg, char *buf)
+{
+       int res;
+
+       switch (acg->acg_mpio_type) {
+       case SCST_ACG_MPIO_AUTO:
+               res = sprintf(buf, "auto\n");
+               break;
+       case SCST_ACG_MPIO_ENABLE:
+               res = sprintf(buf, "enable\n%s\n", SCST_SYSFS_KEY_MARK);
+               break;
+       case SCST_ACG_MPIO_DISABLE:
+               res = sprintf(buf, "disable\n%s\n", SCST_SYSFS_KEY_MARK);
+               break;
+       default:
+               res = sprintf(buf, "Unknown\n");
+               break;
+       }
+
+       return res;
+}
+
+static ssize_t __scst_acg_mpio_type_store(struct scst_acg *acg,
+       const char *buf, size_t count)
+{
+       int res = count;
+       enum scst_acg_mpio prev = acg->acg_mpio_type;
+       struct scst_acg_dev *acg_dev;
+
+       if (strncasecmp(buf, SCST_ACG_MPIO_AUTO_STR,
+                       min_t(int, strlen(SCST_ACG_MPIO_AUTO_STR), count)) == 0)
+               acg->acg_mpio_type = SCST_ACG_MPIO_AUTO;
+       else if (strncasecmp(buf, SCST_ACG_MPIO_ENABLE_STR,
+                       min_t(int, strlen(SCST_ACG_MPIO_ENABLE_STR), count)) == 0)
+               acg->acg_mpio_type = SCST_ACG_MPIO_ENABLE;
+       else if (strncasecmp(buf, SCST_ACG_MPIO_DISABLE_STR,
+                       min_t(int, strlen(SCST_ACG_MPIO_DISABLE_STR), count)) == 0)
+               acg->acg_mpio_type = SCST_ACG_MPIO_DISABLE;
+       else {
+               PRINT_ERROR("Unknown MPIO type %s", buf);
+               res = -EINVAL;
+               goto out;
+       }
+
+       if (prev == acg->acg_mpio_type)
+               goto out;
+
+       res = scst_suspend_activity(true);
+       if (res != 0)
+               goto out;
+
+       if (mutex_lock_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out_resume;
+       }
+
+       list_for_each_entry(acg_dev, &acg->acg_dev_list, acg_dev_list_entry) {
+               int rc;
+
+               scst_stop_dev_threads(acg_dev->dev);
+
+               rc = scst_create_dev_threads(acg_dev->dev);
+               if (rc != 0)
+                       res = rc;
+       }
+
+       mutex_unlock(&scst_mutex);
+
+out_resume:
+       scst_resume_activity();
+
+out:
+       return res;
+}
+
+static ssize_t scst_tgt_mpio_type_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       struct scst_acg *acg;
+       struct scst_tgt *tgt;
+
+       tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
+       acg = tgt->default_acg;
+
+       return __scst_acg_mpio_type_show(acg, buf);
+}
+
+static ssize_t scst_tgt_mpio_type_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int res;
+       struct scst_acg *acg;
+       struct scst_tgt *tgt;
+
+       tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
+       acg = tgt->default_acg;
+
+       res = __scst_acg_mpio_type_store(acg, buf, count);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
 static int scst_create_acg_sysfs(struct scst_tgt *tgt,
        struct scst_acg *acg)
 {
@@ -1801,10 +2113,17 @@ static int scst_create_acg_sysfs(struct scst_tgt *tgt,
        retval = sysfs_create_file(&acg->acg_kobj, &scst_acg_addr_method.attr);
        if (retval != 0) {
                PRINT_ERROR("Can't add tgt attr %s for tgt %s",
-                       scst_acg_addr_method.attr.name,
-                               tgt->tgt_name);
+                       scst_acg_addr_method.attr.name, tgt->tgt_name);
                goto out;
        }
+
+       retval = sysfs_create_file(&acg->acg_kobj, &scst_acg_mpio_type.attr);
+       if (retval != 0) {
+               PRINT_ERROR("Can't add tgt attr %s for tgt %s",
+                       scst_acg_mpio_type.attr.name, tgt->tgt_name);
+               goto out;
+       }
+
 out:
        TRACE_EXIT_RES(retval);
        return retval;
@@ -1856,6 +2175,30 @@ static ssize_t scst_acg_addr_method_store(struct kobject *kobj,
        return res;
 }
 
+static ssize_t scst_acg_mpio_type_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       struct scst_acg *acg;
+
+       acg = container_of(kobj, struct scst_acg, acg_kobj);
+
+       return __scst_acg_mpio_type_show(acg, buf);
+}
+
+static ssize_t scst_acg_mpio_type_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int res;
+       struct scst_acg *acg;
+
+       acg = container_of(kobj, struct scst_acg, acg_kobj);
+
+       res = __scst_acg_mpio_type_store(acg, buf, count);
+
+       TRACE_EXIT_RES(res);
+       return res;
+}
+
 static ssize_t scst_ini_group_mgmt_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf)
 {
@@ -2509,7 +2852,9 @@ static ssize_t scst_threads_show(struct kobject *kobj,
 
        TRACE_ENTRY();
 
-       count = sprintf(buf, "%d\n", scst_global_threads_count());
+       count = sprintf(buf, "%d\n%s", scst_main_cmd_threads.nr_threads,
+               (scst_main_cmd_threads.nr_threads != scst_threads) ?
+                       SCST_SYSFS_KEY_MARK "\n" : "");
 
        TRACE_EXIT();
        return count;
@@ -2523,12 +2868,12 @@ static ssize_t scst_threads_store(struct kobject *kobj,
 
        TRACE_ENTRY();
 
-       if (mutex_lock_interruptible(&scst_global_threads_mutex) != 0) {
+       if (mutex_lock_interruptible(&scst_mutex) != 0) {
                res = -EINTR;
                goto out;
        }
 
-       oldtn = scst_nr_global_threads;
+       oldtn = scst_main_cmd_threads.nr_threads;
 
        res = strict_strtoul(buf, 0, &newtn);
        if (res != 0) {
@@ -2536,18 +2881,27 @@ static ssize_t scst_threads_store(struct kobject *kobj,
                goto out_up;
        }
 
+       if (newtn <= 0) {
+               PRINT_ERROR("Illegal threads num value %ld", newtn);
+               res = -EINVAL;
+               goto out_up;
+       }
+
        delta = newtn - oldtn;
        if (delta < 0)
-               __scst_del_global_threads(-delta);
-       else
-               __scst_add_global_threads(delta);
+               scst_del_threads(&scst_main_cmd_threads, -delta);
+       else {
+               res = scst_add_threads(&scst_main_cmd_threads, NULL, NULL, delta);
+               if (res != 0)
+                       goto out_up;
+       }
 
        PRINT_INFO("Changed cmd threads num: old %ld, new %ld", oldtn, newtn);
 
        res = count;
 
 out_up:
-       mutex_unlock(&scst_global_threads_mutex);
+       mutex_unlock(&scst_mutex);
 
 out:
        TRACE_EXIT_RES(res);
index a8d65f8..af82a6f 100644 (file)
@@ -315,16 +315,16 @@ active:
                        pref_context);
                /* go through */
        case SCST_CONTEXT_THREAD:
-               spin_lock_irqsave(&cmd->cmd_lists->cmd_list_lock, flags);
+               spin_lock_irqsave(&cmd->cmd_threads->cmd_list_lock, flags);
                TRACE_DBG("Adding cmd %p to active cmd list", cmd);
                if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))
                        list_add(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
+                               &cmd->cmd_threads->active_cmd_list);
                else
                        list_add_tail(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
-               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-               spin_unlock_irqrestore(&cmd->cmd_lists->cmd_list_lock, flags);
+                               &cmd->cmd_threads->active_cmd_list);
+               wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+               spin_unlock_irqrestore(&cmd->cmd_threads->cmd_list_lock, flags);
                break;
        }
 
@@ -1066,16 +1066,16 @@ static void scst_process_redirect_cmd(struct scst_cmd *cmd,
        case SCST_CONTEXT_THREAD:
                if (check_retries)
                        scst_check_retries(tgt);
-               spin_lock_irqsave(&cmd->cmd_lists->cmd_list_lock, flags);
+               spin_lock_irqsave(&cmd->cmd_threads->cmd_list_lock, flags);
                TRACE_DBG("Adding cmd %p to active cmd list", cmd);
                if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))
                        list_add(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
+                               &cmd->cmd_threads->active_cmd_list);
                else
                        list_add_tail(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
-               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-               spin_unlock_irqrestore(&cmd->cmd_lists->cmd_list_lock, flags);
+                               &cmd->cmd_threads->active_cmd_list);
+               wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+               spin_unlock_irqrestore(&cmd->cmd_threads->cmd_list_lock, flags);
                break;
 
        case SCST_CONTEXT_TASKLET:
@@ -3307,7 +3307,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
                                        break;
                                }
 
-                               cmd->cmd_lists = tgt_dev->dev->p_cmd_lists;
+                               cmd->cmd_threads = tgt_dev->active_cmd_threads;
                                cmd->tgt_dev = tgt_dev;
                                cmd->dev = tgt_dev->dev;
 
@@ -3333,7 +3333,7 @@ static int scst_translate_lun(struct scst_cmd *cmd)
 }
 
 /*
- * No locks, but might be on IRQ
+ * No locks, but might be on IRQ.
  *
  * Returns 0 on success, > 0 when we need to wait for unblock,
  * < 0 if there is no device (lun) or device type handler.
@@ -3462,16 +3462,16 @@ restart:
                list_del(&cmd->cmd_list_entry);
                spin_unlock(&scst_init_lock);
 
-               spin_lock(&cmd->cmd_lists->cmd_list_lock);
+               spin_lock(&cmd->cmd_threads->cmd_list_lock);
                TRACE_MGMT_DBG("Adding cmd %p to active cmd list", cmd);
                if (unlikely(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE))
                        list_add(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
+                               &cmd->cmd_threads->active_cmd_list);
                else
                        list_add_tail(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
-               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-               spin_unlock(&cmd->cmd_lists->cmd_list_lock);
+                               &cmd->cmd_threads->active_cmd_list);
+               wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+               spin_unlock(&cmd->cmd_threads->cmd_list_lock);
 
                spin_lock(&scst_init_lock);
                goto restart;
@@ -3664,7 +3664,7 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
        if (res == SCST_CMD_STATE_RES_CONT_NEXT) {
                /* None */
        } else if (res == SCST_CMD_STATE_RES_NEED_THREAD) {
-               spin_lock_irq(&cmd->cmd_lists->cmd_list_lock);
+               spin_lock_irq(&cmd->cmd_threads->cmd_list_lock);
 #ifdef CONFIG_SCST_EXTRACHECKS
                switch (cmd->state) {
                case SCST_CMD_STATE_DEV_PARSE:
@@ -3680,20 +3680,20 @@ void scst_process_active_cmd(struct scst_cmd *cmd, bool atomic)
                        TRACE_DBG("Adding cmd %p to head of active cmd list",
                                  cmd);
                        list_add(&cmd->cmd_list_entry,
-                               &cmd->cmd_lists->active_cmd_list);
+                               &cmd->cmd_threads->active_cmd_list);
 #ifdef CONFIG_SCST_EXTRACHECKS
                        break;
                default:
                        PRINT_CRIT_ERROR("cmd %p is in invalid state %d)", cmd,
                                cmd->state);
-                       spin_unlock_irq(&cmd->cmd_lists->cmd_list_lock);
+                       spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock);
                        sBUG();
-                       spin_lock_irq(&cmd->cmd_lists->cmd_list_lock);
+                       spin_lock_irq(&cmd->cmd_threads->cmd_list_lock);
                        break;
                }
 #endif
-               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-               spin_unlock_irq(&cmd->cmd_lists->cmd_list_lock);
+               wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+               spin_unlock_irq(&cmd->cmd_threads->cmd_list_lock);
        } else
                sBUG();
 
@@ -3731,9 +3731,9 @@ static void scst_do_job_active(struct list_head *cmd_list,
        return;
 }
 
-static inline int test_cmd_lists(struct scst_cmd_lists *p_cmd_lists)
+static inline int test_cmd_threads(struct scst_cmd_threads *p_cmd_threads)
 {
-       int res = !list_empty(&p_cmd_lists->active_cmd_list) ||
+       int res = !list_empty(&p_cmd_threads->active_cmd_list) ||
            unlikely(kthread_should_stop()) ||
            tm_dbg_is_release();
        return res;
@@ -3741,62 +3741,81 @@ static inline int test_cmd_lists(struct scst_cmd_lists *p_cmd_lists)
 
 int scst_cmd_thread(void *arg)
 {
-       struct scst_cmd_lists *p_cmd_lists = (struct scst_cmd_lists *)arg;
+       struct scst_cmd_threads *p_cmd_threads = (struct scst_cmd_threads *)arg;
+       static DEFINE_MUTEX(io_context_mutex);
 
        TRACE_ENTRY();
 
-       PRINT_INFO("Processing thread started, PID %d", current->pid);
+       PRINT_INFO("Processing thread %s (PID %d) started", current->comm,
+               current->pid);
 
 #if 0
        set_user_nice(current, 10);
 #endif
        current->flags |= PF_NOFREEZE;
 
-       spin_lock_irq(&p_cmd_lists->cmd_list_lock);
+       mutex_lock(&io_context_mutex);
+
+       WARN_ON(current->io_context);
+
+       if (p_cmd_threads != &scst_main_cmd_threads) {
+               if (p_cmd_threads->io_context == NULL) {
+                       p_cmd_threads->io_context = ioc_task_link(
+                               get_io_context(GFP_KERNEL, -1));
+                       TRACE_DBG("Alloced new IO context %p "
+                               "(p_cmd_threads %p)",
+                               p_cmd_threads->io_context,
+                               p_cmd_threads);
+               } else {
+                       put_io_context(current->io_context);
+                       current->io_context = ioc_task_link(
+                                               p_cmd_threads->io_context);
+                       TRACE_DBG("Linked IO context %p "
+                               "(p_cmd_threads %p)",
+                               p_cmd_threads->io_context,
+                               p_cmd_threads);
+               }
+       }
+
+       mutex_unlock(&io_context_mutex);
+
+       spin_lock_irq(&p_cmd_threads->cmd_list_lock);
        while (!kthread_should_stop()) {
                wait_queue_t wait;
                init_waitqueue_entry(&wait, current);
 
-               if (!test_cmd_lists(p_cmd_lists)) {
+               if (!test_cmd_threads(p_cmd_threads)) {
                        add_wait_queue_exclusive_head(
-                               &p_cmd_lists->cmd_list_waitQ,
+                               &p_cmd_threads->cmd_list_waitQ,
                                &wait);
                        for (;;) {
                                set_current_state(TASK_INTERRUPTIBLE);
-                               if (test_cmd_lists(p_cmd_lists))
+                               if (test_cmd_threads(p_cmd_threads))
                                        break;
-                               spin_unlock_irq(&p_cmd_lists->cmd_list_lock);
+                               spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
                                schedule();
-                               spin_lock_irq(&p_cmd_lists->cmd_list_lock);
+                               spin_lock_irq(&p_cmd_threads->cmd_list_lock);
                        }
                        set_current_state(TASK_RUNNING);
-                       remove_wait_queue(&p_cmd_lists->cmd_list_waitQ, &wait);
+                       remove_wait_queue(&p_cmd_threads->cmd_list_waitQ, &wait);
                }
 
                if (tm_dbg_is_release()) {
-                       spin_unlock_irq(&p_cmd_lists->cmd_list_lock);
+                       spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
                        tm_dbg_check_released_cmds();
-                       spin_lock_irq(&p_cmd_lists->cmd_list_lock);
+                       spin_lock_irq(&p_cmd_threads->cmd_list_lock);
                }
 
-               scst_do_job_active(&p_cmd_lists->active_cmd_list,
-                       &p_cmd_lists->cmd_list_lock, false);
+               scst_do_job_active(&p_cmd_threads->active_cmd_list,
+                       &p_cmd_threads->cmd_list_lock, false);
        }
-       spin_unlock_irq(&p_cmd_lists->cmd_list_lock);
+       spin_unlock_irq(&p_cmd_threads->cmd_list_lock);
 
-#ifdef CONFIG_SCST_EXTRACHECKS
-       /*
-        * If kthread_should_stop() is true, we are guaranteed to be either
-        * on the module unload, or there must be at least one other thread to
-        * process the commands lists.
-        */
-       if (p_cmd_lists == &scst_main_cmd_lists) {
-               sBUG_ON((scst_nr_global_threads == 1) &&
-                        !list_empty(&scst_main_cmd_lists.active_cmd_list));
-       }
-#endif
+       EXTRACHECKS_BUG_ON((p_cmd_threads->nr_threads == 1) &&
+                !list_empty(&p_cmd_threads->active_cmd_list));
 
-       PRINT_INFO("Processing thread PID %d finished", current->pid);
+       PRINT_INFO("Processing thread %s (PID %d) finished", current->comm,
+               current->pid);
 
        TRACE_EXIT();
        return 0;
@@ -4224,11 +4243,11 @@ static bool __scst_check_unblock_aborted_cmd(struct scst_cmd *cmd,
        bool res;
        if (test_bit(SCST_CMD_ABORTED, &cmd->cmd_flags)) {
                list_del(list_entry);
-               spin_lock(&cmd->cmd_lists->cmd_list_lock);
+               spin_lock(&cmd->cmd_threads->cmd_list_lock);
                list_add_tail(&cmd->cmd_list_entry,
-                       &cmd->cmd_lists->active_cmd_list);
-               wake_up(&cmd->cmd_lists->cmd_list_waitQ);
-               spin_unlock(&cmd->cmd_lists->cmd_list_lock);
+                       &cmd->cmd_threads->active_cmd_list);
+               wake_up(&cmd->cmd_threads->cmd_list_waitQ);
+               spin_unlock(&cmd->cmd_threads->cmd_list_lock);
                res = 1;
        } else
                res = 0;