- Dev handler sysfs interface changed to match new unified rules
authorvlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 9 Mar 2010 19:36:48 +0000 (19:36 +0000)
committervlnb <vlnb@d57e44dd-8a1f-0410-8b47-8ef2f437770f>
Tue, 9 Mar 2010 19:36:48 +0000 (19:36 +0000)
 - Docs updated
 - Other minor fixes, improvements and cleanups

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

25 files changed:
iscsi-scst/README
iscsi-scst/README_in-tree
iscsi-scst/doc/iscsi-scst-howto.txt
iscsi-scst/kernel/iscsi.h
iscsi-scst/kernel/target.c
iscsi-scst/usr/config.c
iscsi-scst/usr/event.c
qla2x00t/qla2x00-target/README
qla2x00t/qla2x00-target/ToDo
qla2x00t/qla2x00-target/qla2x00t.c
scst/README
scst/README_in-tree
scst/include/scst.h
scst/src/dev_handlers/scst_cdrom.c
scst/src/dev_handlers/scst_changer.c
scst/src/dev_handlers/scst_disk.c
scst/src/dev_handlers/scst_modisk.c
scst/src/dev_handlers/scst_processor.c
scst/src/dev_handlers/scst_raid.c
scst/src/dev_handlers/scst_tape.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_sysfs.c

index 8907429..bb31f4f 100644 (file)
@@ -31,7 +31,7 @@ See HOWTOs in the doc/ subdirectory.
 Only vanilla kernels from kernel.org and RHEL/CentOS 5.2 kernels are
 supported, but it should work on other (vendors') kernels, if you manage
 to successfully compile on them. The main problem with vendor's kernels
-is that they often contain patches, which will appear only in the next
+is that they often contain patches, which appear only in the next
 version of the vanilla kernel, therefore it's quite hard to track such
 changes. Thus, if during compilation for some vendor's kernel your
 compiler complains about redefinition of some symbol, you should either
@@ -87,7 +87,7 @@ functionality, which put_page_callback patch provides. For example,
 Fedora or Gentoo use kernels, which, although have version number like
 2.6.18, are greatly differ from the "vanilla" kernel 2.6.18,
 maintained by Linus Torvalds for that the put_page_callback patch was
-created. In this case I would recommend you either:
+created. In this case it is recommended either:
 
  - Search net/ in your kernel source for "put_page" and "get_page" functions.
    If you find any in some place, except in net/sunrpc/svc.c and
@@ -108,14 +108,11 @@ Usage
 
 See HOWTOs in the doc/ subdirectory.
 
-In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm is
+In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm utility is
 obsolete. Use the sysfs interface facilities instead.
 
 It is recommended to use TEST UNIT READY ("tur") command to check if
-iSCSI-SCST target is alive.
-
-IMPORTANT: In the procfs build all LUN information (access control)
-=========  MUST be configured BEFORE iscsi-scstd started!
+iSCSI-SCST target is alive in MPIO configurations.
 
 Also see SCST README file how to tune for the best performance.
 
@@ -128,8 +125,9 @@ Sysfs interface
 
 Starting from 2.0.0 iSCSI-SCST has sysfs interface. You can switch to it
 by running "make disable_proc". To switch back to the procfs interface
-you should run "make enable_proc". The procfs interface from version
-2.0.0 is obsolete and will be removed in one of the next versions.
+you should run "make enable_proc". The procfs interface starting from
+version 2.0.0 is obsolete and will be removed in one of the next
+versions.
 
 Root of SCST sysfs interface is /sys/kernel/scst_tgt. Root of iSCSI-SCST
 is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries:
@@ -168,9 +166,9 @@ is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries:
    per-target attributes. See content of this file for help how to use
    it.
 
-Each iSCSI-SCST attribute can contain in the last line mark "[key]". It
-is automatically added mark used to allow scstadmin to see which
-attributes it should save in the config file. You can ignore it.
+Each iSCSI-SCST sysfs file (attribute) can contain in the last line mark
+"[key]". It is automatically added mark used to allow scstadmin to see
+which attributes it should save in the config file. You can ignore it.
 
 Each target subdirectory contains the following entries:
 
@@ -192,16 +190,40 @@ Each target subdirectory contains the following entries:
    the "mgmt" entry, see above.
 
  - Entries defining default iSCSI parameters values used during iSCSI
-   parameters negotiation.
+   parameters negotiation. Only entries which can be changed or make
+   sense are listed there.
 
  - QueuedCommands - defines maximum number of commands queued to any
-   session of this target.
+   session of this target. Default is 32 commands.
+
+ - RspTimeout - defines the maximum time in seconds a command can wait for
+   response from initiator, otherwise the corresponding connection will
+   be closed. For performance reasons it is implemented as a timer,
+   which once in RspTimeout time checks the oldest command waiting for
+   response and, if it's older than RspTimeout, then it closes the
+   connection. Hence, a stalled connection will be closed in time
+   between RspTimeout and 2*RspTimeout. Default is 30 seconds.
+
+ - NopInInterval - defines interval between NOP-In requests, which the
+   target will send on idle connections to check if the initiator is
+   still alive. If there is no NOP-Out reply from the initiator in
+   RspTimeout time, the corresponding connection will be closed. Default
+   is 30 seconds. If it's set to 0, then NOP-In requests are disabled.
 
  - enabled - using this attribute you can enable or disable iSCSI-SCST
    accept new connections to this target. It allows to finish
    configuring it before it starts accepting new connections. 0 by
    default.
 
+ - 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
+   commands. This identifier must be unique among all SCST targets, but
+   for convenience SCST allows disabled targets to have not unique
+   rel_tgt_id. In this case SCST will not allow to enable this target
+   until rel_tgt_id becomes unique. This attribute initialized unique by
+   SCST by default.
+
  - tid - TID of this target.
 
 Subdirectory "sessions" contains one subdirectory for each connected
@@ -213,7 +235,8 @@ Each session subdirectory contains the following entries:
    supports 1 connection per session, but the session subdirectory can
    contain several connections: one active and other being closed.
 
- - Entries defining negotiated iSCSI parameters.
+ - Entries defining negotiated iSCSI parameters. Only parameters which
+   can be changed or make sense are listed there.
 
  - initiator_name - contains initiator name
 
@@ -246,18 +269,16 @@ parameters:
 modprobe scst
 modprobe scst_vdisk
 
-echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
 
 service iscsi-scst start
 
 echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
-
 echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
 
 echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
 echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
 
-
 Below is more advanced sample script, which configures more virtual
 devices of various types, including virtual CDROM and 2 targets, one
 with all default parameters, another one with some not default
@@ -271,11 +292,11 @@ authentication for discovery sessions and iSNS server with access control.
 modprobe scst
 modprobe scst_vdisk
 
-echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
-echo "open disk2 /disk2 4096 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
-echo "open blockio /dev/sda5" >/sys/kernel/scst_tgt/handlers/vdisk_blockio/mgmt
-echo "open nullio none" >/sys/kernel/scst_tgt/handlers/vdisk_nullio/mgmt
-echo "open cdrom" >/sys/kernel/scst_tgt/handlers/vcdrom/mgmt
+echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device disk2 filename=/disk2; blocksize=4096; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device blockio filename=/dev/sda5" >/sys/kernel/scst_tgt/handlers/vdisk_blockio/mgmt
+echo "add_device nullio" >/sys/kernel/scst_tgt/handlers/vdisk_nullio/mgmt
+echo "add_device cdrom" >/sys/kernel/scst_tgt/handlers/vcdrom/mgmt
 
 service iscsi-scst start
 
@@ -288,27 +309,26 @@ echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/m
 echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
 echo "add cdrom 1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
 
-echo "add_target iqn.2006-10.net.vlnb:tgt1 \
-       IncomingUser1 joe2 12charsecret2; \
-       IncomingUser joe 12charsecret; \
-       OutgoingUser jim1 12charpasswd; \
-       InitialR2T                      No; \
-       ImmediateData                   Yes; \
-       MaxRecvDataSegmentLength        8192; \
-       MaxXmitDataSegmentLength        8192; \
-       MaxBurstLength                  131072; \
-       FirstBurstLength                32768; \
-       MaxOutstandingR2T               1; \
-       HeaderDigest                    CRC32C,None; \
-       DataDigest                      CRC32C,None; \
-       QueuedCommands                  8; \
-       " >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+echo "add_target iqn.2006-10.net.vlnb:tgt1" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+echo "add_target_attribute iqn.2006-10.net.vlnb:tgt1 IncomingUser1 joe2 12charsecret2" >/sys/kernel/scst_tgt/targets/iscsi/mgmt             
+echo "add_target_attribute iqn.2006-10.net.vlnb:tgt1 IncomingUser joe 12charsecret" >/sys/kernel/scst_tgt/targets/iscsi/mgmt                
+echo "add_target_attribute iqn.2006-10.net.vlnb:tgt1 OutgoingUser jim1 12charpasswd" >/sys/kernel/scst_tgt/targets/iscsi/mgmt               
+echo "No" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/InitialR2T                                                          
+echo "Yes" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ImmediateData                                                      
+echo "8192" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxRecvDataSegmentLength                                          
+echo "8192" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxXmitDataSegmentLength                                          
+echo "131072" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxBurstLength                                                  
+echo "32768" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/FirstBurstLength                                                 
+echo "1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxOutstandingR2T                                                    
+echo "CRC32C,None" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/HeaderDigest                                               
+echo "CRC32C,None" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/DataDigest                                                 
+echo "32" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/QueuedCommands                                                      
 
 echo "add disk2 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
 echo "add nullio 26" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
 
 echo "create special_ini" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/mgmt
-echo "add blockio 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/mgmt
+echo "add blockio 0 read_only=1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/mgmt
 echo "add iqn.2005-03.org.open-iscsi:cacdcd2520" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/initiators/mgmt
 
 echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
@@ -322,7 +342,7 @@ both iSCSI-SCST targets will look like:
 /sys/kernel/scst_tgt
 |-- devices
 |   |-- blockio
-|   |   |-- block_size
+|   |   |-- blocksize
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/0
 |   |   |-- filename
@@ -330,20 +350,21 @@ both iSCSI-SCST targets will look like:
 |   |   |-- read_only
 |   |   |-- removable
 |   |   |-- resync_size
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
-|   |   `-- type
+|   |   |-- type
+|   |   `-- usn
 |   |-- cdrom
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/1
 |   |   |-- filename
 |   |   |-- handler -> ../../handlers/vcdrom
-|   |   |-- removable
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
-|   |   `-- type
+|   |   |-- type
+|   |   `-- usn
 |   |-- disk1
-|   |   |-- block_size
+|   |   |-- blocksize
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
 |   |   |-- filename
@@ -353,12 +374,13 @@ both iSCSI-SCST targets will look like:
 |   |   |-- read_only
 |   |   |-- removable
 |   |   |-- resync_size
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
 |   |   |-- type
+|   |   |-- usn
 |   |   `-- write_through
 |   |-- disk2
-|   |   |-- block_size
+|   |   |-- blocksize
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
 |   |   |-- filename
@@ -368,35 +390,42 @@ both iSCSI-SCST targets will look like:
 |   |   |-- read_only
 |   |   |-- removable
 |   |   |-- resync_size
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
 |   |   |-- type
+|   |   |-- usn
 |   |   `-- write_through
 |   `-- nullio
-|       |-- block_size
+|       |-- blocksize
 |       |-- exported
 |       |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/26
 |       |-- handler -> ../../handlers/vdisk_nullio
 |       |-- read_only
 |       |-- removable
-|       |-- size
+|       |-- size_mb
 |       |-- t10_dev_id
-|       `-- type
+|       |-- type
+|       `-- usn
 |-- handlers
 |   |-- vcdrom
+|   |   |-- cdrom -> ../../devices/cdrom
 |   |   |-- mgmt
 |   |   |-- trace_level
 |   |   `-- type
 |   |-- vdisk_blockio
+|   |   |-- blockio -> ../../devices/blockio
 |   |   |-- mgmt
 |   |   |-- trace_level
 |   |   `-- type
 |   |-- vdisk_fileio
+|   |   |-- disk1 -> ../../devices/disk1
+|   |   |-- disk2 -> ../../devices/disk2
 |   |   |-- mgmt
 |   |   |-- trace_level
 |   |   `-- type
 |   `-- vdisk_nullio
 |       |-- mgmt
+|       |-- nullio -> ../../devices/nullio
 |       |-- trace_level
 |       `-- type
 |-- sgv
@@ -435,6 +464,7 @@ both iSCSI-SCST targets will look like:
 |       |   |   |   |-- device -> ../../../../../devices/cdrom
 |       |   |   |   `-- read_only
 |       |   |   `-- mgmt
+|       |   |-- rel_tgt_id
 |       |   |-- sessions
 |       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
 |       |   |       |-- 10.170.75.2
@@ -454,6 +484,7 @@ both iSCSI-SCST targets will look like:
 |       |   |       |-- commands
 |       |   |       |-- force_close
 |       |   |       |-- initiator_name
+|       |   |       |-- luns -> ../../luns
 |       |   |       |-- reinstating
 |       |   |       `-- sid
 |       |   `-- tid
@@ -491,6 +522,7 @@ both iSCSI-SCST targets will look like:
 |       |   |   |   |-- device -> ../../../../../devices/nullio
 |       |   |   |   `-- read_only
 |       |   |   `-- mgmt
+|       |   |-- rel_tgt_id
 |       |   |-- sessions
 |       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
 |       |   |       |-- 10.170.75.2
@@ -510,6 +542,7 @@ both iSCSI-SCST targets will look like:
 |       |   |       |-- commands
 |       |   |       |-- force_close
 |       |   |       |-- initiator_name
+|       |   |       |-- luns -> ../../ini_group/special_ini/luns
 |       |   |       |-- reinstating
 |       |   |       `-- sid
 |       |   `-- tid
@@ -568,7 +601,7 @@ http://virtualgeek.typepad.com/virtual_geek/2009/01/a-multivendor-post-to-help-o
 It's about VMware, but its recommendations apply to other environments
 as well.
 
-3. ISCSI initiators built in pre-CentOS/RHEL 5 reported to have some
+3. ISCSI initiators from pre-CentOS/RHEL 5 reported to have some
 performance problems. If you use it, it is strongly advised to upgrade.
 
 
index 61871c3..6d4f60b 100644 (file)
@@ -17,11 +17,11 @@ Usage
 See in http://scst.sourceforge.net/iscsi-scst-howto.txt how to configure
 iSCSI-SCST.
 
-In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm is
+In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm utility is
 obsolete. Use the sysfs interface facilities instead.
 
 It is recommended to use TEST UNIT READY ("tur") command to check if
-iSCSI-SCST target is alive.
+iSCSI-SCST target is alive in MPIO configurations.
 
 IMPORTANT: In the procfs build all LUN information (access control)
 =========  MUST be configured BEFORE iscsi-scstd started!
@@ -77,9 +77,9 @@ is /sys/kernel/scst_tgt/targets/iscsi. It has the following entries:
    per-target attributes. See content of this file for help how to use
    it.
 
-Each iSCSI-SCST attribute can contain in the last line mark "[key]". It
-is automatically added mark used to allow scstadmin to see which
-attributes it should save in the config file. You can ignore it.
+Each iSCSI-SCST sysfs file (attribute) can contain in the last line mark
+"[key]". It is automatically added mark used to allow scstadmin to see
+which attributes it should save in the config file. You can ignore it.
 
 Each target subdirectory contains the following entries:
 
@@ -101,16 +101,40 @@ Each target subdirectory contains the following entries:
    the "mgmt" entry, see above.
 
  - Entries defining default iSCSI parameters values used during iSCSI
-   parameters negotiation.
+   parameters negotiation. Only entries which can be changed or make
+   sense are listed there.
 
  - QueuedCommands - defines maximum number of commands queued to any
-   session of this target.
+   session of this target. Default is 32 commands.
+
+ - RspTimeout - defines the maximum time in seconds a command can wait for
+   response from initiator, otherwise the corresponding connection will
+   be closed. For performance reasons it is implemented as a timer,
+   which once in RspTimeout time checks the oldest command waiting for
+   response and, if it's older than RspTimeout, then it closes the
+   connection. Hence, a stalled connection will be closed in time
+   between RspTimeout and 2*RspTimeout. Default is 30 seconds.
+
+ - NopInInterval - defines interval between NOP-In requests, which the
+   target will send on idle connections to check if the initiator is
+   still alive. If there is no NOP-Out reply from the initiator in
+   RspTimeout time, the corresponding connection will be closed. Default
+   is 30 seconds. If it's set to 0, then NOP-In requests are disabled.
 
  - enabled - using this attribute you can enable or disable iSCSI-SCST
    accept new connections to this target. It allows to finish
    configuring it before it starts accepting new connections. 0 by
    default.
 
+ - 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
+   commands. This identifier must be unique among all SCST targets, but
+   for convenience SCST allows disabled targets to have not unique
+   rel_tgt_id. In this case SCST will not allow to enable this target
+   until rel_tgt_id becomes unique. This attribute initialized unique by
+   SCST by default.
+
  - tid - TID of this target.
 
 Subdirectory "sessions" contains one subdirectory for each connected
@@ -122,7 +146,8 @@ Each session subdirectory contains the following entries:
    supports 1 connection per session, but the session subdirectory can
    contain several connections: one active and other being closed.
 
- - Entries defining negotiated iSCSI parameters.
+ - Entries defining negotiated iSCSI parameters. Only parameters which
+   can be changed or make sense are listed there.
 
  - initiator_name - contains initiator name
 
@@ -155,18 +180,16 @@ parameters:
 modprobe scst
 modprobe scst_vdisk
 
-echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
 
 service iscsi-scst start
 
 echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
-
 echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
 
 echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
 echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
 
-
 Below is more advanced sample script, which configures more virtual
 devices of various types, including virtual CDROM and 2 targets, one
 with all default parameters, another one with some not default
@@ -180,11 +203,11 @@ authentication for discovery sessions and iSNS server with access control.
 modprobe scst
 modprobe scst_vdisk
 
-echo "open disk1 /disk1 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
-echo "open disk2 /disk2 4096 NV_CACHE" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
-echo "open blockio /dev/sda5" >/sys/kernel/scst_tgt/handlers/vdisk_blockio/mgmt
-echo "open nullio none" >/sys/kernel/scst_tgt/handlers/vdisk_nullio/mgmt
-echo "open cdrom" >/sys/kernel/scst_tgt/handlers/vcdrom/mgmt
+echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device disk2 filename=/disk2; blocksize=4096; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device blockio filename=/dev/sda5" >/sys/kernel/scst_tgt/handlers/vdisk_blockio/mgmt
+echo "add_device nullio" >/sys/kernel/scst_tgt/handlers/vdisk_nullio/mgmt
+echo "add_device cdrom" >/sys/kernel/scst_tgt/handlers/vcdrom/mgmt
 
 service iscsi-scst start
 
@@ -197,27 +220,26 @@ echo "add_target iqn.2006-10.net.vlnb:tgt" >/sys/kernel/scst_tgt/targets/iscsi/m
 echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
 echo "add cdrom 1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/mgmt
 
-echo "add_target iqn.2006-10.net.vlnb:tgt1 \
-       IncomingUser1 joe2 12charsecret2; \
-       IncomingUser joe 12charsecret; \
-       OutgoingUser jim1 12charpasswd; \
-       InitialR2T                      No; \
-       ImmediateData                   Yes; \
-       MaxRecvDataSegmentLength        8192; \
-       MaxXmitDataSegmentLength        8192; \
-       MaxBurstLength                  131072; \
-       FirstBurstLength                32768; \
-       MaxOutstandingR2T               1; \
-       HeaderDigest                    CRC32C,None; \
-       DataDigest                      CRC32C,None; \
-       QueuedCommands                  8; \
-       " >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+echo "add_target iqn.2006-10.net.vlnb:tgt1" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+echo "add_target_attribute iqn.2006-10.net.vlnb:tgt1 IncomingUser1 joe2 12charsecret2" >/sys/kernel/scst_tgt/targets/iscsi/mgmt             
+echo "add_target_attribute iqn.2006-10.net.vlnb:tgt1 IncomingUser joe 12charsecret" >/sys/kernel/scst_tgt/targets/iscsi/mgmt                
+echo "add_target_attribute iqn.2006-10.net.vlnb:tgt1 OutgoingUser jim1 12charpasswd" >/sys/kernel/scst_tgt/targets/iscsi/mgmt               
+echo "No" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/InitialR2T                                                          
+echo "Yes" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ImmediateData                                                      
+echo "8192" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxRecvDataSegmentLength                                          
+echo "8192" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxXmitDataSegmentLength                                          
+echo "131072" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxBurstLength                                                  
+echo "32768" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/FirstBurstLength                                                 
+echo "1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/MaxOutstandingR2T                                                    
+echo "CRC32C,None" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/HeaderDigest                                               
+echo "CRC32C,None" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/DataDigest                                                 
+echo "32" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/QueuedCommands                                                      
 
 echo "add disk2 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
 echo "add nullio 26" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/mgmt
 
 echo "create special_ini" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/mgmt
-echo "add blockio 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/mgmt
+echo "add blockio 0 read_only=1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/mgmt
 echo "add iqn.2005-03.org.open-iscsi:cacdcd2520" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/initiators/mgmt
 
 echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt/enabled
@@ -231,7 +253,7 @@ both iSCSI-SCST targets will look like:
 /sys/kernel/scst_tgt
 |-- devices
 |   |-- blockio
-|   |   |-- block_size
+|   |   |-- blocksize
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/0
 |   |   |-- filename
@@ -239,20 +261,21 @@ both iSCSI-SCST targets will look like:
 |   |   |-- read_only
 |   |   |-- removable
 |   |   |-- resync_size
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
-|   |   `-- type
+|   |   |-- type
+|   |   `-- usn
 |   |-- cdrom
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/1
 |   |   |-- filename
 |   |   |-- handler -> ../../handlers/vcdrom
-|   |   |-- removable
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
-|   |   `-- type
+|   |   |-- type
+|   |   `-- usn
 |   |-- disk1
-|   |   |-- block_size
+|   |   |-- blocksize
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
 |   |   |-- filename
@@ -262,12 +285,13 @@ both iSCSI-SCST targets will look like:
 |   |   |-- read_only
 |   |   |-- removable
 |   |   |-- resync_size
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
 |   |   |-- type
+|   |   |-- usn
 |   |   `-- write_through
 |   |-- disk2
-|   |   |-- block_size
+|   |   |-- blocksize
 |   |   |-- exported
 |   |   |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
 |   |   |-- filename
@@ -277,35 +301,42 @@ both iSCSI-SCST targets will look like:
 |   |   |-- read_only
 |   |   |-- removable
 |   |   |-- resync_size
-|   |   |-- size
+|   |   |-- size_mb
 |   |   |-- t10_dev_id
 |   |   |-- type
+|   |   |-- usn
 |   |   `-- write_through
 |   `-- nullio
-|       |-- block_size
+|       |-- blocksize
 |       |-- exported
 |       |   `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/26
 |       |-- handler -> ../../handlers/vdisk_nullio
 |       |-- read_only
 |       |-- removable
-|       |-- size
+|       |-- size_mb
 |       |-- t10_dev_id
-|       `-- type
+|       |-- type
+|       `-- usn
 |-- handlers
 |   |-- vcdrom
+|   |   |-- cdrom -> ../../devices/cdrom
 |   |   |-- mgmt
 |   |   |-- trace_level
 |   |   `-- type
 |   |-- vdisk_blockio
+|   |   |-- blockio -> ../../devices/blockio
 |   |   |-- mgmt
 |   |   |-- trace_level
 |   |   `-- type
 |   |-- vdisk_fileio
+|   |   |-- disk1 -> ../../devices/disk1
+|   |   |-- disk2 -> ../../devices/disk2
 |   |   |-- mgmt
 |   |   |-- trace_level
 |   |   `-- type
 |   `-- vdisk_nullio
 |       |-- mgmt
+|       |-- nullio -> ../../devices/nullio
 |       |-- trace_level
 |       `-- type
 |-- sgv
@@ -344,6 +375,7 @@ both iSCSI-SCST targets will look like:
 |       |   |   |   |-- device -> ../../../../../devices/cdrom
 |       |   |   |   `-- read_only
 |       |   |   `-- mgmt
+|       |   |-- rel_tgt_id
 |       |   |-- sessions
 |       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
 |       |   |       |-- 10.170.75.2
@@ -363,6 +395,7 @@ both iSCSI-SCST targets will look like:
 |       |   |       |-- commands
 |       |   |       |-- force_close
 |       |   |       |-- initiator_name
+|       |   |       |-- luns -> ../../luns
 |       |   |       |-- reinstating
 |       |   |       `-- sid
 |       |   `-- tid
@@ -400,6 +433,7 @@ both iSCSI-SCST targets will look like:
 |       |   |   |   |-- device -> ../../../../../devices/nullio
 |       |   |   |   `-- read_only
 |       |   |   `-- mgmt
+|       |   |-- rel_tgt_id
 |       |   |-- sessions
 |       |   |   `-- iqn.2005-03.org.open-iscsi:cacdcd2520
 |       |   |       |-- 10.170.75.2
@@ -419,6 +453,7 @@ both iSCSI-SCST targets will look like:
 |       |   |       |-- commands
 |       |   |       |-- force_close
 |       |   |       |-- initiator_name
+|       |   |       |-- luns -> ../../ini_group/special_ini/luns
 |       |   |       |-- reinstating
 |       |   |       `-- sid
 |       |   `-- tid
index 4bd731b..b3b2eed 100644 (file)
@@ -1,5 +1,5 @@
 Installing and using iSCSI-SCST with scstadmin
-----------------------------------------------
+==============================================
 
 1. Download, build and install iSCSI-SCST.
 
@@ -121,8 +121,8 @@ the following commands:
   /etc/init.d/iscsi-scst start
 
 
-Installing and using iSCSI-SCST without using scstadmin
--------------------------------------------------------
+Installing and using iSCSI-SCST without using scstadmin via /proc interface
+===========================================================================
 
 First repeat steps 1, 2 and 3 from the previous section but leave out
 "scstadm scstadm_install" from the make command in step 1.
@@ -287,6 +287,164 @@ OFMarkInt=Reject
 IFMarkInt=Reject
 
 
+Installing and using iSCSI-SCST without using scstadmin via /sys interface
+==========================================================================
+
+First repeat steps 1, 2 and 3 from the first section but leave out
+"scstadm scstadm_install" from the make command in step 1.
+
+Next load the scst_disk and scst_vdisk kernel modules as follows:
+
+modprobe scst_disk
+modprobe scst_vdisk
+
+The shell script /etc/init.d/iscsi-scst can now be used to start/stop/restart or
+check the status of iSCSI-SCST.
+
+
+Creating targets
+----------------
+
+echo "add_target iqn.2007-05.com.example:storage.iscsi-scst-1" > /sys/kernel/scst_tgt/targets/iscsi/mgmt
+
+This will add target iqn.2007-05.com.example:storage.iscsi-scst-1.
+
+
+Changing targets' parameters
+----------------------------
+
+echo "8192" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example\:storage.iscsi-scst-1/MaxRecvDataSegmentLength
+
+This will change MaxRecvDataSegmentLength parameter of target
+iqn.2007-05.com.example:storage.iscsi-scst-1 to 8192.
+
+You can read it then by:
+
+# cat /sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example\:storage.iscsi-scst-1/MaxRecvDataSegmentLength
+8192
+[key]
+
+The mark "[key]" shows high level management tools, like scstadmin, that
+this attribute has not default value. You can ignore it.
+
+
+Add vdisk devices
+-----------------
+
+dd if=/dev/zero of=/disk1 bs=1M count=1024
+
+This will create a backend file /disk1 for our future virtual device
+disk1.
+
+echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+
+This will add new FILEIO device disk1 with backend file /disk1 and
+NV_CACHE option enabled.
+
+
+Selecting devices to be used by SCST
+------------------------------------
+
+You can see the list of available devices:
+
+# ls -1 /sys/kernel/scst_tgt/devices
+0:0:0:0
+1:0:0:0
+3:0:0:0
+3:0:0:1
+3:0:0:2
+3:0:0:3
+3:0:0:4
+3:0:0:5
+3:0:0:6
+3:0:0:7
+2:0:0:0
+4:0:0:0
+4:0:0:5
+4:0:0:6
+4:0:0:7
+4:0:0:8
+4:0:0:9
+4:0:1:0
+4:0:1:1
+4:0:1:2
+4:0:1:3
+4:0:1:4
+5:0:0:0
+5:0:0:5
+5:0:0:6
+5:0:0:7
+5:0:0:8
+5:0:0:9
+5:0:1:0
+5:0:1:1
+5:0:1:2
+5:0:1:3
+5:0:1:4
+disk1
+
+
+Defining LUN masking
+--------------------
+
+In order to associate specific LUNs with target
+iqn.2007-05.com.example:storage.iscsi-scst-1, do the following:
+
+echo "add 4:0:0:8 0" > /sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example\:storage.iscsi-scst-1/luns/mgmt
+
+This will assign the LUN 0 for pass-through device 4:0:0:8 to the target
+iqn.2007-05.com.example:storage.iscsi-scst-1.
+
+Note, you must have LUN 0 for each group. That's a SCSI requirement
+(documented in SCST's README).
+
+echo "add disk1 1 read_only=1" > /sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example\:storage.iscsi-scst-1/luns/mgmt
+
+This will assign the read only LUN 1 for virtual device disk1 to the
+target iqn.2007-05.com.example:storage.iscsi-scst-1.
+
+
+Deleting a LUN from a group
+---------------------------
+
+Run the following command:
+
+echo "del disk1" > /sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example\:storage.iscsi-scst-1/luns/mgmt
+
+This will remove the LUN "disk1" from the target iqn.2007-05.com.example:storage.iscsi-scst-1.
+
+For more information about LUN masking, refer to SCST README, section
+"Access and devices visibility management (LUN masking)".
+
+
+Enabling targets
+----------------
+
+After you created and configured target, you should enable it:
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example\:storage.iscsi-scst-1/enabled
+
+Then enable the iSCSI-SCST driver:
+
+echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
+
+
+Assigning targets to specific initiators
+----------------------------------------
+
+In order to assign targets to specific initiators, you need to have
+/etc/initiators.allow  and /etc/initiators.deny. You can find
+example files in etc/initiators.allow and etc/initiators.deny.
+
+Note that all targets are allowed to all initiators by default, so if
+you want to use /etc/initiators.allow, you will need to have
+/etc/initiators.deny that looks like this:
+
+ALL ALL
+
+This will deny all initiators expect for those defined in /etc/initiators.allow.
+
+
 Bart Van Assche
 Erez Zilber
 Vladislav Bolkhovitin
index 798f824..51f8982 100644 (file)
@@ -534,10 +534,9 @@ extern ssize_t iscsi_sysfs_send_event(uint32_t tid,
 extern struct iscsi_target *target_lookup_by_id(u32);
 extern int __add_target(struct iscsi_kern_target_info *);
 extern int __del_target(u32 id);
-extern ssize_t iscsi_sysfs_add_target(const char *target_name,
-       const char *params);
+extern ssize_t iscsi_sysfs_add_target(const char *target_name, char *params);
 extern ssize_t iscsi_sysfs_del_target(const char *target_name);
-extern ssize_t iscsi_sysfs_mgmt_cmd(const char *cmd);
+extern ssize_t iscsi_sysfs_mgmt_cmd(char *cmd);
 extern void target_del_session(struct iscsi_target *target,
        struct iscsi_session *session, int flags);
 extern void target_del_all_sess(struct iscsi_target *target, int flags);
index b8043d8..7fb1318 100644 (file)
@@ -547,7 +547,7 @@ bool iscsi_is_target_enabled(struct scst_tgt *scst_tgt)
        return tgt->tgt_enabled;
 }
 
-ssize_t iscsi_sysfs_add_target(const char *target_name, const char *params)
+ssize_t iscsi_sysfs_add_target(const char *target_name, char *params)
 {
        int res;
 
@@ -594,7 +594,7 @@ out:
        return res;
 }
 
-ssize_t iscsi_sysfs_mgmt_cmd(const char *cmd)
+ssize_t iscsi_sysfs_mgmt_cmd(char *cmd)
 {
        int res;
 
index 5d27ca5..6fe270d 100644 (file)
@@ -642,7 +642,7 @@ static int initiator_match(u32 tid, int fd, char *filename)
                p = buf;
                while (!isblank(*p) && (*p != '\0'))
                        p++;
-               if (p == '\0')
+               if (*p == '\0')
                        continue;
 
                *p = '\0';
@@ -830,7 +830,7 @@ int config_parse_main(const char *data, u32 cookie)
 
                        target = NULL;
                        p = config_sep_string(&q);
-                       if (p == '\0') {
+                       if (*p == '\0') {
                                log_error("Target name required on %s\n", q);
                                continue;
                        }
index 7c83f37..1825056 100644 (file)
@@ -330,7 +330,7 @@ static int handle_e_mgmt_cmd(int fd, const struct iscsi_kern_event *event)
                }
                res = handle_del_param(target, p, event->cookie);
        } else {
-               log_error("Syntax error at %s", p);
+               log_error("Syntax error at %s", pp);
                res = -EINVAL;
        }
 
index 471472d..33142b7 100644 (file)
@@ -17,7 +17,7 @@ Linux kernel 2.6.26 and higher. Sorry, kernels below 2.6.26 are not
 supported, because it's too hard to backport used initiator driver to
 older kernels.
 
-NPIV is particulary supported by this driver. You can create virtual
+NPIV is partially supported by this driver. You can create virtual
 targets using standard Linux interface by echoing wwpn:wwnn into
 /sys/class/fc_host/hostX/vport_create and work with them, but SCST core
 will not see those virtual targets and, hence, provide the
@@ -80,16 +80,22 @@ subdirectory. The target driver will be installed in
 uninstall'.
 
 After the drivers are loaded and adapters successfully initialized by
-the initiator driver, including firmware image load, the target mode
-should be enabled via a sysfs interface on a per card basis. Under the
-appropriate scsi_host there is an entry target_mode_enabled, where you
-should write "1", like:
+the initiator driver, including firmware image load, you should
+configure exported devices using the corresponding interface of SCST
+core. It is highly recommended to use scstadmin utility for that
+purpose.
+
+Then target mode should be enabled via a sysfs interface on a per card
+basis. Under the appropriate scsi_host there is an entry
+target_mode_enabled, where you should write "1", like:
 
 echo "1" >/sys/class/scsi_host/host0/target_mode_enabled
 
-Then you should configure exported devices using the corresponding
-interface of SCST core. It is highly recommended to use scstadmin
-utility for that purpose.
+For the sysfs build you should instead
+
+echo "1" >/sys/kernel/scst_tgt/targets/qla2x00t/target/enabled
+
+See below for full description of the driver's sysfs interface.
 
 You can find some installation and configuration HOWTOs in
 http://scst.sourceforge.net/qla2x00t-howto.html and
@@ -113,6 +119,9 @@ this feature, it is reported in the kernel logs ("confirmed completion
 supported" or not). No major performance degradation was noticed, if it
 is enabled. Supported only for 23xx+. Disabled by default.
 
+In the sysfs build it is moved in /sys/kernel/scst_tgt/targets/qla2x00t/target/
+subdirectory, see the sysfs interface description below.
+
 
 Class 2
 -------
@@ -148,6 +157,260 @@ in/out in Makefile:
    initiator to issue a retransmit request.
 
 
+Sysfs interface
+---------------
+
+Starting from 2.0.0 this driver has sysfs interface. You can switch to
+it by running "make disable_proc". To switch back to the procfs
+interface you should run "make enable_proc". The procfs interface from
+version 2.0.0 is obsolete and will be removed in one of the next
+versions.
+
+Root of SCST sysfs interface is /sys/kernel/scst_tgt. Root of this
+driver is /sys/kernel/scst_tgt/targets/qla2x00t. It has the following
+entries:
+
+ - None, one or more subdirectories for targets with name equal to port
+   names of the corresponding targets.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - version - read-only attribute, which allows to see version of
+   this driver and enabled optional features.
+
+Each target subdirectory contains the following entries:
+
+ - host - link pointing on the corresponding scsi_host of the initiator
+   driver
+
+ - ini_group - subdirectory defining initiator groups for this target,
+   used to define per-initiator access control. See SCST core README for
+   more details.
+
+ - luns - subdirectory defining LUNs of this target. See SCST core
+   README for more details.
+
+ - sessions - subdirectory containing connected to this target sessions.
+
+ - enabled - using this attribute you can enable or disable target mode
+   of this FC port. It allows to finish configuring it before it starts
+   accepting new connections. 0 by default.
+
+ - explicit_confirmation - allows to enable explicit conformations, see
+   above.
+
+ - 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
+   commands. This identifier must be unique among all SCST targets, but
+   for convenience SCST allows disabled targets to have not unique
+   rel_tgt_id. In this case SCST will not allow to enable this target
+   until rel_tgt_id becomes unique. This attribute initialized unique by
+   SCST by default.
+
+Subdirectory "sessions" contains one subdirectory for each connected
+session with name equal to port name of the connected initiator.
+
+Each session subdirectory contains the following entries:
+
+ - initiator_name - contains initiator's port name
+
+ - active_commands - contains number of active, i.e. not yet or being
+   executed, SCSI commands in this session.
+
+ - commands - contains overall number of SCSI commands in this session.
+
+Below is a sample script, which configures 1 virtual disk "disk1" using
+/disk1 image for usage with 25:00:00:f0:98:87:92:f3 target. All
+initiators connected to this target will see this device.
+
+#!/bin/bash
+
+modprobe scst
+modprobe scst_vdisk
+
+echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+
+modprobe qla2x00tgt
+
+echo "add disk1 0" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/mgmt
+echo 1 >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/enabled
+
+Below is more advanced sample script, which configures more virtual
+devices of various types, including virtual CDROM. In this script
+initiator 25:00:00:f0:99:87:94:a3 will see disk1 and disk2 devices, all
+other initiators will see read only blockio, nullio and cdrom devices.
+
+#!/bin/bash
+
+modprobe scst
+modprobe scst_vdisk
+
+echo "add_device disk1 filename=/disk1; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device disk2 filename=/disk2; blocksize=4096; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+echo "add_device blockio filename=/dev/sda5" >/sys/kernel/scst_tgt/handlers/vdisk_blockio/mgmt
+echo "add_device nullio" >/sys/kernel/scst_tgt/handlers/vdisk_nullio/mgmt
+echo "add_device cdrom" >/sys/kernel/scst_tgt/handlers/vcdrom/mgmt
+
+modprobe qla2x00tgt
+
+echo "add blockio 0 read_only=1" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/mgmt
+echo "add nullio 1" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/mgmt
+echo "add cdrom 2" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/mgmt
+
+echo "create 25:00:00:f0:99:87:94:a3" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/ini_group/mgmt
+echo "add disk1 0" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/ini_group/25:00:00:f0:99:87:94:a3/luns/mgmt
+echo "add disk2 1" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/ini_group/25:00:00:f0:99:87:94:a3/luns/mgmt
+echo "add 25:00:00:f0:99:87:94:a3" >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/ini_group/25:00:00:f0:99:87:94:a3/initiators/mgmt
+
+echo 1 >/sys/kernel/scst_tgt/targets/qla2x00t/25:00:00:f0:98:87:92:f3/enabled
+
+The resulting overall SCST sysfs hierarchy with initiator
+25:00:00:f0:99:87:94:a3 connected will look like:
+
+/sys/kernel/scst_tgt
+|-- devices
+|   |-- blockio
+|   |   |-- blocksize
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_blockio
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size_mb
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   `-- usn
+|   |-- cdrom
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/2
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vcdrom
+|   |   |-- size_mb
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   `-- usn
+|   |-- disk1
+|   |   |-- blocksize
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/qla2x00t/25:00:00:f0:98:87:92:f3/ini_group/25:00:00:f0:99:87:94:a3/luns/0
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_fileio
+|   |   |-- nv_cache
+|   |   |-- o_direct
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size_mb
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   |-- usn
+|   |   `-- write_through
+|   |-- disk2
+|   |   |-- blocksize
+|   |   |-- exported
+|   |   |   `-- export0 -> ../../../targets/qla2x00t/25:00:00:f0:98:87:92:f3/ini_group/25:00:00:f0:99:87:94:a3/luns/1
+|   |   |-- filename
+|   |   |-- handler -> ../../handlers/vdisk_fileio
+|   |   |-- nv_cache
+|   |   |-- o_direct
+|   |   |-- read_only
+|   |   |-- removable
+|   |   |-- resync_size
+|   |   |-- size_mb
+|   |   |-- t10_dev_id
+|   |   |-- type
+|   |   |-- usn
+|   |   `-- write_through
+|   `-- nullio
+|       |-- blocksize
+|       |-- exported
+|       |   `-- export0 -> ../../../targets/qla2x00t/25:00:00:f0:98:87:92:f3/luns/1
+|       |-- handler -> ../../handlers/vdisk_nullio
+|       |-- read_only
+|       |-- removable
+|       |-- size_mb
+|       |-- t10_dev_id
+|       |-- type
+|       `-- usn
+|-- handlers
+|   |-- vcdrom
+|   |   |-- cdrom -> ../../devices/cdrom
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   |-- vdisk_blockio
+|   |   |-- blockio -> ../../devices/blockio
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   |-- vdisk_fileio
+|   |   |-- disk1 -> ../../devices/disk1
+|   |   |-- disk2 -> ../../devices/disk2
+|   |   |-- mgmt
+|   |   |-- trace_level
+|   |   `-- type
+|   `-- vdisk_nullio
+|       |-- mgmt
+|       |-- nullio -> ../../devices/nullio
+|       |-- trace_level
+|       `-- type
+|-- sgv
+|   |-- global_stats
+|   |-- sgv
+|   |   `-- stats
+|   |-- sgv-clust
+|   |   `-- stats
+|   `-- sgv-dma
+|       `-- stats
+|-- targets
+|   `-- qla2x00t
+|       |-- 25:00:00:f0:98:87:92:f3
+|       |   |-- enabled
+|       |   |-- explicit_confirmation
+|       |   |-- host -> ../../../../../class/scsi_host/host4
+|       |   |-- ini_group
+|       |   |   |-- 25:00:00:f0:99:87:94:a3
+|       |   |   |   |-- initiators
+|       |   |   |   |   |-- 25:00:00:f0:99:87:94:a3
+|       |   |   |   |   `-- mgmt
+|       |   |   |   `-- luns
+|       |   |   |       |-- 0
+|       |   |   |       |   |-- device -> ../../../../../../../devices/disk1
+|       |   |   |       |   `-- read_only
+|       |   |   |       |-- 1
+|       |   |   |       |   |-- device -> ../../../../../../../devices/disk2
+|       |   |   |       |   `-- read_only
+|       |   |   |       `-- mgmt
+|       |   |   `-- mgmt
+|       |   |-- luns
+|       |   |   |-- 0
+|       |   |   |   |-- device -> ../../../../../devices/blockio
+|       |   |   |   `-- read_only
+|       |   |   |-- 1
+|       |   |   |   |-- device -> ../../../../../devices/nullio
+|       |   |   |   `-- read_only
+|       |   |   |-- 2
+|       |   |   |   |-- device -> ../../../../../devices/cdrom
+|       |   |   |   `-- read_only
+|       |   |   `-- mgmt
+|       |   |-- rel_tgt_id
+|       |   `-- sessions
+|       |       `-- 25:00:00:f0:99:87:94:a3
+|       |           |-- active_commands
+|       |           |-- commands
+|       |           |-- initiator_name
+|       |           `-- luns -> ../../ini_group/25:00:00:f0:99:87:94:a3/luns
+|       |-- trace_level
+|       `-- version
+|-- threads
+|-- trace_level
+`-- version
+
+
 Credits
 -------
 
@@ -162,6 +425,6 @@ initiator driver.
 WWN-based authentification, a lot of useful suggestions, bug reports and
 help in debugging.
 
- * Ming Zhang <mingz@ele.uri.edu> for his fixes.
+ * Ming Zhang <mingz@ele.uri.edu> for fixes.
 
 Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net
index 6a113aa..2351a67 100644 (file)
@@ -1,9 +1,13 @@
 Known issues and unimplemented features
 ---------------------------------------
 
+ - Allow to set port and node names through sysfs.
  - Complete NPIV support. Particularly, SCST core should see all the virtual
    targets and provide separate target-oriented access control for them.
 
+ - Minor "ToDo"'s spread in the code.
+
  - If a Linux initiator asks for devices using INQUIRY command too soon
    before the controller on the 23xx target is fully initialized in the
    target mode, the initiator could receive garbage devices and the
@@ -15,8 +19,7 @@ Known issues and unimplemented features
    "unknown or no device type", but the SCSI mid-level treats such
    devices as real and adds them to the system. To avoid it, try to
    leave the target enough time for initialization (some 10th seconds).
-
- - Minor "ToDo"'s spread in the code.
+   With the latest firmware that might be fixed.
 
  - On 23xx if on a tape with block size 0 we write block with size X
    and then read it with bs <X the tape skips all blocks with size X
index 4fb7bd7..b083a92 100644 (file)
@@ -140,7 +140,11 @@ static unsigned long q2t_trace_flag = Q2T_DEFAULT_LOG_FLAGS;
 #endif
 
 static struct scst_tgt_template tgt2x_template = {
+#ifdef CONFIG_SCST_PROC
        .name = "qla2x00tgt",
+#else
+       .name = "qla2x00t",
+#endif
        .sg_tablesize = 0,
        .use_clustering = 1,
 #ifdef CONFIG_QLA_TGT_DEBUG_WORK_IN_THREAD
@@ -256,7 +260,7 @@ static inline int q2t_issue_marker(scsi_qla_host_t *ha, int ha_locked)
        if (unlikely(ha->marker_needed != 0)) {
                int rc = qla2x00_issue_marker(ha, ha_locked);
                if (rc != QLA_SUCCESS) {
-                       PRINT_ERROR("qla2x00tgt(%ld): issue_marker() "
+                       PRINT_ERROR("qla2x00t(%ld): issue_marker() "
                                "failed", ha->instance);
                }
                return rc;
@@ -364,7 +368,7 @@ static int q2t_unreg_sess(struct q2t_sess *sess)
        if (sess->deleted)
                list_del(&sess->del_list_entry);
 
-       PRINT_INFO("qla2x00tgt(%ld): %ssession for loop_id %d deleted",
+       PRINT_INFO("qla2x00t(%ld): %ssession for loop_id %d deleted",
                sess->tgt->ha->instance, sess->local ? "local " : "",
                sess->loop_id);
 
@@ -410,7 +414,7 @@ static int q2t_reset(scsi_qla_host_t *ha, void *iocb, int mcmd)
                                break;
 
                        default:
-                               PRINT_ERROR("qla2x00tgt(%ld): Not allowed "
+                               PRINT_ERROR("qla2x00t(%ld): Not allowed "
                                        "command %x in %s", ha->instance,
                                        mcmd, __func__);
                                sess = NULL;
@@ -476,7 +480,7 @@ static void q2t_alloc_session_done(struct scst_session *scst_sess,
                scsi_qla_host_t *ha = tgt->ha;
                unsigned long flags;
 
-               PRINT_INFO("qla2x00tgt(%ld): Session initialization failed",
+               PRINT_INFO("qla2x00t(%ld): Session initialization failed",
                           ha->instance);
 
                spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -576,7 +580,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport,
 
        sess = kzalloc(sizeof(*sess), GFP_KERNEL);
        if (sess == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): session allocation failed, "
+               PRINT_ERROR("qla2x00t(%ld): session allocation failed, "
                        "all commands from port %02x:%02x:%02x:%02x:"
                        "%02x:%02x:%02x:%02x will be refused", ha->instance,
                        fcport->port_name[0], fcport->port_name[1],
@@ -597,7 +601,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport,
 
        wwn_str = kmalloc(wwn_str_len, GFP_KERNEL);
        if (wwn_str == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): Allocation of wwn_str failed. "
+               PRINT_ERROR("qla2x00t(%ld): Allocation of wwn_str failed. "
                        "All commands from port %02x:%02x:%02x:%02x:%02x:%02x:"
                        "%02x:%02x will be refused", ha->instance,
                        fcport->port_name[0], fcport->port_name[1],
@@ -618,7 +622,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport,
                sess, q2t_alloc_session_done);
 
        if (sess->scst_sess == NULL) {
-               PRINT_CRIT_ERROR("qla2x00tgt(%ld): scst_register_session() "
+               PRINT_CRIT_ERROR("qla2x00t(%ld): scst_register_session() "
                        "failed for host %ld (wwn %s, loop_id %d), all "
                        "commands from it will be refused", ha->instance,
                        ha->host_no, wwn_str, fcport->loop_id);
@@ -632,7 +636,7 @@ static struct q2t_sess *q2t_create_sess(scsi_qla_host_t *ha, fc_port_t *fcport,
        tgt->sess_count++;
        spin_unlock_irq(&ha->hardware_lock);
 
-       PRINT_INFO("qla2x00tgt(%ld): %ssession for wwn %s (loop_id %d, "
+       PRINT_INFO("qla2x00t(%ld): %ssession for wwn %s (loop_id %d, "
                "s_id %x:%x:%x, confirmed completion %ssupported) added",
                ha->instance, local ? "local " : "", wwn_str, fcport->loop_id,
                sess->s_id.b.al_pa, sess->s_id.b.area, sess->s_id.b.domain,
@@ -685,7 +689,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport)
                        list_del(&sess->del_list_entry);
                        sess->deleted = 0;
 
-                       PRINT_INFO("qla2x00tgt(%ld): session for port %02x:"
+                       PRINT_INFO("qla2x00t(%ld): session for port %02x:"
                                "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) "
                                "reappeared", ha->instance, fcport->port_name[0],
                                fcport->port_name[1], fcport->port_name[2],
@@ -694,7 +698,7 @@ static void q2t_fc_port_added(scsi_qla_host_t *ha, fc_port_t *fcport)
                                fcport->port_name[7], sess->loop_id);
                        TRACE_MGMT_DBG("Appeared sess %p", sess);
                } else if (sess->local) {
-                       TRACE(TRACE_MGMT, "qla2x00tgt(%ld): local session for "
+                       TRACE(TRACE_MGMT, "qla2x00t(%ld): local session for "
                                "port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x "
                                "(loop ID %d) became global", ha->instance,
                                fcport->port_name[0], fcport->port_name[1],
@@ -750,7 +754,7 @@ static void q2t_fc_port_deleted(scsi_qla_host_t *ha, fc_port_t *fcport)
                list_add_tail(&sess->del_list_entry, &tgt->del_sess_list);
                sess->deleted = 1;
 
-               PRINT_INFO("qla2x00tgt(%ld): %ssession for port %02x:%02x:%02x:"
+               PRINT_INFO("qla2x00t(%ld): %ssession for port %02x:%02x:%02x:"
                        "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for "
                        "deletion in %d secs", ha->instance,
                        sess->local ? "local " : "",
@@ -897,7 +901,7 @@ static void q2x_modify_command_count(scsi_qla_host_t *ha, int cmd_count,
 
        pkt = (modify_lun_entry_t *)qla2x00_req_pkt(ha);
        if (pkt == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -952,7 +956,7 @@ static void q2x_send_notify_ack(scsi_qla_host_t *ha, notify_entry_t *iocb,
 
        ntfy = (nack_entry_t *)qla2x00_req_pkt(ha);
        if (ntfy == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -1016,7 +1020,7 @@ static void q24_send_abts_resp(scsi_qla_host_t *ha,
 
        resp = (abts24_resp_entry_t *)qla2x00_req_pkt(ha);
        if (resp == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -1093,7 +1097,7 @@ static void q24_retry_term_exchange(scsi_qla_host_t *ha,
 
        ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha);
        if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -1140,7 +1144,7 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts)
        TRACE_ENTRY();
 
        if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) {
-               PRINT_ERROR("qla2x00tgt(%ld): ABTS: Abort Sequence not "
+               PRINT_ERROR("qla2x00t(%ld): ABTS: Abort Sequence not "
                        "supported", ha->instance);
                goto out_err;
        }
@@ -1148,19 +1152,19 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts)
        tag = abts->exchange_addr_to_abort;
 
        if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) {
-               TRACE_MGMT_DBG("qla2x00tgt(%ld): ABTS: Unknown Exchange "
+               TRACE_MGMT_DBG("qla2x00t(%ld): ABTS: Unknown Exchange "
                        "Address received", ha->instance);
                goto out_err;
        }
 
-       TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task abort (s_id=%x:%x:%x, "
+       TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort (s_id=%x:%x:%x, "
                "tag=%d, param=%x)", ha->instance, abts->fcp_hdr_le.s_id[0],
                abts->fcp_hdr_le.s_id[1], abts->fcp_hdr_le.s_id[2], tag,
                le32_to_cpu(abts->fcp_hdr_le.parameter));
 
        sess = q2t_find_sess_by_s_id_le(ha->tgt, abts->fcp_hdr_le.s_id);
        if (sess == NULL) {
-               TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task abort for unexisting "
+               TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort for unexisting "
                        "session", ha->instance);
                ha->tgt->tm_to_unknown = 1;
                goto out_err;
@@ -1179,7 +1183,7 @@ static void q24_handle_abts(scsi_qla_host_t *ha, abts24_recv_entry_t *abts)
        rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag,
                SCST_ATOMIC, mcmd);
        if (rc != 0) {
-               PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_tag() failed: %d",
+               PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d",
                            ha->instance, rc);
                goto out_err_free;
        }
@@ -1216,7 +1220,7 @@ static void q24_send_task_mgmt_ctio(scsi_qla_host_t *ha,
 
        ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha);
        if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -1268,7 +1272,7 @@ static void q24_send_notify_ack(scsi_qla_host_t *ha,
 
        nack = (nack24xx_entry_t *)qla2x00_req_pkt(ha);
        if (nack == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -1409,7 +1413,7 @@ out:
        return res;
 
 out_err:
-       PRINT_ERROR("qla2x00tgt(%ld): PCI mapping failed: sg_cnt=%d",
+       PRINT_ERROR("qla2x00t(%ld): PCI mapping failed: sg_cnt=%d",
                prm->tgt->ha->instance, prm->cmd->sg_cnt);
        res = -1;
        goto out;
@@ -1801,7 +1805,7 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd,
        TRACE_ENTRY();
 
        if (unlikely(cmd->aborted)) {
-               TRACE_MGMT_DBG("qla2x00tgt(%ld): terminating exchange "
+               TRACE_MGMT_DBG("qla2x00t(%ld): terminating exchange "
                        "for aborted cmd=%p (scst_cmd=%p, tag=%d)",
                        ha->instance, cmd, scst_cmd, cmd->tag);
 
@@ -2180,7 +2184,7 @@ static void q24_init_ctio_ret_entry(ctio7_status0_entry_t *ctio,
                if (unlikely((prm->sense_buffer_len % 4) != 0)) {
                        static int q;
                        if (q < 10) {
-                               PRINT_INFO("qla2x00tgt(%ld): %d bytes of sense "
+                               PRINT_INFO("qla2x00t(%ld): %d bytes of sense "
                                        "lost", prm->tgt->ha->instance,
                                        prm->sense_buffer_len % 4);
                                q++;
@@ -2421,7 +2425,7 @@ static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
 
        ctio = (ctio_ret_entry_t *)qla2x00_req_pkt(ha);
        if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out_unlock;
        }
@@ -2430,7 +2434,7 @@ static void q2x_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
        ctio->entry_count = 1;
        if (cmd != NULL) {
                if (cmd->state < Q2T_STATE_PROCESSED) {
-                       PRINT_ERROR("qla2x00tgt(%ld): Terminating cmd %p with "
+                       PRINT_ERROR("qla2x00t(%ld): Terminating cmd %p with "
                                "incorrect state %d", ha->instance, cmd,
                                cmd->state);
                } else
@@ -2494,7 +2498,7 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
 
        ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha);
        if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out_unlock;
        }
@@ -2504,7 +2508,7 @@ static void q24_send_term_exchange(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
        if (cmd != NULL) {
                ctio->common.nport_handle = cmd->loop_id;
                if (cmd->state < Q2T_STATE_PROCESSED) {
-                       PRINT_ERROR("qla2x00tgt(%ld): Terminating cmd %p with "
+                       PRINT_ERROR("qla2x00t(%ld): Terminating cmd %p with "
                                "incorrect state %d", ha->instance, cmd,
                                 cmd->state);
                } else
@@ -2584,11 +2588,11 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
 
        tgt->ctio_srr_id++;
 
-       TRACE_MGMT_DBG("qla2x00tgt(%ld): CTIO with SRR "
+       TRACE_MGMT_DBG("qla2x00t(%ld): CTIO with SRR "
                "status received", ha->instance);
 
        if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): SRR CTIO, "
+               PRINT_ERROR("qla2x00t(%ld): SRR CTIO, "
                        "but ctio is NULL", ha->instance);
                res = -EINVAL;
                goto out;
@@ -2617,7 +2621,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
                                TRACE_MGMT_DBG("%s", "Scheduling srr work");
                                schedule_work(&tgt->srr_work);
                        } else {
-                               PRINT_ERROR("qla2x00tgt(%ld): imm_srr_id "
+                               PRINT_ERROR("qla2x00t(%ld): imm_srr_id "
                                        "== ctio_srr_id (%d), but there is no "
                                        "corresponding SRR IMM, deleting CTIO "
                                        "SRR %p", ha->instance, tgt->ctio_srr_id,
@@ -2633,7 +2637,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd,
                spin_unlock(&tgt->srr_lock);
        } else {
                struct srr_imm *ti;
-               PRINT_CRIT_ERROR("qla2x00tgt(%ld): Unable to "
+               PRINT_CRIT_ERROR("qla2x00t(%ld): Unable to "
                    "allocate SRR CTIO entry", ha->instance);
                spin_lock(&tgt->srr_lock);
                list_for_each_entry_safe(imm, ti, &tgt->srr_imm_list,
@@ -2722,13 +2726,13 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle,
                }
                /* handle-1 is actually used */
                if (unlikely(handle > MAX_OUTSTANDING_COMMANDS)) {
-                       PRINT_ERROR("qla2x00tgt(%ld): Wrong handle %x "
+                       PRINT_ERROR("qla2x00t(%ld): Wrong handle %x "
                                "received", ha->instance, handle);
                        goto out;
                }
                cmd = q2t_get_cmd(ha, handle);
                if (unlikely(cmd == NULL)) {
-                       PRINT_WARNING("qla2x00tgt(%ld): Suspicious: unable to "
+                       PRINT_WARNING("qla2x00t(%ld): Suspicious: unable to "
                                   "find the command with handle %x",
                                   ha->instance, handle);
                        goto out;
@@ -2741,7 +2745,7 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle,
 
                if (IS_FWI2_CAPABLE(ha)) {
                        /* We can't get loop ID from CTIO7 */
-                       PRINT_ERROR("qla2x00tgt(%ld): Wrong CTIO received: "
+                       PRINT_ERROR("qla2x00t(%ld): Wrong CTIO received: "
                                "QLA24xx doesn't support NULL handles",
                                ha->instance);
                        goto out;
@@ -2753,7 +2757,7 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle,
 
                sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id);
                if (sess == NULL) {
-                       PRINT_WARNING("qla2x00tgt(%ld): Suspicious: "
+                       PRINT_WARNING("qla2x00t(%ld): Suspicious: "
                                   "ctio_completion for non-existing session "
                                   "(loop_id %d, tag %d)",
                                   ha->instance, loop_id, tag);
@@ -2762,7 +2766,7 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle,
 
                scst_cmd = scst_find_cmd_by_tag(sess->scst_sess, tag);
                if (scst_cmd == NULL) {
-                       PRINT_WARNING("qla2x00tgt(%ld): Suspicious: unable to "
+                       PRINT_WARNING("qla2x00t(%ld): Suspicious: unable to "
                             "find the command with tag %d (loop_id %d)",
                             ha->instance, tag, loop_id);
                        goto out;
@@ -2824,7 +2828,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle,
                case CTIO_INVALID_RX_ID:
                        /* They are OK */
                        TRACE(TRACE_MINOR_AND_MGMT_DBG,
-                               "qla2x00tgt(%ld): CTIO with "
+                               "qla2x00t(%ld): CTIO with "
                                "status %#x received, state %x, scst_cmd %p, "
                                "op %x (LIP_RESET=e, ABORTED=2, TARGET_RESET=17, "
                                "TIMEOUT=b, INVALID_RX_ID=8)", ha->instance,
@@ -2833,7 +2837,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle,
 
                case CTIO_PORT_LOGGED_OUT:
                case CTIO_PORT_UNAVAILABLE:
-                       PRINT_INFO("qla2x00tgt(%ld): CTIO with PORT LOGGED "
+                       PRINT_INFO("qla2x00t(%ld): CTIO with PORT LOGGED "
                                "OUT (29) or PORT UNAVAILABLE (28) status %x "
                                "received (state %x, scst_cmd %p, op %x)",
                                ha->instance, status, cmd->state, scst_cmd,
@@ -2847,7 +2851,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle,
                                goto out;
 
                default:
-                       PRINT_ERROR("qla2x00tgt(%ld): CTIO with error status "
+                       PRINT_ERROR("qla2x00t(%ld): CTIO with error status "
                                "0x%x received (state %x, scst_cmd %p, op %x)",
                                ha->instance, status, cmd->state, scst_cmd,
                                scst_cmd->cdb[0]);
@@ -2885,7 +2889,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle,
                TRACE_MGMT_DBG("Aborted command %p (tag %d) finished", cmd,
                        cmd->tag);
        } else {
-               PRINT_ERROR("qla2x00tgt(%ld): A command in state (%d) should "
+               PRINT_ERROR("qla2x00t(%ld): A command in state (%d) should "
                        "not return a CTIO complete", ha->instance, cmd->state);
        }
 
@@ -2941,7 +2945,7 @@ static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd)
                                    SCST_ATOMIC);
 
        if (cmd->scst_cmd == NULL) {
-               PRINT_ERROR("%s", "qla2x00tgt: scst_rx_cmd() failed");
+               PRINT_ERROR("%s", "qla2x00t: scst_rx_cmd() failed");
                res = -EFAULT;
                goto out;
        }
@@ -2979,7 +2983,7 @@ static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd)
                cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_UNTAGGED;
                break;
        default:
-               PRINT_ERROR("qla2x00tgt: unknown task code %x, use "
+               PRINT_ERROR("qla2x00t: unknown task code %x, use "
                        "ORDERED instead", atio->task_codes);
                cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
                break;
@@ -3017,7 +3021,7 @@ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd)
                atio->fcp_cmnd.cdb, Q2T_MAX_CDB_LEN, SCST_ATOMIC);
 
        if (cmd->scst_cmd == NULL) {
-               PRINT_ERROR("%s", "qla2x00tgt: scst_rx_cmd() failed");
+               PRINT_ERROR("%s", "qla2x00t: scst_rx_cmd() failed");
                res = -EFAULT;
                goto out;
        }
@@ -3054,7 +3058,7 @@ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd)
                cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_UNTAGGED;
                break;
        default:
-               PRINT_ERROR("qla2x00tgt: unknown task code %x, use "
+               PRINT_ERROR("qla2x00t: unknown task code %x, use "
                        "ORDERED instead", atio->fcp_cmnd.task_attr);
                cmd->scst_cmd->queue_type = SCST_CMD_QUEUE_ORDERED;
                break;
@@ -3129,7 +3133,7 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio)
                atio7_entry_t *a = (atio7_entry_t *)atio;
                sess = q2t_find_sess_by_s_id(tgt, a->fcp_hdr.s_id);
                if (unlikely(sess == NULL)) {
-                       TRACE_MGMT_DBG("qla2x00tgt(%ld): Unable to find "
+                       TRACE_MGMT_DBG("qla2x00t(%ld): Unable to find "
                                "wwn login (s_id %x:%x:%x), trying to create "
                                "it manually", ha->instance,
                                a->fcp_hdr.s_id[0], a->fcp_hdr.s_id[1],
@@ -3140,7 +3144,7 @@ static int q2t_send_cmd_to_scst(scsi_qla_host_t *ha, atio_t *atio)
                sess = q2t_find_sess_by_loop_id(tgt,
                        GET_TARGET_ID(ha, (atio_entry_t *)atio));
                if (unlikely(sess == NULL)) {
-                       TRACE_MGMT_DBG("qla2x00tgt(%ld): Unable to find "
+                       TRACE_MGMT_DBG("qla2x00t(%ld): Unable to find "
                                "wwn login (loop_id=%d), trying to create it "
                                "manually", ha->instance,
                                GET_TARGET_ID(ha, (atio_entry_t *)atio));
@@ -3201,7 +3205,7 @@ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun,
 
        mcmd = mempool_alloc(q2t_mgmt_cmd_mempool, GFP_ATOMIC);
        if (mcmd == NULL) {
-               PRINT_CRIT_ERROR("qla2x00tgt(%ld): Allocation of management "
+               PRINT_CRIT_ERROR("qla2x00t(%ld): Allocation of management "
                        "command failed, some commands and their data could "
                        "leak", sess->tgt->ha->instance);
                res = -ENOMEM;
@@ -3274,14 +3278,14 @@ static int q2t_issue_task_mgmt(struct q2t_sess *sess, uint8_t *lun,
                break;
 
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): Unknown task mgmt fn 0x%x",
+               PRINT_ERROR("qla2x00t(%ld): Unknown task mgmt fn 0x%x",
                            sess->tgt->ha->instance, fn);
                rc = -1;
                break;
        }
 
        if (rc != 0) {
-               PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_lun() failed: %d",
+               PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_lun() failed: %d",
                            sess->tgt->ha->instance, rc);
                res = -EFAULT;
                goto out_free;
@@ -3332,7 +3336,7 @@ static int q2t_handle_task_mgmt(scsi_qla_host_t *ha, void *iocb)
        }
 
        if (sess == NULL) {
-               TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task mgmt fn 0x%x for "
+               TRACE(TRACE_MGMT, "qla2x00t(%ld): task mgmt fn 0x%x for "
                        "non-existant session", ha->instance, fn);
                tgt->tm_to_unknown = 1;
                res = -ESRCH;
@@ -3362,7 +3366,7 @@ static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb)
 
        sess = q2t_find_sess_by_loop_id(ha->tgt, loop_id);
        if (sess == NULL) {
-               TRACE(TRACE_MGMT, "qla2x00tgt(%ld): task abort for unexisting "
+               TRACE(TRACE_MGMT, "qla2x00t(%ld): task abort for unexisting "
                        "session", ha->instance);
                ha->tgt->tm_to_unknown = 1;
                res = -EFAULT;
@@ -3384,7 +3388,7 @@ static int q2t_abort_task(scsi_qla_host_t *ha, notify_entry_t *iocb)
        rc = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK, tag,
                SCST_ATOMIC, mcmd);
        if (rc != 0) {
-               PRINT_ERROR("qla2x00tgt(%ld): scst_rx_mgmt_fn_tag() failed: %d",
+               PRINT_ERROR("qla2x00t(%ld): scst_rx_mgmt_fn_tag() failed: %d",
                            ha->instance, rc);
                res = -EFAULT;
                goto out_free;
@@ -3432,7 +3436,7 @@ static int q24_handle_els(scsi_qla_host_t *ha, notify24xx_entry_t *iocb)
        }
 
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): Unsupported ELS command %x "
+               PRINT_ERROR("qla2x00t(%ld): Unsupported ELS command %x "
                        "received", ha->instance, iocb->status_subcode);
                res = q2t_reset(ha, iocb, Q2T_NEXUS_LOSS_SESS);
                break;
@@ -3484,7 +3488,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
                }
        }
        if (first_sg == -1) {
-               PRINT_ERROR("qla2x00tgt(%ld): Wrong offset %d, buf length %d",
+               PRINT_ERROR("qla2x00t(%ld): Wrong offset %d, buf length %d",
                        cmd->tgt->ha->instance, offset, cmd->bufflen);
                res = -EINVAL;
                goto out;
@@ -3497,7 +3501,7 @@ static int q2t_cut_cmd_data_head(struct q2t_cmd *cmd, unsigned int offset)
 
        sg = kmalloc(cnt * sizeof(sg[0]), GFP_KERNEL);
        if (sg == NULL) {
-               PRINT_CRIT_ERROR("qla2x00tgt(%ld): Unable to allocate cut "
+               PRINT_CRIT_ERROR("qla2x00t(%ld): Unable to allocate cut "
                        "SG (len %zd)", cmd->tgt->ha->instance,
                        cnt * sizeof(sg[0]));
                res = -ENOMEM;
@@ -3574,7 +3578,7 @@ static inline int q2t_srr_adjust_data(struct q2t_cmd *cmd,
        *xmit_type = Q2T_XMIT_ALL;
 
        if (rel_offs < 0) {
-               PRINT_ERROR("qla2x00tgt(%ld): SRR rel_offs (%d) "
+               PRINT_ERROR("qla2x00t(%ld): SRR rel_offs (%d) "
                        "< 0", cmd->tgt->ha->instance, rel_offs);
                res = -1;
        } else if (rel_offs == cmd->bufflen)
@@ -3618,7 +3622,7 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio,
                        spin_unlock_irq(&ha->hardware_lock);
                        __q24_xmit_response(cmd, xmit_type);
                } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): SRR for in data for cmd "
+                       PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd "
                                "without them (tag %d, SCSI status %d), "
                                "reject", ha->instance, cmd->tag,
                                scst_cmd_get_status(cmd->scst_cmd));
@@ -3641,7 +3645,7 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio,
                        spin_unlock_irq(&ha->hardware_lock);
                        __q2t_rdy_to_xfer(cmd);
                } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): SRR for out data for cmd "
+                       PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd "
                                "without them (tag %d, SCSI status %d), "
                                "reject", ha->instance, cmd->tag,
                                scst_cmd_get_status(cmd->scst_cmd));
@@ -3649,7 +3653,7 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio,
                }
                break;
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): Unknown srr_ui value %x",
+               PRINT_ERROR("qla2x00t(%ld): Unknown srr_ui value %x",
                        ha->instance, ntfy->srr_ui);
                goto out_unmap_reject;
        }
@@ -3716,7 +3720,7 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio,
                        spin_unlock_irq(&ha->hardware_lock);
                        __q2x_xmit_response(cmd, xmit_type);
                } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): SRR for in data for cmd "
+                       PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd "
                                "without them (tag %d, SCSI status %d), "
                                "reject", ha->instance, cmd->tag,
                                scst_cmd_get_status(cmd->scst_cmd));
@@ -3739,7 +3743,7 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio,
                        spin_unlock_irq(&ha->hardware_lock);
                        __q2t_rdy_to_xfer(cmd);
                } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): SRR for out data for cmd "
+                       PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd "
                                "without them (tag %d, SCSI status %d), "
                                "reject", ha->instance, cmd->tag,
                                scst_cmd_get_status(cmd->scst_cmd));
@@ -3747,7 +3751,7 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio,
                }
                break;
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): Unknown srr_ui value %x",
+               PRINT_ERROR("qla2x00t(%ld): Unknown srr_ui value %x",
                        ha->instance, ntfy->srr_ui);
                goto out_unmap_reject;
        }
@@ -3829,7 +3833,7 @@ restart:
                        if (i->srr_id == sctio->srr_id) {
                                list_del(&i->srr_list_entry);
                                if (imm) {
-                                       PRINT_ERROR("qla2x00tgt(%ld): There must "
+                                       PRINT_ERROR("qla2x00t(%ld): There must "
                                          "be only one IMM SRR per CTIO SRR "
                                          "(IMM SRR %p, id %d, CTIO %p",
                                          ha->instance, i, i->srr_id, sctio);
@@ -3893,7 +3897,7 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb)
 
        tgt->imm_srr_id++;
 
-       TRACE(TRACE_MGMT, "qla2x00tgt(%ld): SRR received", ha->instance);
+       TRACE(TRACE_MGMT, "qla2x00t(%ld): SRR received", ha->instance);
 
        imm = kzalloc(sizeof(*imm), GFP_ATOMIC);
        if (imm != NULL) {
@@ -3920,7 +3924,7 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb)
                                TRACE_MGMT_DBG("%s", "Scheduling srr work");
                                schedule_work(&tgt->srr_work);
                        } else {
-                               TRACE(TRACE_MGMT, "qla2x00tgt(%ld): imm_srr_id "
+                               TRACE(TRACE_MGMT, "qla2x00t(%ld): imm_srr_id "
                                        "== ctio_srr_id (%d), but there is no "
                                        "corresponding SRR CTIO, deleting IMM "
                                        "SRR %p", ha->instance, tgt->ctio_srr_id,
@@ -3937,7 +3941,7 @@ static void q2t_prepare_srr_imm(scsi_qla_host_t *ha, void *iocb)
        } else {
                struct srr_ctio *ts;
 
-               PRINT_CRIT_ERROR("qla2x00tgt(%ld): Unable to allocate SRR IMM "
+               PRINT_CRIT_ERROR("qla2x00t(%ld): Unable to allocate SRR IMM "
                        "entry, SRR request will be rejected", ha->instance);
 
                /* IRQ is already OFF */
@@ -4061,7 +4065,7 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb)
                break;
 
        case IMM_NTFY_GLBL_LOGO:
-               PRINT_ERROR("qla2x00tgt(%ld): Link failure detected",
+               PRINT_ERROR("qla2x00t(%ld): Link failure detected",
                        ha->instance);
                /* I_T nexus loss */
                qla2x00_mark_all_devices_lost(ha, 1);
@@ -4070,7 +4074,7 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb)
                break;
 
        case IMM_NTFY_IOCB_OVERFLOW:
-               PRINT_ERROR("qla2x00tgt(%ld): Cannot provide requested "
+               PRINT_ERROR("qla2x00t(%ld): Cannot provide requested "
                        "capability (IOCB overflowed the immediate notify "
                        "resource count)", ha->instance);
                break;
@@ -4084,7 +4088,7 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb)
                break;
 
        case IMM_NTFY_RESOURCE:
-               PRINT_ERROR("qla2x00tgt(%ld): Out of resources, host %ld",
+               PRINT_ERROR("qla2x00t(%ld): Out of resources, host %ld",
                            ha->instance, ha->host_no);
                break;
 
@@ -4105,7 +4109,7 @@ static void q2t_handle_imm_notify(scsi_qla_host_t *ha, void *iocb)
                break;
 
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): Received unknown immediate "
+               PRINT_ERROR("qla2x00t(%ld): Received unknown immediate "
                        "notify status %x", ha->instance, status);
                break;
        }
@@ -4135,7 +4139,7 @@ static void q2x_send_busy(scsi_qla_host_t *ha, atio_entry_t *atio)
 
        ctio = (ctio_ret_entry_t *)qla2x00_req_pkt(ha);
        if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -4190,7 +4194,7 @@ static void q24_send_busy(scsi_qla_host_t *ha, atio7_entry_t *atio,
 
        ctio = (ctio7_status1_entry_t *)qla2x00_req_pkt(ha);
        if (ctio == NULL) {
-               PRINT_ERROR("qla2x00tgt(%ld): %s failed: unable to allocate "
+               PRINT_ERROR("qla2x00t(%ld): %s failed: unable to allocate "
                        "request packet", ha->instance, __func__);
                goto out;
        }
@@ -4254,7 +4258,7 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio)
        case ATIO_TYPE7:
                if (unlikely(atio->entry_count > 1) ||
                    unlikely(atio->fcp_cmnd.add_cdb_len != 0)) {
-                       PRINT_ERROR("qla2x00tgt(%ld): Multi entry ATIO7 IOCBs "
+                       PRINT_ERROR("qla2x00t(%ld): Multi entry ATIO7 IOCBs "
                                "(%d), ie with CDBs>16 bytes (%d), are not "
                                "supported", ha->instance, atio->entry_count,
                                atio->fcp_cmnd.add_cdb_len);
@@ -4274,7 +4278,7 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio)
                                sizeof(atio->fcp_cmnd.cdb));
                if (unlikely(atio->exchange_addr ==
                                ATIO_EXCHANGE_ADDRESS_UNKNOWN)) {
-                       TRACE(TRACE_OUT_OF_MEM, "qla2x00tgt(%ld): ATIO_TYPE7 "
+                       TRACE(TRACE_OUT_OF_MEM, "qla2x00t(%ld): ATIO_TYPE7 "
                                "received with UNKNOWN exchange address, "
                                "sending QUEUE_FULL", ha->instance);
                        q24_send_busy(ha, atio, SAM_STAT_TASK_SET_FULL);
@@ -4292,7 +4296,7 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio)
                                q24_send_term_exchange(ha, NULL, atio, 1);
 #endif
                        } else {
-                               PRINT_INFO("qla2x00tgt(%ld): Unable to send "
+                               PRINT_INFO("qla2x00t(%ld): Unable to send "
                                   "command to SCST, sending BUSY status",
                                   ha->instance);
                                q24_send_busy(ha, atio, SAM_STAT_BUSY);
@@ -4304,7 +4308,7 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio)
        {
                notify_entry_t *pkt = (notify_entry_t *)atio;
                if (unlikely(pkt->entry_status != 0)) {
-                       PRINT_ERROR("qla2x00tgt(%ld): Received ATIO packet %x "
+                       PRINT_ERROR("qla2x00t(%ld): Received ATIO packet %x "
                                "with error status %x", ha->instance,
                                pkt->entry_type, pkt->entry_status);
                        break;
@@ -4315,7 +4319,7 @@ static void q24_atio_pkt(scsi_qla_host_t *ha, atio7_entry_t *atio)
        }
 
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): Received unknown ATIO atio "
+               PRINT_ERROR("qla2x00t(%ld): Received unknown ATIO atio "
                     "type %x", ha->instance, atio->entry_type);
                break;
        }
@@ -4351,7 +4355,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
         */
 
        if (unlikely(pkt->entry_status != 0)) {
-               PRINT_ERROR("qla2x00tgt(%ld): Received response packet %x "
+               PRINT_ERROR("qla2x00t(%ld): Received response packet %x "
                     "with error status %x", ha->instance, pkt->entry_type,
                     pkt->entry_status);
                switch (pkt->entry_type) {
@@ -4396,7 +4400,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
                TRACE_BUFFER("Incoming ATIO packet data", atio,
                        REQUEST_ENTRY_SIZE);
                if (atio->status != __constant_cpu_to_le16(ATIO_CDB_VALID)) {
-                       PRINT_ERROR("qla2x00tgt(%ld): ATIO with error "
+                       PRINT_ERROR("qla2x00t(%ld): ATIO with error "
                                    "status %x received", ha->instance,
                                    le16_to_cpu(atio->status));
                        break;
@@ -4413,7 +4417,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
                                q2x_send_term_exchange(ha, NULL, atio, 1);
 #endif
                        } else {
-                               PRINT_INFO("qla2x00tgt(%ld): Unable to send "
+                               PRINT_INFO("qla2x00t(%ld): Unable to send "
                                        "command to SCST, sending BUSY status",
                                        ha->instance);
                                q2x_send_busy(ha, atio);
@@ -4461,12 +4465,12 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
                                RESPONSE_ENTRY_SIZE);
                        tgt->notify_ack_expected--;
                        if (entry->status != __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) {
-                               PRINT_ERROR("qla2x00tgt(%ld): NOTIFY_ACK "
+                               PRINT_ERROR("qla2x00t(%ld): NOTIFY_ACK "
                                            "failed %x", ha->instance,
                                            le16_to_cpu(entry->status));
                        }
                } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): Unexpected NOTIFY_ACK "
+                       PRINT_ERROR("qla2x00t(%ld): Unexpected NOTIFY_ACK "
                                    "received", ha->instance);
                }
                break;
@@ -4504,13 +4508,13 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
                                         */
                                        q24_retry_term_exchange(ha, entry);
                                } else
-                                       PRINT_ERROR("qla2x00tgt(%ld): ABTS_RESP_24XX "
+                                       PRINT_ERROR("qla2x00t(%ld): ABTS_RESP_24XX "
                                            "failed %x (subcode %x:%x)", ha->instance,
                                            entry->compl_status, entry->error_subcode1,
                                            entry->error_subcode2);
                        }
                } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): Unexpected ABTS_RESP_24XX "
+                       PRINT_ERROR("qla2x00t(%ld): Unexpected ABTS_RESP_24XX "
                                    "received", ha->instance);
                }
                break;
@@ -4530,12 +4534,12 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
                                  entry->command_count);
                        tgt->modify_lun_expected--;
                        if (entry->status != MODIFY_LUN_SUCCESS) {
-                               PRINT_ERROR("qla2x00tgt(%ld): MODIFY_LUN "
+                               PRINT_ERROR("qla2x00t(%ld): MODIFY_LUN "
                                            "failed %x", ha->instance,
                                            entry->status);
                        }
                } else {
-                       PRINT_ERROR("qla2x00tgt(%ld): Unexpected MODIFY_LUN "
+                       PRINT_ERROR("qla2x00t(%ld): Unexpected MODIFY_LUN "
                            "received", (ha != NULL) ? (long)ha->instance : -1);
                }
                break;
@@ -4555,7 +4559,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
                                "error: %#x", entry->status);
                        entry->status = ENABLE_LUN_SUCCESS;
                } else if (entry->status != ENABLE_LUN_SUCCESS) {
-                       PRINT_ERROR("qla2x00tgt(%ld): ENABLE_LUN "
+                       PRINT_ERROR("qla2x00t(%ld): ENABLE_LUN "
                                "failed %x", ha->instance, entry->status);
                        qla_clear_tgt_mode(ha);
                } /* else success */
@@ -4563,7 +4567,7 @@ static void q2t_response_pkt(scsi_qla_host_t *ha, response_t *pkt)
        }
 
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): Received unknown response pkt "
+               PRINT_ERROR("qla2x00t(%ld): Received unknown response pkt "
                     "type %x", ha->instance, pkt->entry_type);
                break;
        }
@@ -4945,7 +4949,7 @@ static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd)
                TRACE_MGMT_DBG("Force finishing aborted cmd %p (tag %d)",
                        cmd, cmd->tag);
        } else {
-               PRINT_ERROR("qla2x00tgt(%ld): A command in state (%d) should "
+               PRINT_ERROR("qla2x00t(%ld): A command in state (%d) should "
                        "not be HW pending", ha->instance, cmd->state);
                goto out_unlock;
        }
@@ -5008,7 +5012,7 @@ static int q2t_add_target(scsi_qla_host_t *ha)
        kfree(wwn);
 
        if (!tgt->scst_tgt) {
-               PRINT_ERROR("qla2x00tgt(%ld): scst_register() "
+               PRINT_ERROR("qla2x00t(%ld): scst_register() "
                            "failed for host %ld(%p)", ha->instance,
                            ha->host_no, ha);
                res = -ENOMEM;
@@ -5026,7 +5030,7 @@ static int q2t_add_target(scsi_qla_host_t *ha)
                        tgt->datasegs_per_cont = DATASEGS_PER_CONT_24XX;
        } else {
                if (ha->flags.enable_64bit_addressing) {
-                       PRINT_INFO("qla2x00tgt(%ld): 64 Bit PCI "
+                       PRINT_INFO("qla2x00t(%ld): 64 Bit PCI "
                                   "addressing enabled", ha->instance);
                        tgt->tgt_enable_64bit_addr = 1;
                        /* 3 is reserved */
@@ -5035,7 +5039,7 @@ static int q2t_add_target(scsi_qla_host_t *ha)
                        tgt->datasegs_per_cmd = DATASEGS_PER_COMMAND64;
                        tgt->datasegs_per_cont = DATASEGS_PER_CONT64;
                } else {
-                       PRINT_INFO("qla2x00tgt(%ld): Using 32 Bit "
+                       PRINT_INFO("qla2x00t(%ld): Using 32 Bit "
                                   "PCI addressing", ha->instance);
                        sg_tablesize =
                                QLA_MAX_SG32(ha->request_q_length - 3);
@@ -5071,7 +5075,7 @@ static int q2t_remove_target(scsi_qla_host_t *ha)
        TRACE_ENTRY();
 
        if ((ha->q2t_tgt == NULL) || (ha->tgt != NULL)) {
-               PRINT_ERROR("qla2x00tgt(%ld): Can't remove "
+               PRINT_ERROR("qla2x00t(%ld): Can't remove "
                        "existing target", ha->instance);
        }
 
@@ -5114,18 +5118,18 @@ static int q2t_host_action(scsi_qla_host_t *ha,
                fc_port_t *fcport;
 
                if (qla_tgt_mode_enabled(ha)) {
-                       PRINT_INFO("qla2x00tgt(%ld): Target mode already "
+                       PRINT_INFO("qla2x00t(%ld): Target mode already "
                                "enabled", ha->instance);
                        break;
                }
 
                if ((ha->q2t_tgt == NULL) || (ha->tgt != NULL)) {
-                       PRINT_ERROR("qla2x00tgt(%ld): Can't enable target mode "
+                       PRINT_ERROR("qla2x00t(%ld): Can't enable target mode "
                                "for not existing target", ha->instance);
                        break;
                }
 
-               PRINT_INFO("qla2x00tgt(%ld): Enabling target mode",
+               PRINT_INFO("qla2x00t(%ld): Enabling target mode",
                        ha->instance);
 
                spin_lock_irq(&ha->hardware_lock);
@@ -5143,12 +5147,12 @@ static int q2t_host_action(scsi_qla_host_t *ha,
 
        case DISABLE_TARGET_MODE:
                if (!qla_tgt_mode_enabled(ha)) {
-                       PRINT_INFO("qla2x00tgt(%ld): Target mode already "
+                       PRINT_INFO("qla2x00t(%ld): Target mode already "
                                "disabled", ha->instance);
                        break;
                }
 
-               PRINT_INFO("qla2x00tgt(%ld): Disabling target mode",
+               PRINT_INFO("qla2x00t(%ld): Disabling target mode",
                        ha->instance);
 
                sBUG_ON(ha->tgt == NULL);
@@ -5157,7 +5161,7 @@ static int q2t_host_action(scsi_qla_host_t *ha,
                break;
 
        default:
-               PRINT_ERROR("qla2x00tgt(%ld): %s: unsupported action %d",
+               PRINT_ERROR("qla2x00t(%ld): %s: unsupported action %d",
                        ha->instance, __func__, action);
                res = -EINVAL;
                break;
index ded18a5..71934da 100644 (file)
@@ -86,7 +86,7 @@ will loose this performance benefit.
 3. readahead-2.6.X.patch. This patch fixes problem in Linux readahead
 subsystem and greatly improves performance for software RAIDs. See
 http://sourceforge.net/mailarchive/forum.php?thread_name=a0272b440906030714g67eabc5k8f847fb1e538cc62%40mail.gmail.com&forum_name=scst-devel
-thread for more details.
+thread for more details. It is included in the mainstream kernel 2.6.33.
 
 4. readahead-context-2.6.X.patch. This is backported from 2.6.31 version
 of the context readahead patch http://lkml.org/lkml/2009/4/12/9, big
@@ -176,7 +176,7 @@ Usage in failover mode
 ----------------------
 
 It is recommended to use TEST UNIT READY ("tur") command to check if
-SCST target is alive.
+SCST target is alive in MPIO configurations.
 
 
 Device handlers
@@ -311,7 +311,8 @@ in/out in Makefile:
 
  - CONFIG_SCST_MEASURE_LATENCY - if defined, provides in /proc/scsi_tgt/latency
    file average commands processing latency. You can clear already
-   measured results by writing 0 in this file. Note, you need a
+   measured results by writing 0 in this file. For the sysfs build you
+   can find those results in /sys/kernel/scst_tgt and below. Note, you need a
    non-preemptible kernel to have correct results.
 
 HIGHMEM kernel configurations are fully supported, but not recommended
@@ -347,12 +348,14 @@ Module scst supports the following parameters:
    default it is approximately TotalMem/4.
 
 
-SCST /proc commands
----------------------
+SCST /proc interface
+--------------------
 
 For communications with user space programs SCST provides proc-based
-interface in /proc/scsi_tgt directory. It contains the following
-entries:
+interface in /proc/scsi_tgt directory. This interface is available in
+the procfs build only. Starting from version 2.0.0 it is obsolete and
+will be removed in one of the next versions. It contains the following
+entries.
 
   - "help" file, which provides online help for SCST commands
 
@@ -406,8 +409,8 @@ will assign device handler "dev_disk" to real device sitting on host 1,
 channel 0, ID 1, LUN 0.
 
 
-Access and devices visibility management (LUN masking)
-------------------------------------------------------
+Access and devices visibility management (LUN masking) - /proc interface
+------------------------------------------------------------------------
 
 Access and devices visibility management allows for an initiator or
 group of initiators to see different devices with different LUNs
@@ -581,11 +584,325 @@ initiators or add new devices to old groups, but not altering existing
 LUNs in them.
 
 
+SCST sysfs interface
+--------------------
+
+Starting from 2.0.0 SCST has sysfs interface. You can switch to it by
+running "make disable_proc". To switch back to the procfs interface you
+should run "make enable_proc".
+
+Root of SCST sysfs interface is /sys/kernel/scst_tgt. It has the
+following entries:
+
+ - devices - this is a root subdirectory for all SCST devices
+
+ - handlers - this is a root subdirectory for all SCST dev handlers
+
+ - sgv - this is a root subdirectory for all SCST SGV caches
+
+ - targets - this is a root subdirectory for all SCST targets
+
+ - threads - allows to read and set number of global SCST I/O threads.
+   Those threads used with async. dev handlers, for instance, vdisk
+   BLOCKIO or NULLIO.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - version - read-only attribute, which allows to see version of
+   SCST and enabled optional features.
+
+Each SCST sysfs file (attribute) can contain in the last line mark
+"[key]". It is automatically added mark used to allow scstadmin to see
+which attributes it should save in the config file. You can ignore it.
+
+"Devices" subdirectory contains subdirectories for each SCST devices.
+
+Content of each device's subdirectory is dev handler specific. See
+documentation for your dev handlers for more info about it as well as
+SysfsRules file for more info about common to all dev handlers rules.
+Standard SCST dev handlers have at least the following common entries:
+
+ - exported - subdirectory containing links to all LUNs where this
+   device was exported.
+
+ - handler - if dev handler determined for this device, this link points
+   to it. The handler can be not set for pass-through devices.
+
+ - type - SCSI type of this device
+See below for more information about other entries of this subdirectory
+of the standard SCST dev handlers.
+
+"Handlers" subdirectory contains subdirectories for each SCST dev
+handler.
+
+Content of each handler's subdirectory is dev handler specific. See
+documentation for your dev handlers for more info about it as well as
+SysfsRules file for more info about common to all dev handlers rules.
+Standard SCST dev handlers have at least the following common entries:
+
+ - pass_through - if exists, it contains 1 and this dev handler is a
+   pass-through dev handler.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - type - SCSI type of devices served by this dev handler.
+
+See below for more information about other entries of this subdirectory
+of the standard SCST dev handlers.
+
+"Sgv" subdirectory contains statistic information of SCST SGV caches. It
+has the following entries:
+
+ - None, one or more subdirectories for each existing SGV cache.
+ - global_stats - file containing global SGV caches statistics.
+
+Each SGV cache's subdirectory has the following item:
+
+ - stats - file containing statistics for this SGV caches.
+
+"Targets" subdirectory contains subdirectories for each SCST target.
+
+Content of each target's subdirectory is target specific. See
+documentation for your target for more info about it as well as
+SysfsRules file for more info about common to all dev handlers rules.
+Every target should have at least the following entries:
+
+ - ini_group - subdirectory, which contains and allows to define
+   initiator-oriented access control information, see below.
+
+ - luns - subdirectory, which contains list of available LUNs in the
+   target-oriented access control and allows to define it, see below.
+
+ - sessions - subdirectory containing connected to this target sessions.
+
+ - enabled - using this attribute you can enable or disable this target/
+   It allows to finish configuring it before it starts accepting new
+   connections. 0 by default.
+
+ - 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
+   commands. This identifier must be unique among all SCST targets, but
+   for convenience SCST allows disabled targets to have not unique
+   rel_tgt_id. In this case SCST will not allow to enable this target
+   until rel_tgt_id becomes unique. This attribute initialized unique by
+   SCST by default.
+
+Subdirectory "sessions" contains one subdirectory for each connected
+session with name equal to name of the connected initiator.
+
+Each session subdirectory contains the following entries:
+
+ - initiator_name - contains initiator name
+
+ - force_close - optional write-only attribute, which allows to force
+   close this session.
+
+ - active_commands - contains number of active, i.e. not yet or being
+   executed, SCSI commands in this session.
+
+ - commands - contains overall number of SCSI commands in this session.
+
+ - other target driver specific attributes and subdirectories.
+
+See below description of the VDISK's sysfs interface for samples.
+
+
+Access and devices visibility management (LUN masking) - sysfs interface
+------------------------------------------------------------------------
+
+Access and devices visibility management allows for an initiator or
+group of initiators to see different devices with different LUNs
+with necessary access permissions.
+
+SCST supports two modes of access control:
+
+1. Target-oriented. In this mode you define for each target a default
+set of LUNs, which are accessible to all initiators, connected to that
+target. This is a regular access control mode, which people usually mean
+thinking about access control in general. For instance, in IET this is
+the only supported mode.
+
+2. Initiator-oriented. In this mode you define which LUNs are accessible
+for each initiator. In this mode you should create for each set of one
+or more initiators, which should access to the same set of devices with
+the same LUNs, a separate security group, then add to it devices and
+names of allowed initiator(s).
+
+Both modes can be used simultaneously. In this case the
+initiator-oriented mode has higher priority, than the target-oriented,
+i.e. initiators are at first searched in all defined security groups for
+this target and, if none matches, the default target's set of LUNs is
+used. This set of LUNs might be empty, then the initiator will not see
+any LUNs from the target.
+
+You can at any time find out which set of LUNs each session is assigned
+to by looking where link
+/sys/kernel/scst_tgt/targets/target_driver/target_name/sessions/initiator_name/luns
+points to.
+
+To configure the target-oriented access control SCST provides the
+following interface. Each target's sysfs subdirectory
+(/sys/kernel/scst_tgt/targets/target_driver/target_name) has "luns"
+subdirectory. This subdirectory contains the list of already defined
+target-oriented access control LUNs for this target as well as file
+"mgmt". This file has the following commands, which you can send to it,
+for instance, using "echo" shell command. You can always get a small
+help about supported commands by looking inside this file. "Parameters"
+are one or more param_name=value pairs separated by ';'.
+
+ - "add H:C:I:L lun [parameters]" - adds a pass-through device with
+   host:channel:id:lun with LUN "lun". Optionally, the device could be
+   marked as read only by using parameter "read_only". The recommended
+   way to find out H:C:I:L numbers is use of lsscsi utility.
+
+ - "replace H:C:I:L lun [parameters]" - replaces by pass-through device
+   with host:channel:id:lun existing with LUN "lun" device with
+   generation of INQUIRY DATA HAS CHANGED Unit Attention. If the old
+   device doesn't exist, this command acts as the "add" command.
+   Optionally, the device could be marked as read only by using
+   parameter "read_only". The recommended way to find out H:C:I:L
+   numbers is use of lsscsi utility.
+
+ - "del H:C:I:L" - deletes a pass-through device with host:channel:id:lun
+   The recommended way to find out H:C:I:L numbers is use of lsscsi
+   utility.
+
+ - "add VNAME lun [parameters]" - adds a virtual device with name VNAME
+   with LUN "lun". Optionally, the device could be marked as read only
+   by using parameter "read_only".
+
+ - "replace VNAME lun [parameters]" - replaces by virtual device
+   with name VNAME existing with LUN "lun" device with generation of
+   INQUIRY DATA HAS CHANGED Unit Attention. If the old device doesn't
+   exist, this command acts as the "add" command. Optionally, the device
+   could be marked as read only by using parameter "read_only".
+
+ - "del VNAME" - deletes a virtual device with name VNAME.
+
+ - "clear" - clears the list of devices
+
+To configure the initiator-oriented access control SCST provides the
+following interface. Each target's sysfs subdirectory
+(/sys/kernel/scst_tgt/targets/target_driver/target_name) has "ini_group"
+subdirectory. This subdirectory contains the list of already defined
+security groups for this target as well as file "mgmt". This file has
+the following commands, which you can send to it, for instance, using
+"echo" shell command. You can always get a small help about supported
+commands by looking inside this file.
+
+ - "create GROUP_NAME" - creates a new security group.
+
+ - "del GROUP_NAME" - deletes a new security group.
+
+Each security group's subdirectory contains 2 subdirectories: initiators
+and luns.
+
+Each "initiators" subdirectory contains list of added to this groups
+initiator as well as as well as file "mgmt". This file has the following
+commands, which you can send to it, for instance, using "echo" shell
+command. You can always get a small help about supported commands by
+looking inside this file.
+
+ - "add INITIATOR_NAME" - adds initiator with name INITIATOR_NAME to the
+   group.
+
+ - "del INITIATOR_NAME" - deletes initiator with name INITIATOR_NAME
+   from the group.
+
+ - "move INITIATOR_NAME DEST_GROUP_NAME" moves initiator with name
+   INITIATOR_NAME from the current group to group with name
+   DEST_GROUP_NAME.
+
+ - "clear" - deletes all initiators from this group.
+                                  
+For "add" and "del" commands INITIATOR_NAME can be a simple DOS-type
+patterns, containing '*' and '?' symbols. '*' means match all any
+symbols, '?' means match only any single symbol. For instance,
+"blah.xxx" will match "bl?h.*".
+
+Each "luns" subdirectory contains the list of already defined LUNs for
+this group as well as file "mgmt". Content of this file as well as list
+of available in it commands is fully identical to the "luns"
+sybdirectory of the target-oriented access control.
+
+Examples:
+
+ - echo "create INI" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/mgmt -
+   creates security group INI for target iqn.2006-10.net.vlnb:tgt1.
+
+ - echo "add 2:0:1:0 11" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI/luns/mgmt -
+   adds a pass-through device sitting on host 2, channel 0, ID 1, LUN 0
+   to group with name INI as LUN 11.
+
+ - echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI/luns/mgmt -
+   adds a virtual disk with name disk1 to group with name INI as LUN 0.
+
+ - echo "add 21:*:e0:?b:83:*" >/sys/kernel/scst_tgt/targets/21:00:00:a0:8c:54:52:12/ini_group/INI/initiators/mgmt -
+   adds a pattern to group with name INI to Fibre Channel target with
+   WWN 21:00:00:a0:8c:54:52:12, which matches WWNs of Fibre Channel
+   initiator ports.
+
+Consider you need to have an iSCSI target with name
+"iqn.2007-05.com.example:storage.disk1.sys1.xyz", which should export
+virtual device "dev1" with LUN 0 and virtual device "dev2" with LUN 1,
+but initiator with name
+"iqn.2007-05.com.example:storage.disk1.spec_ini.xyz" should see only
+virtual device "dev2" read only with LUN 0. To achieve that you should
+do the following commands:
+
+# echo "iqn.2007-05.com.example:storage.disk1.sys1.xyz" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+# echo "add dev1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/luns/mgmt
+# echo "add dev2 1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/luns/mgmt
+# echo "create SPEC_INI" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/ini_group/mgmt
+# echo "add dev2 0 read_only=1" \
+       >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/ini_group/SPEC_INI/luns/mgmt
+# echo "iqn.2007-05.com.example:storage.disk1.spec_ini.xyz" \
+       >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/ini_group/SPEC_INI/initiators/mgmt
+
+For Fibre Channel or SAS in the above example you should use target's
+and initiator ports WWNs instead of iSCSI names.
+
+It is highly recommended to use scstadmin utility instead of described
+in this section low level interface.
+
+IMPORTANT
+=========
+
+There must be LUN 0 in each set of LUNs, i.e. LUs numeration must not
+start from, e.g., 1. Otherwise you will see no devices on remote
+initiators and SCST core will write into the kernel log message: "tgt_dev
+for LUN 0 not found, command to unexisting LU?"
+
+IMPORTANT
+=========
+
+All the access control must be fully configured BEFORE the corresponding
+target is enabled! When you enable a target, it will immediately start
+accepting new connections, hence creating new sessions, and those new
+sessions will be assigned to security groups according to the
+*currently* configured access control settings. For instance, to
+the default target's set of LUNs, instead of "HOST004" group as you may
+need, because "HOST004" doesn't exist yet. So, one must configure all
+the security groups before new connections from the initiators are
+created, i.e. before the target enabled.
+
+
 VDISK device handler
 --------------------
 
+/proc interface
+~~~~~~~~~~~~~~~
+
+This interface starting from version 2.0.0 is obsolete and will be
+removed in one of the next versions.
+
 After loading VDISK device handler creates in /proc/scsi_tgt/
-subdirectories "vdisk" and "vcdrom". They have similar layout:
+subdirectories "vdisk" and "vcdrom". They have the following layout:
 
   - "trace_level" and "type" files as described above
 
@@ -665,16 +982,206 @@ mode is used.
 For example, "echo "open disk1 /vdisks/disk1" >/proc/scsi_tgt/vdisk/vdisk"
 will open file /vdisks/disk1 as virtual FILEIO disk with name "disk1".
 
-Also the VDISK handler has module parameter "num_threads", which
-specifies count of I/O threads for each VDISK's device. If you have a
-workload, which tends to produce rather random accesses (e.g. DB-like),
-you should increase this count to a bigger value, like 32. If you have a
-rather sequential workload, you should decrease it to a lower value,
-like number of CPUs on the target or even 1. Due to some limitations of
-Linux I/O subsystem, increasing number of I/O threads leads to
-performance drop, especially with deadline scheduler, so decreasing it
-can improve sequential performance. The default provides a good
-compromise between random and sequential accesses.
+/sys interface
+~~~~~~~~~~~~~~
+
+Starting from 2.0.0 VDISK device handler has sysfs interface. You can
+switch to it by running "make disable_proc". To switch back to the
+procfs interface you should run "make enable_proc". The procfs interface
+starting from version 2.0.0 is obsolete and will be removed in one of
+the next versions.
+
+VDISK has 4 built-in dev handlers: vdisk_fileio, vdisk_blockio,
+vdisk_nullio and vcdrom. Roots of their sysfs interface are
+/sys/kernel/scst_tgt/handlers/handler_name, e.g. for vdisk_fileio:
+/sys/kernel/scst_tgt/handlers/vdisk_fileio. Each root has the following
+entries:
+
+ - None, one or more links to devices with name equal to names
+   of the corresponding devices.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - mgmt - main management entry, which allows to add/delete VDISK
+   devices with the corresponding type.
+
+The "mgmt" file has the following commands, which you can send to it,
+for instance, using "echo" shell command. You can always get a small
+help about supported commands by looking inside this file. "Parameters"
+are one or more param_name=value pairs separated by ';'.
+
+ - echo "add_device device_name [parameters]" - adds a virtual device
+   with name device_name and specified parameters (see below)
+
+ - echo "del_device device_name" - deletes a virtual device with name
+   device_name.
+
+Handler vdisk_fileio provides FILEIO mode to create virtual devices.
+This mode uses as backend files and accesses to them using regular
+read()/write() file calls. This allows to use full power of Linux page
+cache. The following parameters possible for vdisk_fileio:
+
+ - filename - specifies path and file name of the backend file. The path
+   must be absolute.
+
+ - blocksize - specifies block size used by this virtual device. The
+   block size must be power of 2 and >= 512 bytes. Default is 512.
+
+ - write_through - disables write back caching. Note, this option
+   has sense only if you also *manually* disable write-back cache in
+   *all* your backstorage devices and make sure it's actually disabled,
+   since many devices are known to lie about this mode to get better
+   benchmark results. Default is 0.
+
+ - read_only - read only. Default is 0.
+
+ - o_direct - disables both read and write caching. This mode isn't
+   currently fully implemented, you should use user space fileio_tgt
+   program in O_DIRECT mode instead (see below).
+
+ - nv_cache - enables "non-volatile cache" mode. In this mode it is
+   assumed that the target has a GOOD UPS with ability to cleanly
+   shutdown target in case of power failure and it is software/hardware
+   bugs free, i.e. all data from the target's cache are guaranteed
+   sooner or later to go to the media. Hence all data synchronization
+   with media operations, like SYNCHRONIZE_CACHE, are ignored in order
+   to bring more performance. Also in this mode target reports to
+   initiators that the corresponding device has write-through cache to
+   disable all write-back cache workarounds used by initiators. Use with
+   extreme caution, since in this mode after a crash of the target
+   journaled file systems don't guarantee the consistency after journal
+   recovery, therefore manual fsck MUST be ran. Note, that since usually
+   the journal barrier protection (see "IMPORTANT" note below) turned
+   off, enabling NV_CACHE could change nothing from data protection
+   point of view, since no data synchronization with media operations
+   will go from the initiator. This option overrides "write_through"
+   option. Disabled by default.
+
+ - removable - with this flag set the device is reported to remote
+   initiators as removable.
+
+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
+storage HBAs and for applications that either do not need caching
+between application and disk or need the large block throughput. See
+below for more info.
+
+The following parameters possible for vdisk_blockio: filename,
+blocksize, read_only, removable. See vdisk_fileio above for description
+of those parameters.
+
+Handler vdisk_nullio provides NULLIO mode to create virtual devices. In
+this mode no real I/O is done, but success returned to initiators.
+Intended to be used for performance measurements at the same way as
+"*_perf" handlers. The following parameters possible for vdisk_nullio:
+blocksize, read_only, removable. See vdisk_fileio above for description
+of those parameters.
+
+Handler vcdrom allows emulation of a virtual CDROM device using an ISO
+file as backend. It doesn't have any parameters.
+
+For example:
+
+echo "add_device disk1 filename=/disk1; blocksize=4096; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+
+will create a FILEIO virtual device disk1 with backend file /disk1   
+with block size 4K and NV_CACHE enabled.
+
+Each vdisk_fileio's device has the following attributes in
+/sys/kernel/scst_tgt/devices/device_name:
+
+ - filename - contains path and file name of the backend file.
+
+ - blocksize - contains block size used by this virtual device.
+
+ - write_through - contains status of write back caching of this virtual
+   device.
+
+ - read_only - contains read only status of this virtual device.
+
+ - o_direct - contains O_DIRECT status of this virtual device.
+
+ - nv_cache - contains NV_CACHE status of this virtual device.
+
+ - removable - contains removable status of this virtual device.
+
+ - size_mb - contains size of this virtual device in MB.
+
+ - t10_dev_id - contains and allows to set T10 vendor specific
+   identifier for Device Identification VPD page (0x83) of INQUIRY data.
+   By default VDISK handler always generates t10_dev_id for every new
+   created device at creation time based on the device name and
+   scst_vdisk_ID scst_vdisk.ko module parameter (see below).
+
+ - usn - contains the virtual device's serial number of INQUIRY data. It
+   is created at the device creation time based on the device name and
+   scst_vdisk_ID scst_vdisk.ko module parameter (see below).
+
+ - type - contains SCSI type of this virtual device.
+
+ - resync_size - write only attribute, which makes vdisk_fileio to
+   rescan size of the backend file. It is usful if you changed it, for
+   instance, if you resized it.
+
+For example:
+
+/sys/kernel/scst_tgt/devices/disk1
+|-- blocksize
+|-- exported
+|   |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
+|   |-- export1 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_group/INI/luns/0
+|   |-- export2 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
+|   |-- export3 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI1/luns/0
+|   |-- export4 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI2/luns/0
+|-- filename
+|-- handler -> ../../handlers/vdisk_fileio
+|-- nv_cache
+|-- o_direct
+|-- read_only
+|-- removable
+|-- resync_size
+|-- size_mb
+|-- t10_dev_id
+|-- 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.
+
+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.
+
+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:
+
+ - echo "/image.iso" >/sys/kernel/scst_tgt/devices/cdrom/filename - will
+   insert file /image.iso as virtual media to the virtual CDROM cdrom.
+
+ - echo "" >/sys/kernel/scst_tgt/devices/cdrom/filename - will remove
+   "media" from the virtual CDROM cdrom.
+
+Additionally to the sysfs/procfs interface VDISK handler has module
+parameter "num_threads", which specifies count of I/O threads for each
+VDISK's device. If you have a workload, which tends to produce rather
+random accesses (e.g. DB-like), you should increase this count to a
+bigger value, like 32. If you have a rather sequential workload, you
+should decrease it to a lower value, like number of CPUs on the target
+or even 1. Due to some limitations of Linux I/O subsystem, increasing
+number of I/O threads too much leads to sequential performance drop,
+especially with deadline scheduler, so decreasing it can improve
+sequential performance. The default provides a good compromise between
+random and sequential accesses.
 
 You shouldn't be afraid to have too many VDISK I/O threads if you have
 many VDISK devices. Kernel threads consume very little amount of
@@ -704,7 +1211,6 @@ CAUTION: If you partitioned/formatted your device with block size X, *NEVER*
 
 IMPORTANT: By default for performance reasons VDISK FILEIO devices use write
 =========  back caching policy. This is generally safe from the consistence of
-
            journaled file systems, laying over them, point of view, but
           your unsaved cached data will be lost in case of
           power/hardware/software failure, so you must supply your
index 67c5595..46d32ec 100644 (file)
@@ -37,6 +37,7 @@ In addition, SCST supports advanced per-initiator access and devices
 visibility management, so different initiators could see different set
 of devices with different access permissions. See below for details.
 
+
 Installation
 ------------
 
@@ -98,7 +99,7 @@ Usage in failover mode
 ----------------------
 
 It is recommended to use TEST UNIT READY ("tur") command to check if
-SCST target is alive.
+SCST target is alive in MPIO configurations.
 
 
 Device handlers
@@ -145,6 +146,7 @@ NOTE: Since "perf" device handlers on READ operations don't touch the
       was allocated, without even being zeroed. Thus, "perf" device
       handlers impose some security risk, so use them with caution.
 
+
 Compilation options
 -------------------
 
@@ -232,7 +234,8 @@ your favorite kernel configuration Makefile target, e.g. "make xconfig":
 
  - CONFIG_SCST_MEASURE_LATENCY - if defined, provides in /proc/scsi_tgt/latency
    file average commands processing latency. You can clear already
-   measured results by writing 0 in this file. Note, you need a
+   measured results by writing 0 in this file. For the sysfs build you
+   can find those results in /sys/kernel/scst_tgt and below. Note, you need a
    non-preemptible kernel to have correct results.
 
 HIGHMEM kernel configurations are fully supported, but not recommended
@@ -254,6 +257,7 @@ For changing VMSPLIT option (CONFIG_VMSPLIT to be precise) you should in
    memory you have. If it is less than 800MB, you may not touch this
    option at all.
 
+
 Module parameters
 -----------------
 
@@ -266,12 +270,15 @@ Module scst supports the following parameters:
    consumed by the SCST commands for data buffers at any given time. By
    default it is approximately TotalMem/4.
 
-SCST /proc commands
----------------------
+
+SCST /proc interface
+--------------------
 
 For communications with user space programs SCST provides proc-based
-interface in /proc/scsi_tgt directory. It contains the following
-entries:
+interface in /proc/scsi_tgt directory. This interface is available in
+the procfs build only. Starting from version 2.0.0 it is obsolete and
+will be removed in one of the next versions. It contains the following
+entries.
 
   - "help" file, which provides online help for SCST commands
 
@@ -324,8 +331,9 @@ For example, "echo "assign 1:0:1:0 dev_disk" >/proc/scsi_tgt/scsi_tgt"
 will assign device handler "dev_disk" to real device sitting on host 1,
 channel 0, ID 1, LUN 0.
 
-Access and devices visibility management (LUN masking)
-------------------------------------------------------
+
+Access and devices visibility management (LUN masking) - /proc interface
+------------------------------------------------------------------------
 
 Access and devices visibility management allows for an initiator or
 group of initiators to see different devices with different LUNs
@@ -403,15 +411,15 @@ following files and directories under /proc/scsi_tgt:
     H:C:I:L numbers is use of lsscsi utility.
 
   - "add V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices adds
-    device with virtual name "V_NAME" with LUN "lun" in group
-    "GROUP_NAME". Optionally, the device could be marked as read only.
+    device with virtual name "V_NAME" with LUN "lun" in group "GROUP_NAME".
+    Optionally, the device could be marked as read only.
 
   - "replace V_NAME lun [READ_ONLY]" to /proc/scsi_tgt/groups/GROUP_NAME/devices
     replaces by device with virtual name "V_NAME" existing with LUN
     "lun" device in group "GROUP_NAME" with generation of INQUIRY DATA
     HAS CHANGED Unit Attention. If the old device doesn't exist, this
-    command acts as the "add" command. Optionally, the device could be
-    marked as read only.
+    command acts as the "add" command. Optionally, the device could
+    be marked as read only.
 
   - "del V_NAME" to /proc/scsi_tgt/groups/GROUP_NAME/devices deletes device with
     virtual name "V_NAME" from group "GROUP_NAME"
@@ -471,8 +479,8 @@ IMPORTANT
 
 There must be LUN 0 in each security group, i.e. LUs numeration must not
 start from, e.g., 1. Otherwise you will see no devices on remote
-initiators and SCST core will write into the kernel log message:
-"tgt_dev for LUN 0 not found, command to unexisting LU?"
+initiators and SCST core will write into the kernel log message: "tgt_dev
+for LUN 0 not found, command to unexisting LU?"
 
 IMPORTANT
 =========
@@ -498,11 +506,326 @@ target driver load and then only add new devices to new groups for new
 initiators or add new devices to old groups, but not altering existing
 LUNs in them.
 
+
+SCST sysfs interface
+--------------------
+
+Starting from 2.0.0 SCST has sysfs interface. You can switch to it by
+running "make disable_proc". To switch back to the procfs interface you
+should run "make enable_proc".
+
+Root of SCST sysfs interface is /sys/kernel/scst_tgt. It has the
+following entries:
+
+ - devices - this is a root subdirectory for all SCST devices
+
+ - handlers - this is a root subdirectory for all SCST dev handlers
+
+ - sgv - this is a root subdirectory for all SCST SGV caches
+
+ - targets - this is a root subdirectory for all SCST targets
+
+ - threads - allows to read and set number of global SCST I/O threads.
+   Those threads used with async. dev handlers, for instance, vdisk
+   BLOCKIO or NULLIO.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - version - read-only attribute, which allows to see version of
+   SCST and enabled optional features.
+
+Each SCST sysfs file (attribute) can contain in the last line mark
+"[key]". It is automatically added mark used to allow scstadmin to see
+which attributes it should save in the config file. You can ignore it.
+
+"Devices" subdirectory contains subdirectories for each SCST devices.
+
+Content of each device's subdirectory is dev handler specific. See
+documentation for your dev handlers for more info about it as well as
+SysfsRules file for more info about common to all dev handlers rules.
+Standard SCST dev handlers have at least the following common entries:
+
+ - exported - subdirectory containing links to all LUNs where this
+   device was exported.
+
+ - handler - if dev handler determined for this device, this link points
+   to it. The handler can be not set for pass-through devices.
+
+ - type - SCSI type of this device
+See below for more information about other entries of this subdirectory
+of the standard SCST dev handlers.
+
+"Handlers" subdirectory contains subdirectories for each SCST dev
+handler.
+
+Content of each handler's subdirectory is dev handler specific. See
+documentation for your dev handlers for more info about it as well as
+SysfsRules file for more info about common to all dev handlers rules.
+Standard SCST dev handlers have at least the following common entries:
+
+ - pass_through - if exists, it contains 1 and this dev handler is a
+   pass-through dev handler.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - type - SCSI type of devices served by this dev handler.
+
+See below for more information about other entries of this subdirectory
+of the standard SCST dev handlers.
+
+"Sgv" subdirectory contains statistic information of SCST SGV caches. It
+has the following entries:
+
+ - None, one or more subdirectories for each existing SGV cache.
+ - global_stats - file containing global SGV caches statistics.
+
+Each SGV cache's subdirectory has the following item:
+
+ - stats - file containing statistics for this SGV caches.
+
+"Targets" subdirectory contains subdirectories for each SCST target.
+
+Content of each target's subdirectory is target specific. See
+documentation for your target for more info about it as well as
+SysfsRules file for more info about common to all dev handlers rules.
+Every target should have at least the following entries:
+
+ - ini_group - subdirectory, which contains and allows to define
+   initiator-oriented access control information, see below.
+
+ - luns - subdirectory, which contains list of available LUNs in the
+   target-oriented access control and allows to define it, see below.
+
+ - sessions - subdirectory containing connected to this target sessions.
+
+ - enabled - using this attribute you can enable or disable this target/
+   It allows to finish configuring it before it starts accepting new
+   connections. 0 by default.
+
+ - 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
+   commands. This identifier must be unique among all SCST targets, but
+   for convenience SCST allows disabled targets to have not unique
+   rel_tgt_id. In this case SCST will not allow to enable this target
+   until rel_tgt_id becomes unique. This attribute initialized unique by
+   SCST by default.
+
+Subdirectory "sessions" contains one subdirectory for each connected
+session with name equal to name of the connected initiator.
+
+Each session subdirectory contains the following entries:
+
+ - initiator_name - contains initiator name
+
+ - force_close - optional write-only attribute, which allows to force
+   close this session.
+
+ - active_commands - contains number of active, i.e. not yet or being
+   executed, SCSI commands in this session.
+
+ - commands - contains overall number of SCSI commands in this session.
+
+ - other target driver specific attributes and subdirectories.
+
+See below description of the VDISK's sysfs interface for samples.
+
+
+Access and devices visibility management (LUN masking) - sysfs interface
+------------------------------------------------------------------------
+
+Access and devices visibility management allows for an initiator or
+group of initiators to see different devices with different LUNs
+with necessary access permissions.
+
+SCST supports two modes of access control:
+
+1. Target-oriented. In this mode you define for each target a default
+set of LUNs, which are accessible to all initiators, connected to that
+target. This is a regular access control mode, which people usually mean
+thinking about access control in general. For instance, in IET this is
+the only supported mode.
+
+2. Initiator-oriented. In this mode you define which LUNs are accessible
+for each initiator. In this mode you should create for each set of one
+or more initiators, which should access to the same set of devices with
+the same LUNs, a separate security group, then add to it devices and
+names of allowed initiator(s).
+
+Both modes can be used simultaneously. In this case the
+initiator-oriented mode has higher priority, than the target-oriented,
+i.e. initiators are at first searched in all defined security groups for
+this target and, if none matches, the default target's set of LUNs is
+used. This set of LUNs might be empty, then the initiator will not see
+any LUNs from the target.
+
+You can at any time find out which set of LUNs each session is assigned
+to by looking where link
+/sys/kernel/scst_tgt/targets/target_driver/target_name/sessions/initiator_name/luns
+points to.
+
+To configure the target-oriented access control SCST provides the
+following interface. Each target's sysfs subdirectory
+(/sys/kernel/scst_tgt/targets/target_driver/target_name) has "luns"
+subdirectory. This subdirectory contains the list of already defined
+target-oriented access control LUNs for this target as well as file
+"mgmt". This file has the following commands, which you can send to it,
+for instance, using "echo" shell command. You can always get a small
+help about supported commands by looking inside this file. "Parameters"
+are one or more param_name=value pairs separated by ';'.
+
+ - "add H:C:I:L lun [parameters]" - adds a pass-through device with
+   host:channel:id:lun with LUN "lun". Optionally, the device could be
+   marked as read only by using parameter "read_only". The recommended
+   way to find out H:C:I:L numbers is use of lsscsi utility.
+
+ - "replace H:C:I:L lun [parameters]" - replaces by pass-through device
+   with host:channel:id:lun existing with LUN "lun" device with
+   generation of INQUIRY DATA HAS CHANGED Unit Attention. If the old
+   device doesn't exist, this command acts as the "add" command.
+   Optionally, the device could be marked as read only by using
+   parameter "read_only". The recommended way to find out H:C:I:L
+   numbers is use of lsscsi utility.
+
+ - "del H:C:I:L" - deletes a pass-through device with host:channel:id:lun
+   The recommended way to find out H:C:I:L numbers is use of lsscsi
+   utility.
+
+ - "add VNAME lun [parameters]" - adds a virtual device with name VNAME
+   with LUN "lun". Optionally, the device could be marked as read only
+   by using parameter "read_only".
+
+ - "replace VNAME lun [parameters]" - replaces by virtual device
+   with name VNAME existing with LUN "lun" device with generation of
+   INQUIRY DATA HAS CHANGED Unit Attention. If the old device doesn't
+   exist, this command acts as the "add" command. Optionally, the device
+   could be marked as read only by using parameter "read_only".
+
+ - "del VNAME" - deletes a virtual device with name VNAME.
+
+ - "clear" - clears the list of devices
+
+To configure the initiator-oriented access control SCST provides the
+following interface. Each target's sysfs subdirectory
+(/sys/kernel/scst_tgt/targets/target_driver/target_name) has "ini_group"
+subdirectory. This subdirectory contains the list of already defined
+security groups for this target as well as file "mgmt". This file has
+the following commands, which you can send to it, for instance, using
+"echo" shell command. You can always get a small help about supported
+commands by looking inside this file.
+
+ - "create GROUP_NAME" - creates a new security group.
+
+ - "del GROUP_NAME" - deletes a new security group.
+
+Each security group's subdirectory contains 2 subdirectories: initiators
+and luns.
+
+Each "initiators" subdirectory contains list of added to this groups
+initiator as well as as well as file "mgmt". This file has the following
+commands, which you can send to it, for instance, using "echo" shell
+command. You can always get a small help about supported commands by
+looking inside this file.
+
+ - "add INITIATOR_NAME" - adds initiator with name INITIATOR_NAME to the
+   group.
+
+ - "del INITIATOR_NAME" - deletes initiator with name INITIATOR_NAME
+   from the group.
+
+ - "move INITIATOR_NAME DEST_GROUP_NAME" moves initiator with name
+   INITIATOR_NAME from the current group to group with name
+   DEST_GROUP_NAME.
+
+ - "clear" - deletes all initiators from this group.
+                                  
+For "add" and "del" commands INITIATOR_NAME can be a simple DOS-type
+patterns, containing '*' and '?' symbols. '*' means match all any
+symbols, '?' means match only any single symbol. For instance,
+"blah.xxx" will match "bl?h.*".
+
+Each "luns" subdirectory contains the list of already defined LUNs for
+this group as well as file "mgmt". Content of this file as well as list
+of available in it commands is fully identical to the "luns"
+sybdirectory of the target-oriented access control.
+
+Examples:
+
+ - echo "create INI" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/mgmt -
+   creates security group INI for target iqn.2006-10.net.vlnb:tgt1.
+
+ - echo "add 2:0:1:0 11" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI/luns/mgmt -
+   adds a pass-through device sitting on host 2, channel 0, ID 1, LUN 0
+   to group with name INI as LUN 11.
+
+ - echo "add disk1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI/luns/mgmt -
+   adds a virtual disk with name disk1 to group with name INI as LUN 0.
+
+ - echo "add 21:*:e0:?b:83:*" >/sys/kernel/scst_tgt/targets/21:00:00:a0:8c:54:52:12/ini_group/INI/initiators/mgmt -
+   adds a pattern to group with name INI to Fibre Channel target with
+   WWN 21:00:00:a0:8c:54:52:12, which matches WWNs of Fibre Channel
+   initiator ports.
+
+Consider you need to have an iSCSI target with name
+"iqn.2007-05.com.example:storage.disk1.sys1.xyz", which should export
+virtual device "dev1" with LUN 0 and virtual device "dev2" with LUN 1,
+but initiator with name
+"iqn.2007-05.com.example:storage.disk1.spec_ini.xyz" should see only
+virtual device "dev2" read only with LUN 0. To achieve that you should
+do the following commands:
+
+# echo "iqn.2007-05.com.example:storage.disk1.sys1.xyz" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
+# echo "add dev1 0" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/luns/mgmt
+# echo "add dev2 1" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/luns/mgmt
+# echo "create SPEC_INI" >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/ini_group/mgmt
+# echo "add dev2 0 read_only=1" \
+       >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/ini_group/SPEC_INI/luns/mgmt
+# echo "iqn.2007-05.com.example:storage.disk1.spec_ini.xyz" \
+       >/sys/kernel/scst_tgt/targets/iscsi/iqn.2007-05.com.example:storage.disk1.sys1.xyz/ini_group/SPEC_INI/initiators/mgmt
+
+For Fibre Channel or SAS in the above example you should use target's
+and initiator ports WWNs instead of iSCSI names.
+
+It is highly recommended to use scstadmin utility instead of described
+in this section low level interface.
+
+IMPORTANT
+=========
+
+There must be LUN 0 in each set of LUNs, i.e. LUs numeration must not
+start from, e.g., 1. Otherwise you will see no devices on remote
+initiators and SCST core will write into the kernel log message: "tgt_dev
+for LUN 0 not found, command to unexisting LU?"
+
+IMPORTANT
+=========
+
+All the access control must be fully configured BEFORE the corresponding
+target is enabled! When you enable a target, it will immediately start
+accepting new connections, hence creating new sessions, and those new
+sessions will be assigned to security groups according to the
+*currently* configured access control settings. For instance, to
+the default target's set of LUNs, instead of "HOST004" group as you may
+need, because "HOST004" doesn't exist yet. So, one must configure all
+the security groups before new connections from the initiators are
+created, i.e. before the target enabled.
+
+
 VDISK device handler
 --------------------
 
+/proc interface
+~~~~~~~~~~~~~~~
+
+This interface starting from version 2.0.0 is obsolete and will be
+removed in one of the next versions.
+
 After loading VDISK device handler creates in /proc/scsi_tgt/
-subdirectories "vdisk" and "vcdrom". They have similar layout:
+subdirectories "vdisk" and "vcdrom". They have the following layout:
 
   - "trace_level" and "type" files as described above
 
@@ -582,16 +905,207 @@ mode is used.
 For example, "echo "open disk1 /vdisks/disk1" >/proc/scsi_tgt/vdisk/vdisk"
 will open file /vdisks/disk1 as virtual FILEIO disk with name "disk1".
 
-Also the VDISK handler has module parameter "num_threads", which
-specifies count of I/O threads for each VDISK's device. If you have a
-workload, which tends to produce rather random accesses (e.g. DB-like),
-you should increase this count to a bigger value, like 32. If you have a
-rather sequential workload, you should decrease it to a lower value,
-like number of CPUs on the target or even 1. Due to some limitations of
-Linux I/O subsystem, increasing number of I/O threads leads to
-performance drop, especially with deadline scheduler, so decreasing it
-can improve sequential performance. The default provides a good
-compromise between random and sequential accesses.
+
+/sys interface
+~~~~~~~~~~~~~~
+
+Starting from 2.0.0 VDISK device handler has sysfs interface. You can
+switch to it by running "make disable_proc". To switch back to the
+procfs interface you should run "make enable_proc". The procfs interface
+starting from version 2.0.0 is obsolete and will be removed in one of
+the next versions.
+
+VDISK has 4 built-in dev handlers: vdisk_fileio, vdisk_blockio,
+vdisk_nullio and vcdrom. Roots of their sysfs interface are
+/sys/kernel/scst_tgt/handlers/handler_name, e.g. for vdisk_fileio:
+/sys/kernel/scst_tgt/handlers/vdisk_fileio. Each root has the following
+entries:
+
+ - None, one or more links to devices with name equal to names
+   of the corresponding devices.
+
+ - trace_level - allows to enable and disable various tracing
+   facilities. See content of this file for help how to use it.
+
+ - mgmt - main management entry, which allows to add/delete VDISK
+   devices with the corresponding type.
+
+The "mgmt" file has the following commands, which you can send to it,
+for instance, using "echo" shell command. You can always get a small
+help about supported commands by looking inside this file. "Parameters"
+are one or more param_name=value pairs separated by ';'.
+
+ - echo "add_device device_name [parameters]" - adds a virtual device
+   with name device_name and specified parameters (see below)
+
+ - echo "del_device device_name" - deletes a virtual device with name
+   device_name.
+
+Handler vdisk_fileio provides FILEIO mode to create virtual devices.
+This mode uses as backend files and accesses to them using regular
+read()/write() file calls. This allows to use full power of Linux page
+cache. The following parameters possible for vdisk_fileio:
+
+ - filename - specifies path and file name of the backend file. The path
+   must be absolute.
+
+ - blocksize - specifies block size used by this virtual device. The
+   block size must be power of 2 and >= 512 bytes. Default is 512.
+
+ - write_through - disables write back caching. Note, this option
+   has sense only if you also *manually* disable write-back cache in
+   *all* your backstorage devices and make sure it's actually disabled,
+   since many devices are known to lie about this mode to get better
+   benchmark results. Default is 0.
+
+ - read_only - read only. Default is 0.
+
+ - o_direct - disables both read and write caching. This mode isn't
+   currently fully implemented, you should use user space fileio_tgt
+   program in O_DIRECT mode instead (see below).
+
+ - nv_cache - enables "non-volatile cache" mode. In this mode it is
+   assumed that the target has a GOOD UPS with ability to cleanly
+   shutdown target in case of power failure and it is software/hardware
+   bugs free, i.e. all data from the target's cache are guaranteed
+   sooner or later to go to the media. Hence all data synchronization
+   with media operations, like SYNCHRONIZE_CACHE, are ignored in order
+   to bring more performance. Also in this mode target reports to
+   initiators that the corresponding device has write-through cache to
+   disable all write-back cache workarounds used by initiators. Use with
+   extreme caution, since in this mode after a crash of the target
+   journaled file systems don't guarantee the consistency after journal
+   recovery, therefore manual fsck MUST be ran. Note, that since usually
+   the journal barrier protection (see "IMPORTANT" note below) turned
+   off, enabling NV_CACHE could change nothing from data protection
+   point of view, since no data synchronization with media operations
+   will go from the initiator. This option overrides "write_through"
+   option. Disabled by default.
+
+ - removable - with this flag set the device is reported to remote
+   initiators as removable.
+
+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
+storage HBAs and for applications that either do not need caching
+between application and disk or need the large block throughput. See
+below for more info.
+
+The following parameters possible for vdisk_blockio: filename,
+blocksize, read_only, removable. See vdisk_fileio above for description
+of those parameters.
+
+Handler vdisk_nullio provides NULLIO mode to create virtual devices. In
+this mode no real I/O is done, but success returned to initiators.
+Intended to be used for performance measurements at the same way as
+"*_perf" handlers. The following parameters possible for vdisk_nullio:
+blocksize, read_only, removable. See vdisk_fileio above for description
+of those parameters.
+
+Handler vcdrom allows emulation of a virtual CDROM device using an ISO
+file as backend. It doesn't have any parameters.
+
+For example:
+
+echo "add_device disk1 filename=/disk1; blocksize=4096; nv_cache=1" >/sys/kernel/scst_tgt/handlers/vdisk_fileio/mgmt
+
+will create a FILEIO virtual device disk1 with backend file /disk1   
+with block size 4K and NV_CACHE enabled.
+
+Each vdisk_fileio's device has the following attributes in
+/sys/kernel/scst_tgt/devices/device_name:
+
+ - filename - contains path and file name of the backend file.
+
+ - blocksize - contains block size used by this virtual device.
+
+ - write_through - contains status of write back caching of this virtual
+   device.
+
+ - read_only - contains read only status of this virtual device.
+
+ - o_direct - contains O_DIRECT status of this virtual device.
+
+ - nv_cache - contains NV_CACHE status of this virtual device.
+
+ - removable - contains removable status of this virtual device.
+
+ - size_mb - contains size of this virtual device in MB.
+
+ - t10_dev_id - contains and allows to set T10 vendor specific
+   identifier for Device Identification VPD page (0x83) of INQUIRY data.
+   By default VDISK handler always generates t10_dev_id for every new
+   created device at creation time based on the device name and
+   scst_vdisk_ID scst_vdisk.ko module parameter (see below).
+
+ - usn - contains the virtual device's serial number of INQUIRY data. It
+   is created at the device creation time based on the device name and
+   scst_vdisk_ID scst_vdisk.ko module parameter (see below).
+
+ - type - contains SCSI type of this virtual device.
+
+ - resync_size - write only attribute, which makes vdisk_fileio to
+   rescan size of the backend file. It is usful if you changed it, for
+   instance, if you resized it.
+
+For example:
+
+/sys/kernel/scst_tgt/devices/disk1
+|-- blocksize
+|-- exported
+|   |-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
+|   |-- export1 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/ini_group/INI/luns/0
+|   |-- export2 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
+|   |-- export3 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI1/luns/0
+|   |-- export4 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/INI2/luns/0
+|-- filename
+|-- handler -> ../../handlers/vdisk_fileio
+|-- nv_cache
+|-- o_direct
+|-- read_only
+|-- removable
+|-- resync_size
+|-- size_mb
+|-- t10_dev_id
+|-- 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.
+
+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.
+
+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:
+
+ - echo "/image.iso" >/sys/kernel/scst_tgt/devices/cdrom/filename - will
+   insert file /image.iso as virtual media to the virtual CDROM cdrom.
+
+ - echo "" >/sys/kernel/scst_tgt/devices/cdrom/filename - will remove
+   "media" from the virtual CDROM cdrom.
+
+Additionally to the sysfs/procfs interface VDISK handler has module
+parameter "num_threads", which specifies count of I/O threads for each
+VDISK's device. If you have a workload, which tends to produce rather
+random accesses (e.g. DB-like), you should increase this count to a
+bigger value, like 32. If you have a rather sequential workload, you
+should decrease it to a lower value, like number of CPUs on the target
+or even 1. Due to some limitations of Linux I/O subsystem, increasing
+number of I/O threads too much leads to sequential performance drop,
+especially with deadline scheduler, so decreasing it can improve
+sequential performance. The default provides a good compromise between
+random and sequential accesses.
 
 You shouldn't be afraid to have too many VDISK I/O threads if you have
 many VDISK devices. Kernel threads consume very little amount of
@@ -621,7 +1135,6 @@ CAUTION: If you partitioned/formatted your device with block size X, *NEVER*
 
 IMPORTANT: By default for performance reasons VDISK FILEIO devices use write
 =========  back caching policy. This is generally safe from the consistence of
-
            journaled file systems, laying over them, point of view, but
           your unsaved cached data will be lost in case of
           power/hardware/software failure, so you must supply your
@@ -679,6 +1192,7 @@ IMPORTANT: Some disk and partition table management utilities don't support
           size device, the block size stops matter, any program will
           work with files on such file system.
 
+
 BLOCKIO VDISK mode
 ------------------
 
@@ -710,6 +1224,7 @@ IMPORTANT: Since data in BLOCKIO and FILEIO modes are not consistent between
 =========  them, if you try to use a device in both those modes simultaneously,
            you will almost instantly corrupt your data on that device.
 
+
 Pass-through mode
 -----------------
 
@@ -742,6 +1257,7 @@ Another way to solve this issue is to build SG entries with more than 1
 page each. See the following patch as an example:
 http://scst.sf.net/sgv_big_order_alloc.diff
 
+
 User space mode using scst_user dev handler
 -------------------------------------------
 
@@ -754,6 +1270,7 @@ loads it could be significantly faster, than the regular FILEIO access.
 All the words about BLOCKIO from above apply to O_DIRECT as well. See
 fileio_tgt's README file for more details.
 
+
 Performance
 -----------
 
@@ -998,6 +1515,7 @@ or initiator-target link, you can increase SCST_MAX_TGT_DEV_COMMANDS in
 scst_priv.h to 64. Usually initiators don't try to push more commands on
 the target.
 
+
 Credits
 -------
 
index 77ab2e0..7be9118 100644 (file)
@@ -848,7 +848,7 @@ struct scst_tgt_template {
         *
         * MUST HAVE if virtual targets are supported.
         */
-       ssize_t (*add_target) (const char *target_name, const char *params);
+       ssize_t (*add_target) (const char *target_name, char *params);
 
        /*
         * This function deletes a virtual target. See comment for add_target
@@ -861,11 +861,11 @@ struct scst_tgt_template {
        /*
         * This function called if not "add_target" or "del_target" command is
         * sent to the mgmt entry (see comment for add_target above). In this
-        * case the command passed in this function as is in a string form.
+        * case the command passed to this function as is in a string form.
         *
         * OPTIONAL.
         */
-       ssize_t (*mgmt_cmd) (const char *cmd);
+       ssize_t (*mgmt_cmd) (char *cmd);
 
        /*
         * Name of the template. Must be unique to identify
@@ -907,6 +907,9 @@ struct scst_tgt_template {
        /* Optional help string for mgmt_cmd commands */
        const char *mgmt_cmd_help;
 
+       /* Optional help string for add_target parameters */
+        const char *add_target_parameters_help;
+
        /** Private, must be inited to 0 by memset() **/
 
        /* List of targets per template, protected by scst_mutex */
@@ -937,6 +940,9 @@ struct scst_dev_type {
        /* SCSI type of the supported device. MUST HAVE */
        int type;
 
+       /* True, if this dev handler is a pass-through dev handler */
+       unsigned pass_through:1;
+
        /*
         * True, if corresponding function supports execution in
         * the atomic (non-sleeping) context
@@ -946,12 +952,12 @@ struct scst_dev_type {
        unsigned dev_done_atomic:1;
 
 #ifdef CONFIG_SCST_PROC
-       /* Set, if no /proc files should be automatically created by SCST */
+       /* True, if no /proc files should be automatically created by SCST */
        unsigned no_proc:1;
 #endif
 
        /*
-        * Should be set, if exec() is synchronous. This is a hint to SCST core
+        * Should be true, if exec() is synchronous. This is a hint to SCST core
         * to optimize commands order management.
         */
        unsigned exec_sync:1;
@@ -1070,6 +1076,43 @@ struct scst_dev_type {
        int (*read_proc) (struct seq_file *seq, struct scst_dev_type *dev_type);
        int (*write_proc) (char *buffer, char **start, off_t offset,
                int length, int *eof, struct scst_dev_type *dev_type);
+#else
+       /*
+        * This function adds a virtual device.
+        *
+        * If both add_device and del_device callbacks defined, then this
+        * dev handler supposed to support adding/deleting virtual devices.
+        * In this case an "mgmt" entry will be created in the sysfs root for
+        * this handler. The "mgmt" entry will support 2 commands: "add_device"
+        * and "del_device", for which the corresponding callbacks will be called.
+        * Also dev handler can define own commands for the "mgmt" entry, see
+        * mgmt_cmd and mgmt_cmd_help below.
+        *
+        * This approach allows uniform devices management to simplify external
+        * management tools like scstadmin. See README for more details.
+        *
+        * Either both add_device and del_device must be defined, or none.
+        *
+        * MUST HAVE if virtual devices are supported.
+        */
+       ssize_t (*add_device) (const char *device_name, char *params);
+
+       /*
+        * This function deletes a virtual device. See comment for add_device
+        * above.
+        *
+        * MUST HAVE if virtual devices are supported.
+        */
+       ssize_t (*del_device) (const char *device_name);
+
+       /*
+        * This function called if not "add_device" or "del_device" command is
+        * sent to the mgmt entry (see comment for add_device above). In this
+        * case the command passed to this function as is in a string form.
+        *
+        * OPTIONAL.
+        */
+       ssize_t (*mgmt_cmd) (char *cmd);
 #endif
 
        /*
@@ -1094,10 +1137,16 @@ struct scst_dev_type {
        /* Optional local trace table */
        struct scst_trace_log *trace_tbl;
 
+#ifndef CONFIG_SCST_PROC
        /* Optional local trace table help string */
        const char *trace_tbl_help;
 
-#ifndef CONFIG_SCST_PROC
+       /* Optional help string for mgmt_cmd commands */
+        const char *mgmt_cmd_help;
+
+       /* Optional help string for add_device parameters */
+        const char *add_device_parameters_help;
+
        /* Optional sysfs attributes */
        const struct attribute **devt_attrs;
 
@@ -3644,4 +3693,25 @@ int scst_wait_info_completion(struct scst_sysfs_user_info *info,
 
 #endif /* CONFIG_SCST_PROC */
 
+/*
+ * This function returns pointer to the next lexem from token_str skipping
+ * spaces and '=' character and using them then as a delimeter. Content
+ * of token_str is modified by setting '\0' at the delimeter's position.
+ */
+char *scst_get_next_lexem(char **token_str);
+
+/*
+ * This function restores token_str modified by scst_get_next_lexem() to the
+ * previous value before scst_get_next_lexem() was called. Prev_lexem is
+ * a pointer to lexem returned by scst_get_next_lexem().
+ */
+void scst_restore_token_str(char *prev_lexem, char *token_str);
+
+/*
+ * This function returns pointer to the next token strings from input_str
+ * using '\n', ';' and '\0' as a delimeter. Content of input_str is
+ * modified by setting '\0' at the delimeter's position.
+ */
+char *scst_get_next_token_str(char **input_str);
+
 #endif /* __SCST_H */
index 763ae56..19c800f 100644 (file)
@@ -42,6 +42,7 @@ static int cdrom_done(struct scst_cmd *);
 static struct scst_dev_type cdrom_devtype = {
        .name =                 CDROM_NAME,
        .type =                 TYPE_ROM,
+       .pass_through =         1,
        .parse_atomic =         1,
        .dev_done_atomic =      1,
        .attach =               cdrom_attach,
index e6f6735..6f40fe0 100644 (file)
@@ -38,6 +38,7 @@ static int changer_parse(struct scst_cmd *);
 static struct scst_dev_type changer_devtype = {
        .name = CHANGER_NAME,
        .type = TYPE_MEDIUM_CHANGER,
+       .pass_through = 1,
        .parse_atomic = 1,
 /*     .dev_done_atomic =      1, */
        .attach =       changer_attach,
index 1047402..2d42dfd 100644 (file)
@@ -48,6 +48,7 @@ static int disk_exec(struct scst_cmd *cmd);
 static struct scst_dev_type disk_devtype = {
        .name =                 DISK_NAME,
        .type =                 TYPE_DISK,
+       .pass_through =         1,
        .parse_atomic =         1,
        .dev_done_atomic =      1,
        .exec_atomic =          1,
@@ -64,6 +65,7 @@ static struct scst_dev_type disk_devtype = {
 static struct scst_dev_type disk_devtype_perf = {
        .name =                 DISK_PERF_NAME,
        .type =                 TYPE_DISK,
+       .pass_through =         1,
        .parse_atomic =         1,
        .dev_done_atomic =      1,
        .exec_atomic =          1,
index f0989a4..4ff51c5 100644 (file)
@@ -48,6 +48,7 @@ static int modisk_exec(struct scst_cmd *);
 static struct scst_dev_type modisk_devtype = {
        .name =                 MODISK_NAME,
        .type =                 TYPE_MOD,
+       .pass_through =         1,
        .parse_atomic =         1,
        .dev_done_atomic =      1,
        .exec_atomic =          1,
@@ -64,6 +65,7 @@ static struct scst_dev_type modisk_devtype = {
 static struct scst_dev_type modisk_devtype_perf = {
        .name =                 MODISK_PERF_NAME,
        .type =                 TYPE_MOD,
+       .pass_through =         1,
        .parse_atomic =         1,
        .dev_done_atomic =      1,
        .exec_atomic =          1,
index 6facb80..40d447b 100644 (file)
@@ -38,6 +38,7 @@ static int processor_parse(struct scst_cmd *);
 static struct scst_dev_type processor_devtype = {
        .name =                 PROCESSOR_NAME,
        .type =                 TYPE_PROCESSOR,
+       .pass_through =         1,
        .parse_atomic =         1,
 /*     .dev_done_atomic =      1,*/
        .attach =               processor_attach,
index 40e8e88..d20e6ee 100644 (file)
@@ -38,6 +38,7 @@ static int raid_parse(struct scst_cmd *);
 static struct scst_dev_type raid_devtype = {
        .name =                 RAID_NAME,
        .type =                 TYPE_RAID,
+       .pass_through =         1,
        .parse_atomic =         1,
 /*     .dev_done_atomic =      1,*/
        .attach =               raid_attach,
index 9e19d6b..f68c054 100644 (file)
@@ -53,6 +53,7 @@ static int tape_exec(struct scst_cmd *);
 static struct scst_dev_type tape_devtype = {
        .name =                 TAPE_NAME,
        .type =                 TYPE_TAPE,
+       .pass_through =         1,
        .parse_atomic =         1,
        .dev_done_atomic =      1,
        .exec_atomic =          1,
@@ -69,6 +70,7 @@ static struct scst_dev_type tape_devtype = {
 static struct scst_dev_type tape_devtype_perf = {
        .name =                 TAPE_PERF_NAME,
        .type =                 TYPE_TAPE,
+       .pass_through =         1,
        .parse_atomic =         1,
        .dev_done_atomic =      1,
        .exec_atomic =          1,
index 897ca5b..3f9dfc8 100644 (file)
@@ -90,11 +90,14 @@ static struct scst_trace_log vdisk_local_trace_tbl[] = {
 #define        DEF_SECTORS                     56
 #define        DEF_HEADS                       255
 #define LEN_MEM                                (32 * 1024)
+#define DEF_RD_ONLY                    0
+#define DEF_WRITE_THROUGH              0
+#define DEF_NV_CACHE                   0
+#define DEF_O_DIRECT                   0
+#define DEF_REMOVABLE                  0
 
 #define VDISK_NULLIO_SIZE              (3LL*1024*1024*1024*1024/2)
 
-#define VDEV_NONE_FILENAME             "none"
-
 #define DEF_TST                                SCST_CONTR_MODE_SEP_TASK_SETS
 /*
  * Since we can't control backstorage device's reordering, we have to always
@@ -178,8 +181,6 @@ static unsigned int random_values[256] = {
         1056807896UL,  3525009458UL,  1174811641UL,  3049738746UL,
 };
 
-struct vdev_type;
-
 struct scst_vdisk_dev {
        uint32_t block_size;
        uint64_t nblocks;
@@ -211,16 +212,15 @@ struct scst_vdisk_dev {
        int virt_id;
        char name[16+1];        /* Name of the virtual device,
                                   must be <= SCSI Model + 1 */
-       char *file_name;        /* File name, protected by
+       char *filename;         /* File name, protected by
                                   scst_mutex and suspended activities */
-       char t10_dev_id[16+8+2];   /* T10 device ID */
+       char t10_dev_id[16+8+2]; /* T10 device ID */
        char usn[MAX_USN_LEN];
        struct scst_device *dev;
-       struct list_head vdisk_dev_list_entry;
+       struct list_head vdev_list_entry;
 
        struct mutex vdev_sysfs_mutex;
-
-       const struct vdev_type *vdt;
+       struct scst_dev_type *vdev_devt;
 };
 
 struct scst_vdisk_tgt_dev {
@@ -240,80 +240,6 @@ struct scst_vdisk_thr {
        int iv_count;
 };
 
-/***
- *** We make virtual devices emulation in OOP-like fashion: vdev is
- *** an abstract class serving SPC set of commands and some common management
- *** tasks, vdisk - an SBC (abstract) class with fileio, blockio and nullio
- *** end classes, vcdrom - an MMC device class.
- ***
- *** Unfortunately, C doesn't support encapsulation and inheritance, so we
- *** have to open code it, which makes the code look much less beautiful, than
- *** it should.
- ***
- *** Only part to of the work is done. The second half is ToDo.
- ***/
-
-/* VDEV sysfs actions */
-#define VDEV_ACTION_OPEN               0
-#define VDEV_ACTION_CLOSE              1
-
-/* VDISK sysfs actions */
-#define VDISK_ACTION_RESYNC_SIZE       100
-
-/* VCDROM sysfs actions */
-#define VCDROM_ACTION_CHANGE           100
-
-struct vdev_type_funcs {
-       struct scst_vdisk_dev *(*vdev_create) (struct vdev_type *vdt);
-       void (*vdev_destroy) (struct scst_vdisk_dev *virt_dev);
-
-       void (*vdev_init) (struct vdev_type *vdt,
-               struct scst_vdisk_dev *virt_dev);
-       void (*vdev_deinit) (struct scst_vdisk_dev *virt_dev);
-
-       /* Both supposed to be called under scst_vdisk_mutex */
-       int (*vdev_open) (struct vdev_type *vdt, char *p, const char *name);
-       int (*vdev_close) (struct vdev_type *vdt, const char *name);
-
-       /* All 3 supposed to be called under scst_vdisk_mutex */
-       void (*vdev_add) (struct scst_vdisk_dev *virt_dev);
-       void (*vdev_del) (struct scst_vdisk_dev *virt_dev);
-       struct scst_vdisk_dev *(*vdev_find) (const char *name);
-
-       int (*parse_option) (struct scst_vdisk_dev *virt_dev, char *p);
-
-       int (*pre_register) (struct scst_vdisk_dev *virt_dev);
-};
-
-struct vdev_type {
-       const char *vdt_name;
-       const char *help_string;
-
-       struct scst_dev_type *vdt_devt;
-
-       struct vdev_type_funcs vfns;
-};
-
-struct vdisk_fileio_type {
-       struct vdev_type parent_vdt;
-       struct vdev_type_funcs parent_vdt_vfns;
-};
-
-struct vdisk_blockio_type {
-       struct vdev_type parent_vdt;
-       struct vdev_type_funcs parent_vdt_vfns;
-};
-
-struct vdisk_nullio_type {
-       struct vdev_type parent_vdt;
-       struct vdev_type_funcs parent_vdt_vfns;
-};
-
-struct vcdrom_type {
-       struct vdev_type parent_vdt;
-       struct vdev_type_funcs parent_vdt_vfns;
-};
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
 #define DEF_NUM_THREADS                5
 #else
@@ -361,29 +287,23 @@ static int vcdrom_read_proc(struct seq_file *seq,
        struct scst_dev_type *dev_type);
 static int vcdrom_write_proc(char *buffer, char **start, off_t offset,
        int length, int *eof, struct scst_dev_type *dev_type);
+#else
+static ssize_t vdisk_add_fileio_device(const char *device_name, char *params);
+static ssize_t vdisk_add_blockio_device(const char *device_name, char *params);
+static ssize_t vdisk_add_nullio_device(const char *device_name, char *params);
+static int vdisk_del_device(const char *device_name);
+static ssize_t vcdrom_add_device(const char *device_name, char *params);
+static int vcdrom_del_device(const char *device_name);
 #endif
 static int vdisk_task_mgmt_fn(struct scst_mgmt_cmd *mcmd,
        struct scst_tgt_dev *tgt_dev);
-static uint64_t vdisk_gen_dev_id_num(struct scst_vdisk_dev *virt_dev);
+static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name);
 
 /** SYSFS **/
 
 #ifndef CONFIG_SCST_PROC
 
-static ssize_t vdisk_mgmt_show(struct kobject *kobj,
-       struct kobj_attribute *attr, char *buf);
-static ssize_t vdisk_mgmt_store(struct kobject *kobj,
-       struct kobj_attribute *attr, const char *buf, size_t count);
-
-static struct kobj_attribute vdisk_mgmt_attr =
-       __ATTR(mgmt, S_IRUGO | S_IWUSR, vdisk_mgmt_show, vdisk_mgmt_store);
-
-static const struct attribute *vdisk_attrs[] = {
-       &vdisk_mgmt_attr.attr,
-       NULL,
-};
-
-static ssize_t vdisk_sysfs_size_show(struct kobject *kobj,
+static ssize_t vdev_sysfs_size_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf);
 static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf);
@@ -397,22 +317,24 @@ static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf);
 static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf);
-static ssize_t vdisk_sysfs_filename_show(struct kobject *kobj,
+static ssize_t vdev_sysfs_filename_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf);
 static ssize_t vdisk_sysfs_resync_size_store(struct kobject *kobj,
        struct kobj_attribute *attr, const char *buf, size_t count);
-static ssize_t vdisk_sysfs_t10_dev_id_store(struct kobject *kobj,
+static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj,
        struct kobj_attribute *attr, const char *buf, size_t count);
-static ssize_t vdisk_sysfs_t10_dev_id_show(struct kobject *kobj,
+static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf);
+static ssize_t vdev_sysfs_usn_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf);
 
 static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj,
        struct kobj_attribute *attr, const char *buf, size_t count);
 
-static struct kobj_attribute vdisk_size_attr =
-       __ATTR(size, S_IRUGO, vdisk_sysfs_size_show, NULL);
+static struct kobj_attribute vdev_size_attr =
+       __ATTR(size_mb, S_IRUGO, vdev_sysfs_size_show, NULL);
 static struct kobj_attribute vdisk_blocksize_attr =
-       __ATTR(block_size, S_IRUGO, vdisk_sysfs_blocksize_show, NULL);
+       __ATTR(blocksize, S_IRUGO, vdisk_sysfs_blocksize_show, NULL);
 static struct kobj_attribute vdisk_rd_only_attr =
        __ATTR(read_only, S_IRUGO, vdisk_sysfs_rd_only_show, NULL);
 static struct kobj_attribute vdisk_wt_attr =
@@ -424,19 +346,21 @@ static struct kobj_attribute vdisk_o_direct_attr =
 static struct kobj_attribute vdisk_removable_attr =
        __ATTR(removable, S_IRUGO, vdisk_sysfs_removable_show, NULL);
 static struct kobj_attribute vdisk_filename_attr =
-       __ATTR(filename, S_IRUGO, vdisk_sysfs_filename_show, NULL);
+       __ATTR(filename, S_IRUGO, vdev_sysfs_filename_show, NULL);
 static struct kobj_attribute vdisk_resync_size_attr =
        __ATTR(resync_size, S_IWUSR, NULL, vdisk_sysfs_resync_size_store);
-static struct kobj_attribute vdisk_t10_dev_id_attr =
-       __ATTR(t10_dev_id, S_IWUSR|S_IRUGO, vdisk_sysfs_t10_dev_id_show,
-               vdisk_sysfs_t10_dev_id_store);
+static struct kobj_attribute vdev_t10_dev_id_attr =
+       __ATTR(t10_dev_id, S_IWUSR|S_IRUGO, vdev_sysfs_t10_dev_id_show,
+               vdev_sysfs_t10_dev_id_store);
+static struct kobj_attribute vdev_usn_attr =
+       __ATTR(usn, S_IRUGO, vdev_sysfs_usn_show, NULL);
 
 static struct kobj_attribute vcdrom_filename_attr =
-       __ATTR(filename, S_IRUGO|S_IWUSR, vdisk_sysfs_filename_show,
+       __ATTR(filename, S_IRUGO|S_IWUSR, vdev_sysfs_filename_show,
                vcdrom_sysfs_filename_store);
 
 static const struct attribute *vdisk_fileio_attrs[] = {
-       &vdisk_size_attr.attr,
+       &vdev_size_attr.attr,
        &vdisk_blocksize_attr.attr,
        &vdisk_rd_only_attr.attr,
        &vdisk_wt_attr.attr,
@@ -445,35 +369,38 @@ static const struct attribute *vdisk_fileio_attrs[] = {
        &vdisk_removable_attr.attr,
        &vdisk_filename_attr.attr,
        &vdisk_resync_size_attr.attr,
-       &vdisk_t10_dev_id_attr.attr,
+       &vdev_t10_dev_id_attr.attr,
+       &vdev_usn_attr.attr,
        NULL,
 };
 
 static const struct attribute *vdisk_blockio_attrs[] = {
-       &vdisk_size_attr.attr,
+       &vdev_size_attr.attr,
        &vdisk_blocksize_attr.attr,
        &vdisk_rd_only_attr.attr,
        &vdisk_removable_attr.attr,
        &vdisk_filename_attr.attr,
        &vdisk_resync_size_attr.attr,
-       &vdisk_t10_dev_id_attr.attr,
+       &vdev_t10_dev_id_attr.attr,
+       &vdev_usn_attr.attr,
        NULL,
 };
 
 static const struct attribute *vdisk_nullio_attrs[] = {
-       &vdisk_size_attr.attr,
+       &vdev_size_attr.attr,
        &vdisk_blocksize_attr.attr,
        &vdisk_rd_only_attr.attr,
        &vdisk_removable_attr.attr,
-       &vdisk_t10_dev_id_attr.attr,
+       &vdev_t10_dev_id_attr.attr,
+       &vdev_usn_attr.attr,
        NULL,
 };
 
 static const struct attribute *vcdrom_attrs[] = {
-       &vdisk_size_attr.attr,
-       &vdisk_removable_attr.attr,
+       &vdev_size_attr.attr,
        &vcdrom_filename_attr.attr,
-       &vdisk_t10_dev_id_attr.attr,
+       &vdev_t10_dev_id_attr.attr,
+       &vdev_usn_attr.attr,
        NULL,
 };
 
@@ -483,11 +410,9 @@ static const struct attribute *vcdrom_attrs[] = {
 static DEFINE_MUTEX(scst_vdisk_mutex);
 static DEFINE_RWLOCK(vdisk_t10_dev_id_rwlock);
 
-/* Both protected by scst_vdisk_mutex */
-static LIST_HEAD(vdisk_dev_list);
-static LIST_HEAD(vcdrom_dev_list);
+/* Protected by scst_vdisk_mutex */
+static LIST_HEAD(vdev_list);
 
-static struct vdisk_fileio_type fileio_type;
 static struct kmem_cache *vdisk_thr_cachep;
 
 /*
@@ -514,18 +439,22 @@ static struct scst_dev_type vdisk_file_devtype = {
        .read_proc =            vdisk_read_proc,
        .write_proc =           vdisk_write_proc,
 #else
-       .devt_attrs =           vdisk_attrs,
+       .add_device =           vdisk_add_fileio_device,
+       .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",
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
        .trace_flags =          &trace_flag,
        .trace_tbl =            vdisk_local_trace_tbl,
+#ifndef CONFIG_SCST_PROC
        .trace_tbl_help =       VDISK_TRACE_TLB_HELP,
 #endif
+#endif
 };
 
-static struct vdisk_blockio_type blockio_type;
 static struct kmem_cache *blockio_work_cachep;
 
 static struct scst_dev_type vdisk_blk_devtype = {
@@ -546,18 +475,22 @@ static struct scst_dev_type vdisk_blk_devtype = {
        .exec =                 vdisk_do_job,
        .task_mgmt_fn =         vdisk_task_mgmt_fn,
 #ifndef CONFIG_SCST_PROC
-       .devt_attrs =           vdisk_attrs,
+       .add_device =           vdisk_add_blockio_device,
+       .del_device =           vdisk_del_device,
        .dev_attrs =            vdisk_blockio_attrs,
+       .add_device_parameters_help = "filename, blocksize, read_only, "
+               "removable",
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
        .trace_flags =          &trace_flag,
        .trace_tbl =            vdisk_local_trace_tbl,
+#ifndef CONFIG_SCST_PROC
        .trace_tbl_help =       VDISK_TRACE_TLB_HELP,
 #endif
+#endif
 };
 
-static struct vdisk_blockio_type nullio_type;
 static struct scst_dev_type vdisk_null_devtype = {
        .name =                 "vdisk_nullio",
        .type =                 TYPE_DISK,
@@ -576,18 +509,21 @@ static struct scst_dev_type vdisk_null_devtype = {
        .exec =                 vdisk_do_job,
        .task_mgmt_fn =         vdisk_task_mgmt_fn,
 #ifndef CONFIG_SCST_PROC
-       .devt_attrs =           vdisk_attrs,
+       .add_device =           vdisk_add_nullio_device,
+       .del_device =           vdisk_del_device,
        .dev_attrs =            vdisk_nullio_attrs,
+       .add_device_parameters_help = "blocksize, read_only, removable",
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
        .trace_flags =          &trace_flag,
        .trace_tbl =            vdisk_local_trace_tbl,
+#ifndef CONFIG_SCST_PROC
        .trace_tbl_help =       VDISK_TRACE_TLB_HELP,
 #endif
+#endif
 };
 
-static struct vcdrom_type vcdrom_type;
 static struct scst_dev_type vcdrom_devtype = {
        .name =                 "vcdrom",
        .type =                 TYPE_ROM,
@@ -607,15 +543,18 @@ static struct scst_dev_type vcdrom_devtype = {
        .read_proc =            vcdrom_read_proc,
        .write_proc =           vcdrom_write_proc,
 #else
-       .devt_attrs =           vdisk_attrs,
+       .add_device =           vcdrom_add_device,
+       .del_device =           vcdrom_del_device,
        .dev_attrs =            vcdrom_attrs,
 #endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
        .default_trace_flags =  SCST_DEFAULT_DEV_LOG_FLAGS,
        .trace_flags =          &trace_flag,
        .trace_tbl =            vdisk_local_trace_tbl,
+#ifndef CONFIG_SCST_PROC
        .trace_tbl_help =       VDISK_TRACE_TLB_HELP,
 #endif
+#endif
 };
 
 static struct scst_vdisk_thr nullio_thr_data;
@@ -638,15 +577,15 @@ static int scst_vdisk_ID;
 module_param_named(scst_vdisk_ID, scst_vdisk_ID, int, S_IRUGO);
 MODULE_PARM_DESC(scst_vdisk_ID, "SCST virtual disk subsystem ID");
 
-/**************************************************************
- *  Function:  vdev_open_fd
- *
- *  Argument:
- *
- *  Returns :  fd, use IS_ERR(fd) to get error status
- *
- *  Description:
- *************************************************************/
+static const char *vdev_get_filename(const struct scst_vdisk_dev *virt_dev)
+{
+       if (virt_dev->filename != NULL)
+               return virt_dev->filename;
+       else
+               return "none";
+}
+
+/* Returns fd, use IS_ERR(fd) to get error status */
 static struct file *vdev_open_fd(const struct scst_vdisk_dev *virt_dev)
 {
        int open_flags = 0;
@@ -663,24 +602,15 @@ static struct file *vdev_open_fd(const struct scst_vdisk_dev *virt_dev)
        if (virt_dev->wt_flag && !virt_dev->nv_cache)
                open_flags |= O_SYNC;
        TRACE_DBG("Opening file %s, flags 0x%x",
-                 virt_dev->file_name, open_flags);
-       fd = filp_open(virt_dev->file_name, O_LARGEFILE | open_flags, 0600);
+                 virt_dev->filename, open_flags);
+       fd = filp_open(virt_dev->filename, O_LARGEFILE | open_flags, 0600);
 
        TRACE_EXIT();
        return fd;
 }
 
-/**************************************************************
- *  Function:  vdisk_get_file_size
- *
- *  Argument:
- *
- *  Returns :  0 on success and file size in *file_size,
- *            error code otherwise
- *
- *  Description:
- *************************************************************/
-static int vdisk_get_file_size(const char *file_name, bool blockio,
+/* Returns 0 on success and file size in *file_size, error code otherwise */
+static int vdisk_get_file_size(const char *filename, bool blockio,
        loff_t *file_size)
 {
        struct inode *inode;
@@ -691,17 +621,17 @@ static int vdisk_get_file_size(const char *file_name, bool blockio,
 
        *file_size = 0;
 
-       fd = filp_open(file_name, O_LARGEFILE | O_RDONLY, 0600);
+       fd = filp_open(filename, O_LARGEFILE | O_RDONLY, 0600);
        if (IS_ERR(fd)) {
                res = PTR_ERR(fd);
-               PRINT_ERROR("filp_open(%s) returned error %d", file_name, res);
+               PRINT_ERROR("filp_open(%s) returned error %d", filename, res);
                goto out;
        }
 
        inode = fd->f_dentry->d_inode;
 
        if (blockio && !S_ISBLK(inode->i_mode)) {
-               PRINT_ERROR("File %s is NOT a block device", file_name);
+               PRINT_ERROR("File %s is NOT a block device", filename);
                res = -EINVAL;
                goto out_close;
        }
@@ -725,25 +655,11 @@ out:
        return res;
 }
 
-/**************************************************************
- *  Function:  vdisk_attach
- *
- *  Argument:
- *
- *  Returns :  1 if attached, error code otherwise
- *
- *  Description:
- *************************************************************/
 static int vdisk_attach(struct scst_device *dev)
 {
        int res = 0;
        loff_t err;
        struct scst_vdisk_dev *virt_dev = NULL, *vv;
-       struct list_head *vd;
-       uint64_t dev_id_num;
-       int dev_id_len;
-       char dev_id_str[17];
-       int32_t i;
 
        TRACE_ENTRY();
 
@@ -755,15 +671,11 @@ static int vdisk_attach(struct scst_device *dev)
                goto out;
        }
 
-       vd = (dev->type == TYPE_DISK) ?
-                               &vdisk_dev_list :
-                               &vcdrom_dev_list;
-
        /*
         * scst_vdisk_mutex must be already taken before
         * scst_register_virtual_device()
         */
-       list_for_each_entry(vv, vd, vdisk_dev_list_entry) {
+       list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
                if (strcmp(vv->name, dev->virt_name) == 0) {
                        virt_dev = vv;
                        break;
@@ -779,14 +691,12 @@ static int vdisk_attach(struct scst_device *dev)
        virt_dev->dev = dev;
 
        dev->rd_only = virt_dev->rd_only;
-       if (dev->type == TYPE_ROM)
-               dev->rd_only = 1;
 
        if (!virt_dev->cdrom_empty) {
                if (virt_dev->nullio)
                        err = VDISK_NULLIO_SIZE;
                else {
-                       res = vdisk_get_file_size(virt_dev->file_name,
+                       res = vdisk_get_file_size(virt_dev->filename,
                                virt_dev->blockio, &err);
                        if (res != 0)
                                goto out;
@@ -803,7 +713,7 @@ static int vdisk_attach(struct scst_device *dev)
                      "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld,"
                      " cyln=%lld%s)",
                      (dev->type == TYPE_DISK) ? "disk" : "cdrom",
-                     virt_dev->name, virt_dev->file_name,
+                     virt_dev->name, vdev_get_filename(virt_dev),
                      virt_dev->file_size >> 20, virt_dev->block_size,
                      (long long unsigned int)virt_dev->nblocks,
                      (long long unsigned int)virt_dev->nblocks/64/32,
@@ -825,33 +735,12 @@ static int vdisk_attach(struct scst_device *dev)
        dev->swp = DEF_SWP;
        dev->tas = DEF_TAS;
 
-       /* generate T10 device id */
-       dev_id_num = vdisk_gen_dev_id_num(virt_dev);
-       dev_id_len = scnprintf(dev_id_str, sizeof(dev_id_str), "%llx",
-                               dev_id_num);
-
-       write_lock_bh(&vdisk_t10_dev_id_rwlock);
-       i = strlen(virt_dev->name) + 1; /* for ' ' */
-       memset(virt_dev->t10_dev_id, ' ', i + dev_id_len);
-       memcpy(virt_dev->t10_dev_id, virt_dev->name, i-1);
-       memcpy(virt_dev->t10_dev_id + i, dev_id_str, dev_id_len);
-       write_unlock_bh(&vdisk_t10_dev_id_rwlock);
-
 out:
        TRACE_EXIT();
        return res;
 }
 
-/************************************************************
- *  Function:  vdisk_detach
- *
- *  Argument:
- *
- *  Returns :  None
- *
- *  Description:  Called to detach this device type driver.
- *               Scst_mutex supposed to be held.
- ************************************************************/
+/* scst_mutex supposed to be held */
 static void vdisk_detach(struct scst_device *dev)
 {
        struct scst_vdisk_dev *virt_dev =
@@ -862,7 +751,7 @@ static void vdisk_detach(struct scst_device *dev)
        TRACE_DBG("virt_id %d", dev->virt_id);
 
        PRINT_INFO("Detached SCSI target virtual device %s (\"%s\")",
-                     virt_dev->name, virt_dev->file_name);
+                     virt_dev->name, vdev_get_filename(virt_dev));
 
        /* virt_dev will be freed by the caller */
        dev->dh_priv = NULL;
@@ -917,7 +806,7 @@ static struct scst_vdisk_thr *vdisk_init_thr_data(
                res->fd = vdev_open_fd(virt_dev);
                if (IS_ERR(res->fd)) {
                        PRINT_ERROR("filp_open(%s) returned an error %ld",
-                               virt_dev->file_name, PTR_ERR(res->fd));
+                               virt_dev->filename, PTR_ERR(res->fd));
                        goto out_free;
                }
                if (virt_dev->blockio)
@@ -1300,49 +1189,18 @@ static int vdisk_get_block_shift(struct scst_cmd *cmd)
        return virt_dev->block_shift;
 }
 
-/********************************************************************
- *  Function:  vdisk_parse
- *
- *  Argument:
- *
- *  Returns :  The state of the command
- *
- *  Description:  This does the parsing of the command
- *
- *  Note:  Not all states are allowed on return
- ********************************************************************/
 static int vdisk_parse(struct scst_cmd *cmd)
 {
        scst_sbc_generic_parse(cmd, vdisk_get_block_shift);
        return SCST_CMD_STATE_DEFAULT;
 }
 
-/********************************************************************
- *  Function:  vcdrom_parse
- *
- *  Argument:
- *
- *  Returns :  The state of the command
- *
- *  Description:  This does the parsing of the command
- *
- *  Note:  Not all states are allowed on return
- ********************************************************************/
 static int vcdrom_parse(struct scst_cmd *cmd)
 {
        scst_cdrom_generic_parse(cmd, vdisk_get_block_shift);
        return SCST_CMD_STATE_DEFAULT;
 }
 
-/********************************************************************
- *  Function:  vcdrom_exec
- *
- *  Argument:
- *
- *  Returns :
- *
- *  Description:
- ********************************************************************/
 static int vcdrom_exec(struct scst_cmd *cmd)
 {
        int res = SCST_EXEC_COMPLETED;
@@ -1388,12 +1246,12 @@ out_done:
        goto out;
 }
 
-static uint64_t vdisk_gen_dev_id_num(struct scst_vdisk_dev *virt_dev)
+static uint64_t vdisk_gen_dev_id_num(const char *virt_dev_name)
 {
        unsigned int dev_id_num, i;
 
-       for (dev_id_num = 0, i = 0; i < strlen(virt_dev->name); i++) {
-               unsigned int rv = random_values[(int)(virt_dev->name[i])];
+       for (dev_id_num = 0, i = 0; i < strlen(virt_dev_name); i++) {
+               unsigned int rv = random_values[(int)(virt_dev_name[i])];
                /* Do some rotating of the bits */
                dev_id_num ^= ((rv << i) | (rv >> (32 - i)));
        }
@@ -2977,7 +2835,7 @@ static void vdisk_report_registering(const struct scst_vdisk_dev *virt_dev)
        int i, j;
 
        i = snprintf(buf, sizeof(buf), "Registering virtual %s device %s ",
-               virt_dev->vdt->vdt_name, virt_dev->name);
+               virt_dev->vdev_devt->name, virt_dev->name);
        j = i;
 
        if (virt_dev->wt_flag)
@@ -3025,13 +2883,12 @@ static int vdisk_resync_size(struct scst_vdisk_dev *virt_dev)
                goto out;
        }
 
-       if (!virt_dev->nullio) {
-               res = vdisk_get_file_size(virt_dev->file_name,
-                               virt_dev->blockio, &file_size);
-               if (res != 0)
-                       goto out_unlock;
-       } else
-               file_size = VDISK_NULLIO_SIZE;
+       sBUG_ON(virt_dev->nullio);
+
+       res = vdisk_get_file_size(virt_dev->filename,
+                       virt_dev->blockio, &file_size);
+       if (res != 0)
+               goto out_unlock;
 
        if (file_size == virt_dev->file_size) {
                PRINT_INFO("Size of virtual disk %s remained the same",
@@ -3068,453 +2925,688 @@ out:
        return res;
 }
 
-static void vdev_init(struct vdev_type *vdt, struct scst_vdisk_dev *virt_dev)
-{
-       memset(virt_dev, 0, sizeof(*virt_dev));
-       spin_lock_init(&virt_dev->flags_lock);
-       mutex_init(&virt_dev->vdev_sysfs_mutex);
-       virt_dev->vdt = vdt;
-
-       virt_dev->block_size = DEF_DISK_BLOCKSIZE;
-       virt_dev->block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
-
-       return;
-}
-
-static struct scst_vdisk_dev *vdev_create(struct vdev_type *vdt)
+static struct scst_vdisk_dev *vdev_create(struct scst_dev_type *devt,
+       const char *name)
 {
        struct scst_vdisk_dev *virt_dev;
+       uint64_t dev_id_num;
+       int dev_id_len;
+       char dev_id_str[17];
+       int32_t i;
 
-       virt_dev = kmalloc(sizeof(*virt_dev), GFP_KERNEL);
+       virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
        if (virt_dev == NULL) {
                PRINT_ERROR("Allocation of virtual device %s failed",
-                       vdt->vdt_name);
+                       devt->name);
                goto out;
        }
 
-       vdt->vfns.vdev_init(vdt, virt_dev);
-
-out:
-       return virt_dev;
-}
-
-static void vdev_deinit(struct scst_vdisk_dev *virt_dev)
-{
-       kfree(virt_dev->file_name);
-       return;
-}
-
-static void vdev_destroy(struct scst_vdisk_dev *virt_dev)
-{
-       virt_dev->vdt->vfns.vdev_deinit(virt_dev);
-       kfree(virt_dev);
-       return;
-}
+       spin_lock_init(&virt_dev->flags_lock);
+       mutex_init(&virt_dev->vdev_sysfs_mutex);
+       virt_dev->vdev_devt = devt;
 
-static int vdisk_fileio_pre_register(struct scst_vdisk_dev *virt_dev)
-{
-       int res = 0;
+       virt_dev->rd_only = DEF_RD_ONLY;
+       virt_dev->removable = DEF_REMOVABLE;
 
-       if ((virt_dev->file_name == NULL) ||
-           (strcmp(virt_dev->file_name, VDEV_NONE_FILENAME) == 0)) {
-               PRINT_ERROR("%s", "File name required");
-               res = -EINVAL;
-               goto out;
-       }
+       virt_dev->block_size = DEF_DISK_BLOCKSIZE;
+       virt_dev->block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
 
-       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);
-               res = -EINVAL;
-               goto out;
-       }
+       strncpy(virt_dev->name, name, sizeof(virt_dev->name));
 
-out:
-       return res;
-}
+       dev_id_num = vdisk_gen_dev_id_num(virt_dev->name);
+       dev_id_len = scnprintf(dev_id_str, sizeof(dev_id_str), "%llx",
+                               dev_id_num);
 
-static int vdisk_blockio_pre_register(struct scst_vdisk_dev *virt_dev)
-{
-       int res = 0;
+       i = strlen(virt_dev->name) + 1; /* for ' ' */
+       memset(virt_dev->t10_dev_id, ' ', i + dev_id_len);
+       memcpy(virt_dev->t10_dev_id, virt_dev->name, i-1);
+       memcpy(virt_dev->t10_dev_id + i, dev_id_str, dev_id_len);
+       TRACE_DBG("t10_dev_id %s", virt_dev->t10_dev_id);
 
-       if ((virt_dev->file_name == NULL) ||
-           (strcmp(virt_dev->file_name, VDEV_NONE_FILENAME) == 0)) {
-               PRINT_ERROR("%s", "File name required");
-               res = -EINVAL;
-               goto out;
-       }
+       scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx", dev_id_num);
+       TRACE_DBG("usn %s", virt_dev->usn);
 
 out:
-       return res;
-}
-
-static void vdisk_blockio_init(struct vdev_type *vdt,
-       struct scst_vdisk_dev *virt_dev)
-{
-       struct vdisk_blockio_type *v;
-
-       v = container_of(vdt, struct vdisk_blockio_type, parent_vdt);
-
-       v->parent_vdt_vfns.vdev_init(vdt, virt_dev);
-
-       virt_dev->blockio = 1;
-       return;
+       return virt_dev;
 }
 
-static void vdisk_nullio_init(struct vdev_type *vdt,
-       struct scst_vdisk_dev *virt_dev)
+static void vdev_destroy(struct scst_vdisk_dev *virt_dev)
 {
-       struct vdisk_nullio_type *v;
-
-       v = container_of(vdt, struct vdisk_nullio_type, parent_vdt);
-
-       v->parent_vdt_vfns.vdev_init(vdt, virt_dev);
-
-       virt_dev->nullio = 1;
+       kfree(virt_dev->filename);
+       kfree(virt_dev);
        return;
 }
 
-static int vdisk_parse_option(struct scst_vdisk_dev *virt_dev, char *p)
+/* scst_vdisk_mutex supposed to be held */
+static struct scst_vdisk_dev *vdev_find(const char *name)
 {
-       int res = -EINVAL;
+       struct scst_vdisk_dev *res, *vv;
 
        TRACE_ENTRY();
 
-       if (!strncmp("READ_ONLY", p, 9)) {
-               res = 9;
-               virt_dev->rd_only = 1;
-               TRACE_DBG("%s", "READ_ONLY");
-       } else if (!strncmp("REMOVABLE", p, 9)) {
-               res = 9;
-               virt_dev->removable = 1;
-               TRACE_DBG("%s", "REMOVABLE");
+       res = NULL;
+       list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
+               if (strcmp(vv->name, name) == 0) {
+                       res = vv;
+                       break;
+               }
        }
 
-       TRACE_EXIT_RES(res);
+       TRACE_EXIT_HRES((unsigned long)res);
        return res;
 }
 
-static int vdisk_fileio_parse_option(struct scst_vdisk_dev *virt_dev, char *p)
-{
-       int res;
-
-       TRACE_ENTRY();
-
-       res = vdisk_parse_option(virt_dev, p);
-       if (res >= 0)
-               goto out;
-
-       if (!strncmp("WRITE_THROUGH", p, 13)) {
-               res = 13;
-               virt_dev->wt_flag = 1;
-               TRACE_DBG("%s", "WRITE_THROUGH");
-       } else if (!strncmp("NV_CACHE", p, 8)) {
-               res = 8;
-               virt_dev->nv_cache = 1;
-               TRACE_DBG("%s", "NON-VOLATILE CACHE");
-       } else if (!strncmp("O_DIRECT", p, 8)) {
-               res = 8;
-#if 0
-               virt_dev->o_direct_flag = 1;
-               TRACE_DBG("%s", "O_DIRECT");
-#else
-               PRINT_INFO("%s flag doesn't currently"
-                       " work, ignoring it, use fileio_tgt "
-                       "in O_DIRECT mode instead", "O_DIRECT");
-#endif
-       }
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
+#ifndef CONFIG_SCST_PROC
 
 /* scst_vdisk_mutex supposed to be held */
-static int vdev_open(struct vdev_type *vdt, char *p, const char *name)
+static int vdev_fileio_add_device(const char *device_name, char *params)
 {
        int res = 0;
-       char *file_name;
+       unsigned long val;
+       char *param, *p, *pp;
        struct scst_vdisk_dev *virt_dev;
-       size_t len;
 
        TRACE_ENTRY();
 
-       virt_dev = vdt->vfns.vdev_find(name);
-       if (virt_dev != NULL) {
-               PRINT_ERROR("Virtual device with name %s already exist", name);
-               res = -EINVAL;
-               goto out;
-       }
-
-       while (isspace(*p) && *p != '\0')
-               p++;
-       file_name = p;
-       if ((*file_name == '/') || (strcmp(file_name, VDEV_NONE_FILENAME) == 0)) {
-               while (!isspace(*p) && *p != '\0')
-                       p++;
-               *p++ = '\0';
-
-               TRACE_DBG("file name %s", file_name);
-
-               while (isspace(*p) && *p != '\0')
-                       p++;
-       } else {
-               TRACE_DBG("file name %s", "not specified");
-               file_name = NULL;
-       }
-
-       virt_dev = vdt->vfns.vdev_create(vdt);
+       virt_dev = vdev_create(&vdisk_file_devtype, device_name);
        if (virt_dev == NULL) {
-               TRACE(TRACE_OUT_OF_MEM, "Creation of virt_dev %s failed", name);
                res = -ENOMEM;
                goto out;
        }
 
-       if (isdigit(*p)) {
-               uint32_t block_size;
-               int block_shift;
-               char *pp;
+       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)
+                       break;
 
-               block_size = simple_strtoul(p, &pp, 0);
-               p = pp;
-               if ((*p != '\0') && !isspace(*p)) {
-                       PRINT_ERROR("Parse error: \"%s\"", p);
+               p = scst_get_next_lexem(&param);
+               if (*p == '\0') {
+                       PRINT_ERROR("Syntax error at %s (device %s)",
+                               param, device_name);
                        res = -EINVAL;
-                       goto out_free_vdev;
+                       goto out_destroy;
                }
-               while (isspace(*p) && *p != '\0')
-                       p++;
 
-               block_shift = scst_calc_block_shift(block_size);
-               if (block_shift < 9) {
+               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_free_vdev;
+                       goto out_destroy;
                }
 
-               TRACE_DBG("block_size %d, block_shift %d", block_size,
-                       block_shift);
-
-               virt_dev->block_size = block_size;
-               virt_dev->block_shift = block_shift;
-       }
-
-       while (*p != '\0') {
-               while (isspace(*p) && *p != '\0')
-                       p++;
-
-               if (vdt->vfns.parse_option != NULL)
-                       res = vdt->vfns.parse_option(virt_dev, p);
-               else
+               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 (res < 0) {
-                       PRINT_ERROR("Unknown option \"%s\"", p);
-                       goto out_free_vdev;
-               } else if (res == 0)
-                       break;
-
-               p += res;
+               if (!strcasecmp("filename", p)) {
+                       if (*pp != '/') {
+                               PRINT_ERROR("Filename %s must be global "
+                                       "(device %s)", pp, device_name);
+                               res = -EINVAL;
+                               goto out_destroy;
+                       }
 
-               if (!isspace(*p) && (*p != '\0')) {
-                       PRINT_ERROR("Syntax error on %s", p);
-                       goto out_free_vdev;
+                       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;
                }
-       }
-
-       strcpy(virt_dev->name, name);
 
-       scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx",
-                       vdisk_gen_dev_id_num(virt_dev));
-       TRACE_DBG("usn %s", virt_dev->usn);
+               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 (file_name != NULL) {
-               len = strlen(file_name) + 1;
-               virt_dev->file_name = kmalloc(len, GFP_KERNEL);
-               if (virt_dev->file_name == NULL) {
-                       TRACE(TRACE_OUT_OF_MEM, "Allocation of file_name %s "
-                               "failed", file_name);
-                       res = -ENOMEM;
-                       goto out_free_vdev;
+               if (!strcasecmp("write_through", p)) {
+                       virt_dev->wt_flag = val;
+                       TRACE_DBG("WRITE THROUGH %d", virt_dev->wt_flag);
+               } else if (!strcasecmp("nv_cache", p)) {
+                       virt_dev->nv_cache = val;
+                       TRACE_DBG("NON-VOLATILE CACHE %d", virt_dev->nv_cache);
+               } else if (!strcasecmp("o_direct", p)) {
+#if 0
+                       virt_dev->o_direct_flag = val;
+                       TRACE_DBG("O_DIRECT %d", virt_dev->o_direct_flag);
+#else
+                       PRINT_INFO("O_DIRECT flag doesn't currently"
+                               " work, ignoring it, use fileio_tgt "
+                               "in O_DIRECT mode instead (device %s)", device_name);
+#endif
+               } else 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;
                }
-               strlcpy(virt_dev->file_name, file_name, len);
        }
 
-       if (vdt->vfns.pre_register != NULL) {
-               res = vdt->vfns.pre_register(virt_dev);
-               if (res != 0)
-                       goto out_free_vpath;
+       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);
+               res = -EINVAL;
+               goto out_destroy;
+       }
+
+       if (virt_dev->filename == NULL) {
+               PRINT_ERROR("File name required (device %s)", virt_dev->name);
+               res = -EINVAL;
+               goto out_destroy;
        }
 
-       vdt->vfns.vdev_add(virt_dev);
+       list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
 
        vdisk_report_registering(virt_dev);
-       virt_dev->virt_id = scst_register_virtual_device(vdt->vdt_devt,
-                                                virt_dev->name);
+
+       virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+                                       virt_dev->name);
        if (virt_dev->virt_id < 0) {
                res = virt_dev->virt_id;
                goto out_del;
        }
 
-       TRACE_DBG("Added virt_dev (name %s, file name %s, id %d, block size "
-               "%d) to vdisk_dev_list", virt_dev->name, virt_dev->file_name,
-               virt_dev->virt_id, virt_dev->block_size);
+       TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+               virt_dev->virt_id);
 
 out:
        TRACE_EXIT_RES(res);
        return res;
 
 out_del:
-       vdt->vfns.vdev_del(virt_dev);
-
-out_free_vpath:
-       kfree(virt_dev->file_name);
+       list_del(&virt_dev->vdev_list_entry);
 
-out_free_vdev:
-       kfree(virt_dev);
+out_destroy:
+       vdev_destroy(virt_dev);
        goto out;
 }
 
 /* scst_vdisk_mutex supposed to be held */
-static int vdev_close(struct vdev_type *vdt, const char *name)
+static int vdev_blockio_add_device(const char *device_name, char *params)
 {
        int res = 0;
+       unsigned long val;
+       char *param, *p, *pp;
        struct scst_vdisk_dev *virt_dev;
 
        TRACE_ENTRY();
 
-       virt_dev = vdt->vfns.vdev_find(name);
+       virt_dev = vdev_create(&vdisk_blk_devtype, device_name);
        if (virt_dev == NULL) {
-               PRINT_ERROR("Device %s not found", name);
-               res = -EINVAL;
+               res = -ENOMEM;
                goto out;
        }
 
-       scst_unregister_virtual_device(virt_dev->virt_id);
+       virt_dev->blockio = 1;
 
-       PRINT_INFO("Virtual device %s unregistered", virt_dev->name);
-       TRACE_DBG("virt_id %d unregister", virt_dev->virt_id);
+       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;
+               }
 
-       vdt->vfns.vdev_del(virt_dev);
-       vdt->vfns.vdev_destroy(virt_dev);
+               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;
+               }
+       }
+
+       if (virt_dev->filename == NULL) {
+               PRINT_ERROR("File name required (device %s)", virt_dev->name);
+               res = -EINVAL;
+               goto out_destroy;
+       }
+
+       list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+       vdisk_report_registering(virt_dev);
+
+       virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+                                       virt_dev->name);
+       if (virt_dev->virt_id < 0) {
+               res = virt_dev->virt_id;
+               goto out_del;
+       }
+
+       TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+               virt_dev->virt_id);
 
 out:
        TRACE_EXIT_RES(res);
        return res;
+
+out_del:
+       list_del(&virt_dev->vdev_list_entry);
+
+out_destroy:
+       vdev_destroy(virt_dev);
+       goto out;
 }
 
 /* scst_vdisk_mutex supposed to be held */
-static void vdev_del(struct scst_vdisk_dev *virt_dev)
+static int vdev_nullio_add_device(const char *device_name, char *params)
 {
+       int res = 0;
+       unsigned long val;
+       char *param, *p, *pp;
+       struct scst_vdisk_dev *virt_dev;
+
        TRACE_ENTRY();
 
-       list_del(&virt_dev->vdisk_dev_list_entry);
+       virt_dev = vdev_create(&vdisk_null_devtype, device_name);
+       if (virt_dev == NULL) {
+               res = -ENOMEM;
+               goto out;
+       }
 
-       TRACE_EXIT();
-       return;
+       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;
+               }
+       }
+
+       list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+       vdisk_report_registering(virt_dev);
+
+       virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+                                       virt_dev->name);
+       if (virt_dev->virt_id < 0) {
+               res = virt_dev->virt_id;
+               goto out_del;
+       }
+
+       TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+               virt_dev->virt_id);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_del:
+       list_del(&virt_dev->vdev_list_entry);
+
+out_destroy:
+       vdev_destroy(virt_dev);
+       goto out;
 }
 
-/* scst_vdisk_mutex supposed to be held */
-static void vdisk_add(struct scst_vdisk_dev *virt_dev)
+static ssize_t vdisk_add_fileio_device(const char *device_name, char *params)
 {
+       int res;
+
        TRACE_ENTRY();
 
-       list_add_tail(&virt_dev->vdisk_dev_list_entry, &vdisk_dev_list);
+       if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       res = vdev_fileio_add_device(device_name, params);
 
-       TRACE_EXIT();
-       return;
+       mutex_unlock(&scst_vdisk_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
 }
 
-/* scst_vdisk_mutex supposed to be held */
-static struct scst_vdisk_dev *vdisk_find(const char *name)
+static ssize_t vdisk_add_blockio_device(const char *device_name, char *params)
 {
-       struct scst_vdisk_dev *res, *vv;
+       int res;
 
        TRACE_ENTRY();
 
-       res = NULL;
-       list_for_each_entry(vv, &vdisk_dev_list, vdisk_dev_list_entry) {
-               if (strcmp(vv->name, name) == 0) {
-                       res = vv;
-                       break;
-               }
+       if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+               res = -EINTR;
+               goto out;
        }
 
-       TRACE_EXIT_HRES((unsigned long)res);
+       res = vdev_blockio_add_device(device_name, params);
+
+       mutex_unlock(&scst_vdisk_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
        return res;
+
 }
 
+static ssize_t vdisk_add_nullio_device(const char *device_name, char *params)
+{
+       int res;
+
+       TRACE_ENTRY();
+
+       if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       res = vdev_nullio_add_device(device_name, params);
+
+       mutex_unlock(&scst_vdisk_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+}
+
+#endif /* CONFIG_SCST_PROC */
+
 /* scst_vdisk_mutex supposed to be held */
-static void vcdrom_add(struct scst_vdisk_dev *virt_dev)
+static void vdev_del_device(struct scst_vdisk_dev *virt_dev)
 {
        TRACE_ENTRY();
 
-       list_add_tail(&virt_dev->vdisk_dev_list_entry, &vcdrom_dev_list);
+       scst_unregister_virtual_device(virt_dev->virt_id);
+
+       list_del(&virt_dev->vdev_list_entry);
+
+       PRINT_INFO("Virtual device %s unregistered", virt_dev->name);
+       TRACE_DBG("virt_id %d unregistered", virt_dev->virt_id);
+
+       vdev_destroy(virt_dev);
 
-       TRACE_EXIT();
        return;
 }
 
-/* scst_vdisk_mutex supposed to be held */
-static struct scst_vdisk_dev *vcdrom_find(const char *name)
+#ifndef CONFIG_SCST_PROC
+
+static int vdisk_del_device(const char *device_name)
 {
-       struct scst_vdisk_dev *res, *vv;
+       int res = 0;
+       struct scst_vdisk_dev *virt_dev;
 
        TRACE_ENTRY();
 
-       res = NULL;
-       list_for_each_entry(vv, &vcdrom_dev_list, vdisk_dev_list_entry) {
-               if (strcmp(vv->name, name) == 0) {
-                       res = vv;
-                       break;
-               }
+       if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+               res = -EINTR;
+               goto out;
        }
 
-       TRACE_EXIT_HRES((unsigned long)res);
+       virt_dev = vdev_find(device_name);
+       if (virt_dev == NULL) {
+               PRINT_ERROR("Device %s not found", device_name);
+               res = -EINVAL;
+               goto out_unlock;
+       }
+
+       vdev_del_device(virt_dev);
+
+out_unlock:
+       mutex_unlock(&scst_vdisk_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
        return res;
 }
 
-static void vcdrom_init(struct vdev_type *vdt,
-       struct scst_vdisk_dev *virt_dev)
+/* scst_vdisk_mutex supposed to be held */
+static int __vcdrom_add_device(const char *device_name, char *params)
 {
-       struct vcdrom_type *vt;
+       int res = 0;
+       char *p;
+       struct scst_vdisk_dev *virt_dev;
 
-       vt = container_of(vdt, struct vcdrom_type, parent_vdt);
+       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;
+       }
 
-       vt->parent_vdt_vfns.vdev_init(vdt, virt_dev);
+       virt_dev = vdev_create(&vcdrom_devtype, device_name);
+       if (virt_dev == NULL) {
+               res = -ENOMEM;
+               goto out;
+       }
 
        virt_dev->rd_only = 1;
        virt_dev->removable = 1;
+       virt_dev->cdrom_empty = 1;
 
        virt_dev->block_size = DEF_CDROM_BLOCKSIZE;
        virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT;
 
-       return;
+       list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+       vdisk_report_registering(virt_dev);
+
+       virt_dev->virt_id = scst_register_virtual_device(virt_dev->vdev_devt,
+                                       virt_dev->name);
+       if (virt_dev->virt_id < 0) {
+               res = virt_dev->virt_id;
+               goto out_del;
+       }
+
+       TRACE_DBG("Registered virt_dev %s with id %d", virt_dev->name,
+               virt_dev->virt_id);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_del:
+       list_del(&virt_dev->vdev_list_entry);
+
+       vdev_destroy(virt_dev);
+       goto out;
 }
 
-static int vcdrom_pre_register(struct scst_vdisk_dev *virt_dev)
+static ssize_t vcdrom_add_device(const char *device_name, char *params)
+{
+       int res;
+
+       TRACE_ENTRY();
+
+       if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
+
+       res = __vcdrom_add_device(device_name, params);
+
+       mutex_unlock(&scst_vdisk_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+}
+
+static int vcdrom_del_device(const char *device_name)
 {
        int res = 0;
+       struct scst_vdisk_dev *virt_dev;
 
-       if ((virt_dev->file_name == NULL) ||
-           (strcmp(virt_dev->file_name, VDEV_NONE_FILENAME) == 0))
-               virt_dev->cdrom_empty = 1;
+       TRACE_ENTRY();
+
+       if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
+               res = -EINTR;
+               goto out;
+       }
 
-       if (virt_dev->block_size != DEF_CDROM_BLOCKSIZE) {
-               PRINT_WARNING("Block size %d for vortual device %s ignored",
-                       virt_dev->block_size, virt_dev->name);
-               virt_dev->block_size = DEF_CDROM_BLOCKSIZE;
-               virt_dev->block_shift = DEF_CDROM_BLOCKSIZE_SHIFT;
+       virt_dev = vdev_find(device_name);
+       if (virt_dev == NULL) {
+               PRINT_ERROR("Device %s not found", device_name);
+               res = -EINVAL;
+               goto out_unlock;
        }
 
+       vdev_del_device(virt_dev);
+
+out_unlock:
+       mutex_unlock(&scst_vdisk_mutex);
+
+out:
+       TRACE_EXIT_RES(res);
        return res;
 }
 
+#endif /* CONFIG_SCST_PROC */
+
 static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
        const char *buffer, int length)
 {
        loff_t err;
        char *old_fn, *i_buf, *p, *pp;
-       const char *file_name = NULL;
+       const char *filename = NULL;
        int res = 0;
 
        TRACE_ENTRY();
@@ -3533,7 +3625,7 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
 
        while (isspace(*p) && *p != '\0')
                p++;
-       file_name = p;
+       filename = p;
        p = &i_buf[length-1];
        pp = p;
        while (isspace(*p) && *p != '\0') {
@@ -3554,201 +3646,79 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
        /* To sync with detach*() functions */
        mutex_lock(&scst_mutex);
 
-       if (*file_name == '\0') {
+       if (*filename == '\0') {
                virt_dev->cdrom_empty = 1;
                TRACE_DBG("%s", "No media");
-       } else if (*file_name != '/') {
+       } else if (*filename != '/') {
                PRINT_ERROR("File path \"%s\" is not "
-                       "absolute", file_name);
+                       "absolute", filename);
                res = -EINVAL;
                goto out_unlock;
        } else
                virt_dev->cdrom_empty = 0;
 
-       old_fn = virt_dev->file_name;
+       old_fn = virt_dev->filename;
 
        if (!virt_dev->cdrom_empty) {
-               int len = strlen(file_name) + 1;
+               int len = strlen(filename) + 1;
                char *fn = kmalloc(len, GFP_KERNEL);
                if (fn == NULL) {
                        TRACE(TRACE_OUT_OF_MEM, "%s",
-                               "Allocation of file_name failed");
+                               "Allocation of filename failed");
                        res = -ENOMEM;
                        goto out_unlock;
                }
 
-               strlcpy(fn, file_name, len);
-               virt_dev->file_name = fn;
+               strlcpy(fn, filename, len);
+               virt_dev->filename = fn;
 
-               res = vdisk_get_file_size(virt_dev->file_name,
+               res = vdisk_get_file_size(virt_dev->filename,
                                virt_dev->blockio, &err);
                if (res != 0)
                        goto out_free_fn;
        } else {
                err = 0;
-               virt_dev->file_name = NULL;
+               virt_dev->filename = NULL;
        }
 
        if (virt_dev->prevent_allow_medium_removal) {
                PRINT_ERROR("Prevent medium removal for "
                        "virtual device with name %s", virt_dev->name);
                res = -EINVAL;
-               goto out_free_fn;
-       }
-
-       virt_dev->file_size = err;
-       virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
-       if (!virt_dev->cdrom_empty)
-               virt_dev->media_changed = 1;
-
-       mutex_unlock(&scst_mutex);
-
-       scst_dev_del_all_thr_data(virt_dev->dev);
-
-       if (!virt_dev->cdrom_empty) {
-               PRINT_INFO("Changed SCSI target virtual cdrom %s "
-                       "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld,"
-                       " cyln=%lld%s)", virt_dev->name, virt_dev->file_name,
-                       virt_dev->file_size >> 20, virt_dev->block_size,
-                       (long long unsigned int)virt_dev->nblocks,
-                       (long long unsigned int)virt_dev->nblocks/64/32,
-                       virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
-                                                       "than 1" : "");
-       } else {
-               PRINT_INFO("Removed media from SCSI target virtual cdrom %s",
-                       virt_dev->name);
-       }
-
-       kfree(old_fn);
-
-out_resume:
-       scst_resume_activity();
-
-out_sysfs_unlock:
-       mutex_unlock(&virt_dev->vdev_sysfs_mutex);
-
-out_free:
-       kfree(i_buf);
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-
-out_free_fn:
-       kfree(virt_dev->file_name);
-       virt_dev->file_name = old_fn;
-
-out_unlock:
-       mutex_unlock(&scst_mutex);
-       goto out_resume;
-}
-
-#ifndef CONFIG_SCST_PROC
-
-static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj,
-       struct kobj_attribute *attr, const char *buf, size_t count)
-{
-       int res;
-       struct scst_device *dev;
-       struct scst_vdisk_dev *virt_dev;
-
-       TRACE_ENTRY();
-
-       dev = container_of(kobj, struct scst_device, dev_kobj);
-       virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
-
-       res = vcdrom_change(virt_dev, buf, count);
-       if (res != 0)
-               goto out;
-
-       res = count;
-
-out:
-       TRACE_EXIT_RES(res);
-       return res;
-}
-
-static int vdisk_mgmt_cmd(const char *buffer, int length,
-       struct vdev_type *vdt)
-{
-       int res = 0, action;
-       char *p, *name, *i_buf;
-       struct scst_vdisk_dev *virt_dev;
-
-       TRACE_ENTRY();
-
-       if ((length == 0) || (buffer == NULL) || (buffer[0] == '\0'))
-               goto out;
-
-       i_buf = kmalloc(length+1, GFP_KERNEL);
-       if (i_buf == NULL) {
-               PRINT_ERROR("Unable to alloc intermediate buffer with size %d",
-                       length+1);
-               res = -ENOMEM;
-               goto out;
-       }
-
-       memcpy(i_buf, buffer, length);
-       i_buf[length] = '\0';
-
-       if (mutex_lock_interruptible(&scst_vdisk_mutex) != 0) {
-               res = -EINTR;
-               goto out_free;
-       }
-
-       p = i_buf;
-       if (p[strlen(p) - 1] == '\n')
-               p[strlen(p) - 1] = '\0';
-       if (!strncmp("open", p, 4)) {
-               p += 4;
-               action = VDEV_ACTION_OPEN;
-       } else if (!strncmp("close", p, 5)) {
-               p += 5;
-               action = VDEV_ACTION_CLOSE;
-       } else {
-               PRINT_ERROR("Unknown action \"%s\"", p);
-               res = -EINVAL;
-               goto out_up;
-       }
-
-       if (!isspace(*p)) {
-               PRINT_ERROR("Syntax error on %s", p);
-               res = -EINVAL;
-               goto out_up;
+               goto out_free_fn;
        }
 
-       while (isspace(*p) && *p != '\0')
-               p++;
-       name = p;
-       while (!isspace(*p) && *p != '\0')
-               p++;
-       *p++ = '\0';
-       if (*name == '\0') {
-               PRINT_ERROR("%s", "Name required");
-               res = -EINVAL;
-               goto out_up;
-       } else if (strlen(name) >= sizeof(virt_dev->name)) {
-               PRINT_ERROR("Name is too long (max %zd "
-                       "characters)", sizeof(virt_dev->name)-1);
-               res = -EINVAL;
-               goto out_up;
+       virt_dev->file_size = err;
+       virt_dev->nblocks = virt_dev->file_size >> virt_dev->block_shift;
+       if (!virt_dev->cdrom_empty)
+               virt_dev->media_changed = 1;
+
+       mutex_unlock(&scst_mutex);
+
+       scst_dev_del_all_thr_data(virt_dev->dev);
+
+       if (!virt_dev->cdrom_empty) {
+               PRINT_INFO("Changed SCSI target virtual cdrom %s "
+                       "(file=\"%s\", fs=%lldMB, bs=%d, nblocks=%lld,"
+                       " cyln=%lld%s)", virt_dev->name,
+                       vdev_get_filename(virt_dev),
+                       virt_dev->file_size >> 20, virt_dev->block_size,
+                       (long long unsigned int)virt_dev->nblocks,
+                       (long long unsigned int)virt_dev->nblocks/64/32,
+                       virt_dev->nblocks < 64*32 ? " !WARNING! cyln less "
+                                                       "than 1" : "");
+       } else {
+               PRINT_INFO("Removed media from SCSI target virtual cdrom %s",
+                       virt_dev->name);
        }
 
-       if (action == VDEV_ACTION_OPEN) {
-               res = vdt->vfns.vdev_open(vdt, p, name);
-               if (res != 0)
-                       goto out_up;
-       } else if (action == VDEV_ACTION_CLOSE) {
-               res = vdt->vfns.vdev_close(vdt, name);
-               if (res != 0)
-                       goto out_up;
-       } else
-               sBUG();
+       kfree(old_fn);
 
-       res = length;
+out_resume:
+       scst_resume_activity();
 
-out_up:
-       mutex_unlock(&scst_vdisk_mutex);
+out_sysfs_unlock:
+       mutex_unlock(&virt_dev->vdev_sysfs_mutex);
 
 out_free:
        kfree(i_buf);
@@ -3756,34 +3726,42 @@ out_free:
 out:
        TRACE_EXIT_RES(res);
        return res;
-}
-
-static ssize_t vdisk_mgmt_show(struct kobject *kobj,
-       struct kobj_attribute *attr, char *buf)
-{
-       struct scst_dev_type *devt;
-       struct vdev_type *vdt;
 
-       devt = container_of(kobj, struct scst_dev_type, devt_kobj);
-       vdt = (struct vdev_type *)devt->devt_priv;
+out_free_fn:
+       kfree(virt_dev->filename);
+       virt_dev->filename = old_fn;
 
-       return scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, vdt->help_string);
+out_unlock:
+       mutex_unlock(&scst_mutex);
+       goto out_resume;
 }
 
-static ssize_t vdisk_mgmt_store(struct kobject *kobj,
+#ifndef CONFIG_SCST_PROC
+
+static ssize_t vcdrom_sysfs_filename_store(struct kobject *kobj,
        struct kobj_attribute *attr, const char *buf, size_t count)
 {
-       struct scst_dev_type *devt;
-       int res = count;
+       int res;
+       struct scst_device *dev;
+       struct scst_vdisk_dev *virt_dev;
 
-       devt = container_of(kobj, struct scst_dev_type, devt_kobj);
+       TRACE_ENTRY();
+
+       dev = container_of(kobj, struct scst_device, dev_kobj);
+       virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+       res = vcdrom_change(virt_dev, buf, count);
+       if (res != 0)
+               goto out;
 
-       res = vdisk_mgmt_cmd(buf, count, (struct vdev_type *)devt->devt_priv);
+       res = count;
 
+out:
+       TRACE_EXIT_RES(res);
        return res;
 }
 
-static ssize_t vdisk_sysfs_size_show(struct kobject *kobj,
+static ssize_t vdev_sysfs_size_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf)
 {
        int pos = 0;
@@ -3800,7 +3778,7 @@ static ssize_t vdisk_sysfs_size_show(struct kobject *kobj,
                goto out;
        }
 
-       pos = sprintf(buf, "%lld\n", virt_dev->file_size);
+       pos = sprintf(buf, "%lld\n", virt_dev->file_size / 1024 / 1024);
 
        mutex_unlock(&virt_dev->vdev_sysfs_mutex);
 
@@ -3821,7 +3799,9 @@ static ssize_t vdisk_sysfs_blocksize_show(struct kobject *kobj,
        dev = container_of(kobj, struct scst_device, dev_kobj);
        virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
 
-       pos = sprintf(buf, "%d\n", (int)virt_dev->block_size);
+       pos = sprintf(buf, "%d\n%s", (int)virt_dev->block_size,
+               (virt_dev->block_size == DEF_DISK_BLOCKSIZE) ? "" :
+                       SCST_SYSFS_KEY_MARK "\n");
 
        TRACE_EXIT_RES(pos);
        return pos;
@@ -3839,7 +3819,9 @@ static ssize_t vdisk_sysfs_rd_only_show(struct kobject *kobj,
        dev = container_of(kobj, struct scst_device, dev_kobj);
        virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
 
-       pos = sprintf(buf, "%d\n", virt_dev->rd_only ? 1 : 0);
+       pos = sprintf(buf, "%d\n%s", virt_dev->rd_only ? 1 : 0,
+               (virt_dev->rd_only == DEF_RD_ONLY) ? "" :
+                       SCST_SYSFS_KEY_MARK "");
 
        TRACE_EXIT_RES(pos);
        return pos;
@@ -3857,7 +3839,9 @@ static ssize_t vdisk_sysfs_wt_show(struct kobject *kobj,
        dev = container_of(kobj, struct scst_device, dev_kobj);
        virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
 
-       pos = sprintf(buf, "%d\n", virt_dev->wt_flag ? 1 : 0);
+       pos = sprintf(buf, "%d\n%s", virt_dev->wt_flag ? 1 : 0,
+               (virt_dev->wt_flag == DEF_WRITE_THROUGH) ? "" :
+                       SCST_SYSFS_KEY_MARK "");
 
        TRACE_EXIT_RES(pos);
        return pos;
@@ -3875,7 +3859,9 @@ static ssize_t vdisk_sysfs_nv_cache_show(struct kobject *kobj,
        dev = container_of(kobj, struct scst_device, dev_kobj);
        virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
 
-       pos = sprintf(buf, "%d\n", virt_dev->nv_cache ? 1 : 0);
+       pos = sprintf(buf, "%d\n%s", virt_dev->nv_cache ? 1 : 0,
+               (virt_dev->nv_cache == DEF_NV_CACHE) ? "" :
+                       SCST_SYSFS_KEY_MARK "");
 
        TRACE_EXIT_RES(pos);
        return pos;
@@ -3893,7 +3879,9 @@ static ssize_t vdisk_sysfs_o_direct_show(struct kobject *kobj,
        dev = container_of(kobj, struct scst_device, dev_kobj);
        virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
 
-       pos = sprintf(buf, "%d\n", virt_dev->o_direct_flag ? 1 : 0);
+       pos = sprintf(buf, "%d\n%s", virt_dev->o_direct_flag ? 1 : 0,
+               (virt_dev->o_direct_flag == DEF_O_DIRECT) ? "" :
+                       SCST_SYSFS_KEY_MARK "");
 
        TRACE_EXIT_RES(pos);
        return pos;
@@ -3913,11 +3901,15 @@ static ssize_t vdisk_sysfs_removable_show(struct kobject *kobj,
 
        pos = sprintf(buf, "%d\n", virt_dev->removable ? 1 : 0);
 
+       if ((virt_dev->dev->type != TYPE_ROM) &&
+           (virt_dev->removable != DEF_REMOVABLE))
+               pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK);
+
        TRACE_EXIT_RES(pos);
        return pos;
 }
 
-static ssize_t vdisk_sysfs_filename_show(struct kobject *kobj,
+static ssize_t vdev_sysfs_filename_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf)
 {
        int pos = 0;
@@ -3934,7 +3926,10 @@ static ssize_t vdisk_sysfs_filename_show(struct kobject *kobj,
                goto out;
        }
 
-       pos = sprintf(buf, "%s\n", virt_dev->file_name);
+       pos = sprintf(buf, "%s\n", vdev_get_filename(virt_dev));
+
+       if (virt_dev->dev->type != TYPE_ROM)
+               pos += sprintf(&buf[pos], "%s\n", SCST_SYSFS_KEY_MARK);
 
        mutex_unlock(&virt_dev->vdev_sysfs_mutex);
 
@@ -3966,7 +3961,7 @@ out:
        return res;
 }
 
-static ssize_t vdisk_sysfs_t10_dev_id_store(struct kobject *kobj,
+static ssize_t vdev_sysfs_t10_dev_id_store(struct kobject *kobj,
        struct kobj_attribute *attr, const char *buf, size_t count)
 {
        int res, i;
@@ -4012,7 +4007,7 @@ out_unlock:
        return res;
 }
 
-static ssize_t vdisk_sysfs_t10_dev_id_show(struct kobject *kobj,
+static ssize_t vdev_sysfs_t10_dev_id_show(struct kobject *kobj,
        struct kobj_attribute *attr, char *buf)
 {
        int pos = 0;
@@ -4032,6 +4027,24 @@ static ssize_t vdisk_sysfs_t10_dev_id_show(struct kobject *kobj,
        return pos;
 }
 
+static ssize_t vdev_sysfs_usn_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       int pos = 0;
+       struct scst_device *dev;
+       struct scst_vdisk_dev *virt_dev;
+
+       TRACE_ENTRY();
+
+       dev = container_of(kobj, struct scst_device, dev_kobj);
+       virt_dev = (struct scst_vdisk_dev *)dev->dh_priv;
+
+       pos = sprintf(buf, "%s\n", virt_dev->usn);
+
+       TRACE_EXIT_RES(pos);
+       return pos;
+}
+
 #else /* CONFIG_SCST_PROC */
 
 /*
@@ -4057,8 +4070,10 @@ static int vdisk_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type)
                "Name", "Size(MB)", "Block size", "Options", "File name",
                "T10 device id");
 
-       list_for_each_entry(virt_dev, &vdisk_dev_list, vdisk_dev_list_entry) {
+       list_for_each_entry(virt_dev, &vdev_list, vdev_list_entry) {
                int c;
+               if (virt_dev->dev->type != TYPE_DISK)
+                       continue;
                seq_printf(seq, "%-17s %-11d %-12d", virt_dev->name,
                        (uint32_t)(virt_dev->file_size >> 20),
                        virt_dev->block_size);
@@ -4101,7 +4116,7 @@ static int vdisk_read_proc(struct seq_file *seq, struct scst_dev_type *dev_type)
                        c++;
                }
                read_lock_bh(&vdisk_t10_dev_id_rwlock);
-               seq_printf(seq, "%-45s %-16s\n", virt_dev->file_name,
+               seq_printf(seq, "%-45s %-16s\n", vdev_get_filename(virt_dev),
                        virt_dev->t10_dev_id);
                read_unlock_bh(&vdisk_t10_dev_id_rwlock);
        }
@@ -4111,11 +4126,14 @@ out:
        return res;
 }
 
-static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
-                              struct scst_dev_type *dev_type)
+/*
+ * Called when a file in the /proc/VDISK_NAME/VDISK_NAME is written
+ */
+static int vdisk_write_proc(char *buffer, char **start, off_t offset,
+       int length, int *eof, struct scst_dev_type *dev_type)
 {
        int res = 0, action;
-       char *p, *name, *file_name, *i_buf, *t10_dev_id;
+       char *p, *name, *filename, *i_buf, *t10_dev_id;
        struct scst_vdisk_dev *virt_dev, *vv;
        uint32_t block_size = DEF_DISK_BLOCKSIZE;
        int block_shift = DEF_DISK_BLOCKSIZE_SHIFT;
@@ -4183,8 +4201,8 @@ static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
        if (action == 1) {
                /* open */
                virt_dev = NULL;
-               list_for_each_entry(vv, &vdisk_dev_list,
-                                       vdisk_dev_list_entry) {
+               list_for_each_entry(vv, &vdev_list,
+                                       vdev_list_entry) {
                        if (strcmp(vv->name, name) == 0) {
                                virt_dev = vv;
                                break;
@@ -4199,26 +4217,26 @@ static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
 
                while (isspace(*p) && *p != '\0')
                        p++;
-               file_name = p;
+               filename = p;
                while (!isspace(*p) && *p != '\0')
                        p++;
                *p++ = '\0';
-               if (*file_name == '\0') {
+               if (*filename == '\0') {
                        PRINT_ERROR("%s", "File name required");
                        res = -EINVAL;
                        goto out_up;
                }
 
                /* It's going to be removed code, anyway */
-               virt_dev = kzalloc(sizeof(*virt_dev), GFP_KERNEL);
+               virt_dev = vdev_create(dev_type, name);
                if (virt_dev == NULL) {
-                       TRACE(TRACE_OUT_OF_MEM, "%s",
-                                 "Allocation of virt_dev failed");
                        res = -ENOMEM;
                        goto out_up;
                }
-               spin_lock_init(&virt_dev->flags_lock);
-               virt_dev->vdt = &fileio_type.parent_vdt;
+
+               virt_dev->wt_flag = DEF_WRITE_THROUGH;
+               virt_dev->nv_cache = DEF_NV_CACHE;
+               virt_dev->o_direct_flag = DEF_O_DIRECT;
 
                while (isspace(*p) && *p != '\0')
                        p++;
@@ -4271,12 +4289,10 @@ static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
                        } else if (!strncmp("NULLIO", p, 6)) {
                                p += 6;
                                virt_dev->nullio = 1;
-                               virt_dev->vdt = &nullio_type.parent_vdt;
                                TRACE_DBG("%s", "NULLIO");
                        } else if (!strncmp("BLOCKIO", p, 7)) {
                                p += 7;
                                virt_dev->blockio = 1;
-                               virt_dev->vdt = &blockio_type.parent_vdt;
                                TRACE_DBG("%s", "BLOCKIO");
                        } else if (!strncmp("REMOVABLE", p, 9)) {
                                p += 9;
@@ -4291,48 +4307,42 @@ static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
                                p++;
                }
 
-               if (!virt_dev->nullio && (*file_name != '/')) {
+               if (!virt_dev->nullio && (*filename != '/')) {
                        PRINT_ERROR("File path \"%s\" is not "
-                               "absolute", file_name);
+                               "absolute", filename);
                        res = -EINVAL;
                        goto out_up;
                }
 
-               strcpy(virt_dev->name, name);
-
-               scnprintf(virt_dev->usn, sizeof(virt_dev->usn), "%llx",
-                               vdisk_gen_dev_id_num(virt_dev));
-               TRACE_DBG("usn %s", virt_dev->usn);
-
-               len = strlen(file_name) + 1;
-               virt_dev->file_name = kmalloc(len, GFP_KERNEL);
-               if (virt_dev->file_name == NULL) {
+               len = strlen(filename) + 1;
+               virt_dev->filename = kmalloc(len, GFP_KERNEL);
+               if (virt_dev->filename == NULL) {
                        TRACE(TRACE_OUT_OF_MEM, "%s",
-                                 "Allocation of file_name failed");
+                                 "Allocation of filename failed");
                        res = -ENOMEM;
                        goto out_free_vdev;
                }
-               strlcpy(virt_dev->file_name, file_name, len);
+               strlcpy(virt_dev->filename, filename, len);
 
-               list_add_tail(&virt_dev->vdisk_dev_list_entry,
-                                 &vdisk_dev_list);
+               list_add_tail(&virt_dev->vdev_list_entry,
+                                 &vdev_list);
 
                vdisk_report_registering(virt_dev);
                virt_dev->virt_id = scst_register_virtual_device(
-                       virt_dev->vdt->vdt_devt, virt_dev->name);
+                       dev_type, virt_dev->name);
                if (virt_dev->virt_id < 0) {
                        res = virt_dev->virt_id;
                        goto out_free_vpath;
                }
                TRACE_DBG("Added virt_dev (name %s, file name %s, "
                        "id %d, block size %d) to "
-                       "vdisk_dev_list", virt_dev->name,
-                       virt_dev->file_name, virt_dev->virt_id,
+                       "vdev_list", virt_dev->name,
+                       vdev_get_filename(virt_dev), virt_dev->virt_id,
                        virt_dev->block_size);
        } else if (action == 0) {       /* close */
                virt_dev = NULL;
-               list_for_each_entry(vv, &vdisk_dev_list,
-                                       vdisk_dev_list_entry) {
+               list_for_each_entry(vv, &vdev_list,
+                                       vdev_list_entry) {
                        if (strcmp(vv->name, name) == 0) {
                                virt_dev = vv;
                                break;
@@ -4343,19 +4353,11 @@ static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
                        res = -EINVAL;
                        goto out_up;
                }
-               scst_unregister_virtual_device(virt_dev->virt_id);
-               PRINT_INFO("Virtual device %s unregistered",
-                       virt_dev->name);
-               TRACE_DBG("virt_id %d unregister", virt_dev->virt_id);
-
-               list_del(&virt_dev->vdisk_dev_list_entry);
-
-               kfree(virt_dev->file_name);
-               kfree(virt_dev);
+               vdev_del_device(virt_dev);
        } else if (action == 2) {       /* resync_size */
                virt_dev = NULL;
-               list_for_each_entry(vv, &vdisk_dev_list,
-                                       vdisk_dev_list_entry) {
+               list_for_each_entry(vv, &vdev_list,
+                                       vdev_list_entry) {
                        if (strcmp(vv->name, name) == 0) {
                                virt_dev = vv;
                                break;
@@ -4372,8 +4374,8 @@ static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
                        goto out_up;
        } else if (action == 3) {       /* set T10 device id */
                virt_dev = NULL;
-               list_for_each_entry(vv, &vdisk_dev_list,
-                                       vdisk_dev_list_entry) {
+               list_for_each_entry(vv, &vdev_list,
+                                       vdev_list_entry) {
                        if (strcmp(vv->name, name) == 0) {
                                virt_dev = vv;
                                break;
@@ -4424,27 +4426,15 @@ out:
        return res;
 
 out_free_vpath:
-       list_del(&virt_dev->vdisk_dev_list_entry);
-       kfree(virt_dev->file_name);
+       list_del(&virt_dev->vdev_list_entry);
+       kfree(virt_dev->filename);
+       virt_dev->filename = NULL;
 
 out_free_vdev:
-       kfree(virt_dev);
+       vdev_destroy(virt_dev);
        goto out_up;
 }
 
-/*
- * Called when a file in the /proc/VDISK_NAME/VDISK_NAME is written
- */
-static int vdisk_write_proc(char *buffer, char **start, off_t offset,
-       int length, int *eof, struct scst_dev_type *dev_type)
-{
-       int res;
-
-       res = vdisk_proc_mgmt_cmd(buffer, length, dev_type);
-
-       return res;
-}
-
 /*
  * Called when a file in the /proc/VCDROM_NAME/VCDROM_NAME is read
  */
@@ -4463,11 +4453,12 @@ static int vcdrom_read_proc(struct seq_file *seq,
 
        seq_printf(seq, "%-17s %-9s %s\n", "Name", "Size(MB)", "File name");
 
-       list_for_each_entry(virt_dev, &vcdrom_dev_list,
-               vdisk_dev_list_entry) {
+       list_for_each_entry(virt_dev, &vdev_list, vdev_list_entry) {
+               if (virt_dev->dev->type != TYPE_ROM)
+                       continue;
                seq_printf(seq, "%-17s %-9d %s\n", virt_dev->name,
                        (uint32_t)(virt_dev->file_size >> 20),
-                       virt_dev->file_name);
+                       vdev_get_filename(virt_dev));
        }
 
        mutex_unlock(&scst_vdisk_mutex);
@@ -4478,14 +4469,132 @@ out:
 }
 
 /* scst_vdisk_mutex supposed to be held */
-static int vcdrom_proc_change(struct vdev_type *vdt, char *p, const char *name)
+static int vcdrom_open(char *p, char *name)
+{
+       struct scst_vdisk_dev *virt_dev, *vv;
+       char *filename;
+       int len;
+       int res = 0;
+       int cdrom_empty;
+
+       virt_dev = NULL;
+       list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
+               if (strcmp(vv->name, name) == 0) {
+                       virt_dev = vv;
+                       break;
+               }
+       }
+       if (virt_dev) {
+               PRINT_ERROR("Virtual device with name "
+                      "%s already exist", name);
+               res = -EINVAL;
+               goto out;
+       }
+
+       while (isspace(*p) && *p != '\0')
+               p++;
+       filename = p;
+       while (!isspace(*p) && *p != '\0')
+               p++;
+       *p++ = '\0';
+       if (*filename == '\0') {
+               cdrom_empty = 1;
+               TRACE_DBG("%s", "No media");
+       } else if (*filename != '/') {
+               PRINT_ERROR("File path \"%s\" is not "
+                       "absolute", filename);
+               res = -EINVAL;
+               goto out;
+       } else
+               cdrom_empty = 0;
+
+       virt_dev = vdev_create(&vcdrom_devtype, name);
+       if (virt_dev == NULL) {
+               TRACE(TRACE_OUT_OF_MEM, "%s",
+                     "Allocation of virt_dev failed");
+               res = -ENOMEM;
+               goto out;
+       }
+       virt_dev->cdrom_empty = cdrom_empty;
+       virt_dev->rd_only = 1;
+       virt_dev->removable = 1;
+
+       if (!virt_dev->cdrom_empty) {
+               len = strlen(filename) + 1;
+               virt_dev->filename = kmalloc(len, GFP_KERNEL);
+               if (virt_dev->filename == NULL) {
+                       TRACE(TRACE_OUT_OF_MEM, "%s",
+                             "Allocation of filename failed");
+                       res = -ENOMEM;
+                       goto out_free_vdev;
+               }
+               strncpy(virt_dev->filename, filename, len);
+       }
+
+       list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
+
+       PRINT_INFO("Registering virtual CDROM %s", name);
+
+       virt_dev->virt_id =
+           scst_register_virtual_device(&vcdrom_devtype,
+                                        virt_dev->name);
+       if (virt_dev->virt_id < 0) {
+               res = virt_dev->virt_id;
+               goto out_free_vpath;
+       }
+       TRACE_DBG("Added virt_dev (name %s filename %s id %d) "
+                 "to vdev_list", virt_dev->name,
+                 vdev_get_filename(virt_dev), virt_dev->virt_id);
+
+out:
+       return res;
+
+out_free_vpath:
+       list_del(&virt_dev->vdev_list_entry);
+       kfree(virt_dev->filename);
+       virt_dev->filename = NULL;
+
+out_free_vdev:
+       vdev_destroy(virt_dev);
+       goto out;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static int vcdrom_close(char *name)
+{
+       struct scst_vdisk_dev *virt_dev, *vv;
+       int res = 0;
+
+       virt_dev = NULL;
+       list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
+               if (strcmp(vv->name, name) == 0) {
+                       virt_dev = vv;
+                       break;
+               }
+       }
+
+       if (virt_dev == NULL) {
+               PRINT_ERROR("Virtual device with name "
+                      "%s not found", name);
+               res = -EINVAL;
+               goto out;
+       }
+
+       vdev_del_device(virt_dev);
+
+out:
+       return res;
+}
+
+/* scst_vdisk_mutex supposed to be held */
+static int vcdrom_proc_change(char *p, const char *name)
 {
        struct scst_vdisk_dev *virt_dev;
        int res;
 
-       virt_dev = vdt->vfns.vdev_find(name);
+       virt_dev = vdev_find(name);
        if (virt_dev == NULL) {
-               PRINT_ERROR("Virtual device with name "
+               PRINT_ERROR("Virtual cdrom with name "
                       "%s not found", name);
                res = -EINVAL;
                goto out;
@@ -4497,8 +4606,11 @@ out:
        return res;
 }
 
-static int vcdrom_proc_mgmt_cmd(const char *buffer, int length,
-       struct scst_dev_type *dev_type)
+/*
+ * Called when a file in the /proc/VCDROM_NAME/VCDROM_NAME is written
+ */
+static int vcdrom_write_proc(char *buffer, char **start, off_t offset,
+       int length, int *eof, struct scst_dev_type *dev_type)
 {
        int res = 0, action;
        char *p, *name, *i_buf;
@@ -4562,17 +4674,17 @@ static int vcdrom_proc_mgmt_cmd(const char *buffer, int length,
 
        if (action == 2) {
                /* open */
-               res = vdev_open(&vcdrom_type.parent_vdt, p, name);
+               res = vcdrom_open(p, name);
                if (res != 0)
                        goto out_up;
        } else if (action == 1) {
                /* change */
-               res = vcdrom_proc_change(&vcdrom_type.parent_vdt, p, name);
+               res = vcdrom_proc_change(p, name);
                if (res != 0)
                        goto out_up;
        } else {
                /* close */
-               res = vdev_close(&vcdrom_type.parent_vdt, name);
+               res = vcdrom_close(name);
                if (res != 0)
                        goto out_up;
        }
@@ -4589,19 +4701,6 @@ out:
        return res;
 }
 
-/*
- * Called when a file in the /proc/VCDROM_NAME/VCDROM_NAME is written
- */
-static int vcdrom_write_proc(char *buffer, char **start, off_t offset,
-       int length, int *eof, struct scst_dev_type *dev_type)
-{
-       int res;
-
-       res = vcdrom_proc_mgmt_cmd(buffer, length, dev_type);
-
-       return res;
-}
-
 static int vdisk_help_info_show(struct seq_file *seq, void *v)
 {
        char *s = (char *)seq->private;
@@ -4657,16 +4756,12 @@ static void vdisk_proc_help_destroy(struct scst_dev_type *dev_type)
 
 #endif /* CONFIG_SCST_PROC */
 
-static int __init init_scst_vdisk(struct scst_dev_type *devtype,
-       struct vdev_type *vdt)
+static int __init init_scst_vdisk(struct scst_dev_type *devtype)
 {
        int res = 0;
 
        TRACE_ENTRY();
 
-       vdt->vdt_devt = devtype;
-       devtype->devt_priv = vdt;
-
        devtype->module = THIS_MODULE;
 
        res = scst_register_virtual_dev_driver(devtype);
@@ -4700,8 +4795,7 @@ out_unreg:
 #endif
 }
 
-static void exit_scst_vdisk(struct scst_dev_type *devtype,
-       struct list_head *vdisk_dev_list)
+static void exit_scst_vdisk(struct scst_dev_type *devtype)
 {
        TRACE_ENTRY();
 
@@ -4709,20 +4803,13 @@ static void exit_scst_vdisk(struct scst_dev_type *devtype,
        while (1) {
                struct scst_vdisk_dev *virt_dev;
 
-               if (list_empty(vdisk_dev_list))
+               if (list_empty(&vdev_list))
                        break;
 
-               virt_dev = list_entry(vdisk_dev_list->next, typeof(*virt_dev),
-                               vdisk_dev_list_entry);
-
-               scst_unregister_virtual_device(virt_dev->virt_id);
+               virt_dev = list_entry(vdev_list.next, typeof(*virt_dev),
+                               vdev_list_entry);
 
-               list_del(&virt_dev->vdisk_dev_list_entry);
-
-               PRINT_INFO("Virtual device %s unregistered", virt_dev->name);
-               TRACE_DBG("virt_id %d", virt_dev->virt_id);
-               kfree(virt_dev->file_name);
-               kfree(virt_dev);
+               vdev_del_device(virt_dev);
        }
        mutex_unlock(&scst_vdisk_mutex);
 
@@ -4739,102 +4826,6 @@ static void exit_scst_vdisk(struct scst_dev_type *devtype,
        return;
 }
 
-static void __init init_vdev_type(struct vdev_type *vdt)
-{
-       /* vdt supposed to be already zeroed */
-
-       vdt->vfns.vdev_create = vdev_create;
-       vdt->vfns.vdev_destroy = vdev_destroy;
-       vdt->vfns.vdev_open = vdev_open;
-       vdt->vfns.vdev_close = vdev_close;
-       vdt->vfns.vdev_del = vdev_del;
-       vdt->vfns.vdev_init = vdev_init;
-       vdt->vfns.vdev_deinit = vdev_deinit;
-}
-
-static void __init init_fileio_type(void)
-{
-       struct vdev_type *vdt = &fileio_type.parent_vdt;
-
-       init_vdev_type(vdt);
-
-       vdt->vdt_name = "FILEIO";
-       vdt->help_string = "Usage:\n"
-               "       echo \"open|close NAME [FILE_NAME "
-               "[BLOCK_SIZE] [WRITE_THROUGH] [READ_ONLY] [O_DIRECT] "
-               "[NV_CACHE] [REMOVABLE]]\" >mgmt\n";
-
-       fileio_type.parent_vdt_vfns = vdt->vfns;
-
-       vdt->vfns.vdev_add = vdisk_add;
-       vdt->vfns.vdev_find = vdisk_find;
-       vdt->vfns.parse_option = vdisk_fileio_parse_option;
-       vdt->vfns.pre_register = vdisk_fileio_pre_register;
-
-       return;
-}
-
-static void __init init_blockio_type(void)
-{
-       struct vdev_type *vdt = &blockio_type.parent_vdt;
-
-       init_vdev_type(vdt);
-
-       vdt->vdt_name = "BLOCKIO";
-       vdt->help_string = "Usage:\n"
-               "       echo \"open|close NAME [DEVICE_NAME "
-               "[BLOCK_SIZE] [READ_ONLY] [REMOVABLE]]\" >mgmt\n";
-
-       blockio_type.parent_vdt_vfns = vdt->vfns;
-
-       vdt->vfns.vdev_init = vdisk_blockio_init;
-       vdt->vfns.vdev_add = vdisk_add;
-       vdt->vfns.vdev_find = vdisk_find;
-       vdt->vfns.parse_option = vdisk_parse_option;
-       vdt->vfns.pre_register = vdisk_blockio_pre_register;
-
-       return;
-}
-static void __init init_nullio_type(void)
-{
-       struct vdev_type *vdt = &nullio_type.parent_vdt;
-
-       init_vdev_type(vdt);
-
-       vdt->vdt_name = "NULLIO";
-       vdt->help_string = "Usage:\n"
-               "       echo \"open|close NAME [none [BLOCK_SIZE] [READ_ONLY] "
-               "[REMOVABLE]]\" >mgmt\n";
-
-       nullio_type.parent_vdt_vfns = vdt->vfns;
-
-       vdt->vfns.vdev_init = vdisk_nullio_init;
-       vdt->vfns.vdev_add = vdisk_add;
-       vdt->vfns.vdev_find = vdisk_find;
-       vdt->vfns.parse_option = vdisk_parse_option;
-
-       return;
-}
-static void __init init_vcdrom_type(void)
-{
-       struct vdev_type *vdt = &vcdrom_type.parent_vdt;
-
-       init_vdev_type(vdt);
-
-       vdt->vdt_name = "VCDROM";
-       vdt->help_string = "Usage:\n"
-               "       echo \"open|change|close NAME [FILE_NAME]\" >mgmt\n";
-
-       vcdrom_type.parent_vdt_vfns = vdt->vfns;
-
-       vdt->vfns.vdev_init = vcdrom_init;
-       vdt->vfns.vdev_add = vcdrom_add;
-       vdt->vfns.vdev_find = vcdrom_find;
-       vdt->vfns.pre_register = vcdrom_pre_register;
-
-       return;
-}
-
 static int __init init_scst_vdisk_driver(void)
 {
        int res;
@@ -4862,24 +4853,19 @@ static int __init init_scst_vdisk_driver(void)
 
        atomic_set(&nullio_thr_data.hdr.ref, 1); /* never destroy it */
 
-       init_fileio_type();
-       init_blockio_type();
-       init_nullio_type();
-       init_vcdrom_type();
-
-       res = init_scst_vdisk(&vdisk_file_devtype, &fileio_type.parent_vdt);
+       res = init_scst_vdisk(&vdisk_file_devtype);
        if (res != 0)
                goto out_free_slab;
 
-       res = init_scst_vdisk(&vdisk_blk_devtype, &blockio_type.parent_vdt);
+       res = init_scst_vdisk(&vdisk_blk_devtype);
        if (res != 0)
                goto out_free_vdisk;
 
-       res = init_scst_vdisk(&vdisk_null_devtype, &nullio_type.parent_vdt);
+       res = init_scst_vdisk(&vdisk_null_devtype);
        if (res != 0)
                goto out_free_blk;
 
-       res = init_scst_vdisk(&vcdrom_devtype, &vcdrom_type.parent_vdt);
+       res = init_scst_vdisk(&vcdrom_devtype);
        if (res != 0)
                goto out_free_null;
 
@@ -4887,13 +4873,13 @@ out:
        return res;
 
 out_free_null:
-       exit_scst_vdisk(&vdisk_null_devtype, &vdisk_dev_list);
+       exit_scst_vdisk(&vdisk_null_devtype);
 
 out_free_blk:
-       exit_scst_vdisk(&vdisk_blk_devtype, &vdisk_dev_list);
+       exit_scst_vdisk(&vdisk_blk_devtype);
 
 out_free_vdisk:
-       exit_scst_vdisk(&vdisk_file_devtype, &vdisk_dev_list);
+       exit_scst_vdisk(&vdisk_file_devtype);
 
 out_free_slab:
        kmem_cache_destroy(blockio_work_cachep);
@@ -4905,10 +4891,11 @@ out_free_vdisk_cache:
 
 static void __exit exit_scst_vdisk_driver(void)
 {
-       exit_scst_vdisk(&vdisk_null_devtype, &vdisk_dev_list);
-       exit_scst_vdisk(&vdisk_blk_devtype, &vdisk_dev_list);
-       exit_scst_vdisk(&vdisk_file_devtype, &vdisk_dev_list);
-       exit_scst_vdisk(&vcdrom_devtype, &vcdrom_dev_list);
+       exit_scst_vdisk(&vdisk_null_devtype);
+       exit_scst_vdisk(&vdisk_blk_devtype);
+       exit_scst_vdisk(&vdisk_file_devtype);
+       exit_scst_vdisk(&vcdrom_devtype);
+
        kmem_cache_destroy(blockio_work_cachep);
        kmem_cache_destroy(vdisk_thr_cachep);
 }
index 25af9e2..57c640b 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/unistd.h>
 #include <linux/string.h>
 #include <asm/kmap_types.h>
+#include <linux/ctype.h>
 
 #include "scst.h"
 #include "scst_priv.h"
@@ -5825,6 +5826,59 @@ out:
        return;
 }
 
+char *scst_get_next_lexem(char **token_str)
+{
+       char *p = *token_str;
+       char *q;
+       static const char blank = '\0';
+
+       if ((token_str == NULL) || (*token_str == NULL))
+               return (char *)&blank;
+
+       for (p = *token_str; (*p != '\0') && (isspace(*p) || (*p == '=')); p++)
+               ;
+
+       for (q = p; (*q != '\0') && !isspace(*q) && (*q != '='); q++)
+               ;
+
+       if (*q != '\0')
+               *q++ = '\0';
+
+       *token_str = q;
+       return p;
+}
+EXPORT_SYMBOL(scst_get_next_lexem);
+
+void scst_restore_token_str(char *prev_lexem, char *token_str)
+{
+       if (&prev_lexem[strlen(prev_lexem)] != token_str)
+               prev_lexem[strlen(prev_lexem)] = ' ';
+       return;
+}
+EXPORT_SYMBOL(scst_restore_token_str);
+
+char *scst_get_next_token_str(char **input_str)
+{
+       char *p = *input_str;
+       int i = 0;
+
+       while ((p[i] != '\n') && (p[i] != ';') && (p[i] != '\0'))
+               i++;
+
+       if (i == 0)
+               return NULL;
+
+       if (p[i] == '\0')
+               *input_str = &p[i];
+       else
+               *input_str = &p[i+1];
+
+       p[i] = '\0';
+
+       return p;
+}
+EXPORT_SYMBOL(scst_get_next_token_str);
+
 static void __init scst_scsi_op_list_init(void)
 {
        int i;
@@ -5907,7 +5961,7 @@ unsigned long scst_random(void)
        return rv;
 }
 EXPORT_SYMBOL(scst_random);
-#endif
+#endif /* CONFIG_SCST_DEBUG */
 
 #ifdef CONFIG_SCST_DEBUG_TM
 
index 9f2295c..3c1f118 100644 (file)
@@ -737,7 +737,7 @@ EXPORT_SYMBOL(scst_resume_activity);
 static int scst_register_device(struct scsi_device *scsidp)
 {
        int res = 0;
-       struct scst_device *dev;
+       struct scst_device *dev, *d;
        struct scst_dev_type *dt;
 
        TRACE_ENTRY();
@@ -766,6 +766,14 @@ static int scst_register_device(struct scsi_device *scsidp)
        snprintf(dev->virt_name, 50, "%d:%d:%d:%d", scsidp->host->host_no,
                scsidp->channel, scsidp->id, scsidp->lun);
 
+       list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
+               if (strcmp(d->virt_name, dev->virt_name) == 0) {
+                       PRINT_ERROR("Device %s already exists", dev->virt_name);
+                       res = -EEXIST;
+                       goto out_free_dev;
+               }
+       }
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
        dev->rq_disk = alloc_disk(1);
        if (dev->rq_disk == NULL) {
@@ -893,6 +901,19 @@ static int scst_dev_handler_check(struct scst_dev_type *dev_handler)
                goto out;
        }
 
+#ifndef CONFIG_SCST_PROC
+       if (((dev_handler->add_device != NULL) &&
+            (dev_handler->del_device == NULL)) ||
+           ((dev_handler->add_device == NULL) &&
+            (dev_handler->del_device != NULL))) {
+               PRINT_ERROR("Dev handler %s must either define both "
+                       "add_device() and del_device(), or none.",
+                       dev_handler->name);
+               res = -EINVAL;
+               goto out;
+       }
+#endif
+
        if (dev_handler->exec == NULL) {
 #ifdef CONFIG_SCST_ALLOW_PASSTHROUGH_IO_SUBMIT_IN_SIRQ
                dev_handler->exec_atomic = 1;
@@ -913,7 +934,7 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler,
        const char *dev_name)
 {
        int res, rc;
-       struct scst_device *dev = NULL;
+       struct scst_device *dev;
 
        TRACE_ENTRY();
 
@@ -934,6 +955,14 @@ int scst_register_virtual_device(struct scst_dev_type *dev_handler,
        if (res != 0)
                goto out;
 
+       list_for_each_entry(dev, &scst_dev_list, dev_list_entry) {
+               if (strcmp(dev->virt_name, dev_name) == 0) {
+                       PRINT_ERROR("Device %s already exists", dev_name);
+                       res = -EEXIST;
+                       goto out;
+               }
+       }
+
        res = scst_suspend_activity(true);
        if (res != 0)
                goto out;
index 7351587..f82fb15 100644 (file)
@@ -726,13 +726,9 @@ void scst_check_debug_sn(struct scst_cmd *cmd);
 static inline void scst_check_debug_sn(struct scst_cmd *cmd) {}
 #endif
 
-/*
- * It deals with comparing 32 bit unsigned ints and worry about wraparound
- * (automatic with unsigned arithmetic). Borrowed from net/tcp.h.
- */
-static inline int scst_sn_before(__u32 seq1, __u32 seq2)
+static inline int scst_sn_before(uint32_t seq1, uint32_t seq2)
 {
-       return (__s32)(seq1-seq2) < 0;
+       return (int32_t)(seq1-seq2) < 0;
 }
 
 bool scst_is_relative_target_port_id_unique(uint16_t id, struct scst_tgt *t);
index f035440..105cfd5 100644 (file)
@@ -118,9 +118,6 @@ static ssize_t scst_rel_tgt_id_show(struct kobject *kobj,
 static ssize_t scst_rel_tgt_id_store(struct kobject *kobj,
                                    struct kobj_attribute *attr,
                                    const char *buf, size_t count);
-static ssize_t scst_acg_luns_mgmt_show(struct kobject *kobj,
-                                  struct kobj_attribute *attr,
-                                  char *buf);
 static ssize_t scst_acg_luns_mgmt_store(struct kobject *kobj,
                                    struct kobj_attribute *attr,
                                    const char *buf, size_t count);
@@ -217,13 +214,21 @@ static ssize_t scst_tgtt_mgmt_show(struct kobject *kobj,
                     "%s"
                     "\n"
                     "where parameters are one or more "
-                    "param_name=value pairs separated by ';'\n";
+                    "param_name=value pairs separated by ';'\n"
+                    "%s%s";
        struct scst_tgt_template *tgtt;
 
        tgtt = container_of(kobj, struct scst_tgt_template, tgtt_kobj);
 
-       return sprintf(buf, help, (tgtt->mgmt_cmd_help != NULL) ?
-                                       tgtt->mgmt_cmd_help : "");
+       if (tgtt->add_target_parameters_help != NULL)
+               return sprintf(buf, help,
+                       (tgtt->mgmt_cmd_help) ? tgtt->mgmt_cmd_help : "",
+                       "\nThe following parameters available: ",
+                       tgtt->add_target_parameters_help);
+       else
+               return sprintf(buf, help,
+                       (tgtt->mgmt_cmd_help) ? tgtt->mgmt_cmd_help : "",
+                       "", "");
 }
 
 static ssize_t scst_tgtt_mgmt_store(struct kobject *kobj,
@@ -231,7 +236,7 @@ static ssize_t scst_tgtt_mgmt_store(struct kobject *kobj,
                                    const char *buf, size_t count)
 {
        int res;
-       char *buffer, *p, *target_name;
+       char *buffer, *p, *pp, *target_name;
        struct scst_tgt_template *tgtt;
 
        TRACE_ENTRY();
@@ -246,62 +251,38 @@ static ssize_t scst_tgtt_mgmt_store(struct kobject *kobj,
 
        memcpy(buffer, buf, count);
        buffer[count] = '\0';
-       p = buffer;
-
-       p = buffer;
-       if (p[strlen(p) - 1] == '\n')
-               p[strlen(p) - 1] = '\0';
 
-       if (strncasecmp("add_target ", p, 11) == 0) {
-               p += 11;
+       pp = buffer;
+       if (pp[strlen(pp) - 1] == '\n')
+               pp[strlen(pp) - 1] = '\0';
 
-               while (isspace(*p) && *p != '\0')
-                       p++;
-
-               if (p == '\0')
-                       goto out_syntax_err;
-
-               target_name = p;
-
-               while (!isspace(*p) && *p != '\0')
-                       p++;
+       p = scst_get_next_lexem(&pp);
 
-               if (*p != '\0') {
-                       *p = '\0';
-                       p++;
+       if (strcasecmp("add_target", p) == 0) {
+               target_name = scst_get_next_lexem(&pp);
+               if (*target_name == '\0') {
+                       PRINT_ERROR("%s", "Target name required");
+                       res = -EINVAL;
+                       goto out_free;
+               }
+               res = tgtt->add_target(target_name, pp);
+       } else if (strcasecmp("del_target", p) == 0) {
+               target_name = scst_get_next_lexem(&pp);
+               if (*target_name == '\0') {
+                       PRINT_ERROR("%s", "Target name required");
+                       res = -EINVAL;
+                       goto out_free;
                }
 
-               while (isspace(*p) && *p != '\0')
-                       p++;
-
-               res = tgtt->add_target(target_name, p);
-       } else if (strncasecmp("del_target ", p, 11) == 0) {
-               p += 11;
-
-               while (isspace(*p) && *p != '\0')
-                       p++;
-
-               if (p == '\0')
+               p = scst_get_next_lexem(&pp);
+               if (*p != '\0')
                        goto out_syntax_err;
 
-               target_name = p;
-
-               while (!isspace(*p) && *p != '\0')
-                       p++;
-
-               if (*p != '\0') {
-                       *p = '\0';
-                       p++;
-                       while (isspace(*p) && *p != '\0')
-                               p++;
-                       if (*p != '\0')
-                               goto out_syntax_err;
-               }
-
                res = tgtt->del_target(target_name);
-       } else if (tgtt->mgmt_cmd != NULL)
+       } else if (tgtt->mgmt_cmd != NULL) {
+               scst_restore_token_str(p, pp);
                res = tgtt->mgmt_cmd(buffer);
-       else {
+       else {
                PRINT_ERROR("Unknown action \"%s\"", p);
                res = -EINVAL;
                goto out_free;
@@ -521,7 +502,7 @@ static struct kobj_attribute scst_luns_mgmt =
               scst_luns_mgmt_store);
 
 static struct kobj_attribute scst_acg_luns_mgmt =
-       __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_acg_luns_mgmt_show,
+       __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_luns_mgmt_show,
               scst_acg_luns_mgmt_store);
 
 static struct kobj_attribute scst_acg_ini_mgmt =
@@ -814,6 +795,14 @@ int scst_create_devt_dev_sysfs(struct scst_device *dev)
                goto out;
        }
 
+       retval = sysfs_create_link(&dev->handler->devt_kobj,
+                       &dev->dev_kobj, dev->virt_name);
+       if (retval != 0) {
+               PRINT_ERROR("Can't create handler link for dev %s",
+                       dev->virt_name);
+               goto out;
+       }
+
        pattr = dev->handler->dev_attrs;
        if (pattr != NULL) {
                while (*pattr != NULL) {
@@ -852,6 +841,7 @@ void scst_devt_dev_sysfs_put(struct scst_device *dev)
        }
 
        sysfs_remove_link(&dev->dev_kobj, "handler");
+       sysfs_remove_link(&dev->handler->devt_kobj, dev->virt_name);
 
 out:
        TRACE_EXIT();
@@ -1206,6 +1196,13 @@ restart:
                }
        }
 
+       if (sess->acg == sess->tgt->default_acg)
+               retval = sysfs_create_link(&sess->sess_kobj,
+                               sess->tgt->tgt_luns_kobj, "luns");
+       else
+               retval = sysfs_create_link(&sess->sess_kobj,
+                               sess->acg->luns_kobj, "luns");
+
 out_free:
        if (name != sess->initiator_name)
                kfree(name);
@@ -1260,8 +1257,10 @@ static ssize_t scst_lun_rd_only_show(struct kobject *kobj,
 
        acg_dev = container_of(kobj, struct scst_acg_dev, acg_dev_kobj);
 
-       return sprintf(buf, "%d\n",
-               (acg_dev->rd_only || acg_dev->dev->rd_only) ? 1 : 0);
+       if (acg_dev->rd_only || acg_dev->dev->rd_only)
+               return sprintf(buf, "%d\n%s\n", 1, SCST_SYSFS_KEY_MARK);
+       else
+               return sprintf(buf, "%d\n", 0);
 }
 
 static struct kobj_attribute lun_options_attr =
@@ -1464,12 +1463,49 @@ static ssize_t __scst_luns_mgmt_store(struct scst_acg *acg,
                while (isspace(*e) && *e != '\0')
                        e++;
 
-               if (*e != '\0') {
-                       if ((strncasecmp("READ_ONLY", e, 9) == 0) &&
-                           (isspace(e[9]) || (e[9] == '\0')))
-                               read_only = 1;
-                       else {
-                               PRINT_ERROR("Unknown option \"%s\"", e);
+               while (1) {
+                       char *pp;
+                       unsigned long val;
+                       char *param = scst_get_next_token_str(&e);
+                       if (param == NULL)
+                               break;
+
+                       p = scst_get_next_lexem(&param);
+                       if (*p == '\0') {
+                               PRINT_ERROR("Syntax error at %s (device %s)",
+                                       param, dev->virt_name);
+                               res = -EINVAL;
+                               goto out_free_up;
+                       }
+
+                       pp = scst_get_next_lexem(&param);
+                       if (*pp == '\0') {
+                               PRINT_ERROR("Parameter %s value missed for device %s",
+                                       p, dev->virt_name);
+                               res = -EINVAL;
+                               goto out_free_up;
+                       }
+
+                       if (scst_get_next_lexem(&param)[0] != '\0') {
+                               PRINT_ERROR("Too many parameter's %s values (device %s)",
+                                       p, dev->virt_name);
+                               res = -EINVAL;
+                               goto out_free_up;
+                       }
+
+                       res = strict_strtoul(pp, 0, &val);
+                       if (res != 0) {
+                               PRINT_ERROR("strict_strtoul() for %s failed: %d "
+                                       "(device %s)", pp, res, dev->virt_name);
+                               goto out_free_up;
+                       }
+
+                       if (!strcasecmp("read_only", p)) {
+                               read_only = val;
+                               TRACE_DBG("READ ONLY %d", read_only);
+                       } else {
+                               PRINT_ERROR("Unknown parameter %s (device %s)",
+                                       p, dev->virt_name);
                                res = -EINVAL;
                                goto out_free_up;
                        }
@@ -1578,16 +1614,19 @@ static ssize_t scst_luns_mgmt_show(struct kobject *kobj,
                                   struct kobj_attribute *attr,
                                   char *buf)
 {
-       static char *help = "Usage: echo \"add|del H:C:I:L lun [READ_ONLY]\" "
+       static char *help = "Usage: echo \"add|del H:C:I:L lun [parameters]\" "
                                        ">mgmt\n"
-                           "       echo \"add|del VNAME lun [READ_ONLY]\" "
+                           "       echo \"add|del VNAME lun [parameters]\" "
                                        ">mgmt\n"
-                           "       echo \"replace H:C:I:L lun [READ_ONLY]\" "
+                           "       echo \"replace H:C:I:L lun [parameters]\" "
                                        ">mgmt\n"
-                           "       echo \"replace VNAME lun [READ_ONLY]\" "
+                           "       echo \"replace VNAME lun [parameters]\" "
                                        ">mgmt\n"
-                           "       echo \"clear\" "
-                                       ">mgmt\n";
+                           "       echo \"clear\" >mgmt\n"
+                           "\n"
+                           "where parameters are one or more "
+                           "param_name=value pairs separated by ';'\n"
+                           "\nThe following parameters available: read_only.";
 
        return sprintf(buf, help);
 }
@@ -1984,24 +2023,6 @@ static ssize_t scst_acn_file_show(struct kobject *kobj,
                attr->attr.name);
 }
 
-static ssize_t scst_acg_luns_mgmt_show(struct kobject *kobj,
-                                  struct kobj_attribute *attr,
-                                  char *buf)
-{
-       static char *help = "Usage: echo \"add|del H:C:I:L lun [READ_ONLY]\" "
-                                       ">mgmt\n"
-                           "       echo \"add|del VNAME lun [READ_ONLY]\" "
-                                       ">mgmt\n"
-                           "       echo \"replace H:C:I:L lun [READ_ONLY]\" "
-                                       ">mgmt\n"
-                           "       echo \"replace VNAME lun [READ_ONLY]\" "
-                                       ">mgmt\n"
-                           "       echo \"clear\" "
-                                       ">mgmt\n";
-
-       return sprintf(buf, help);
-}
-
 static ssize_t scst_acg_luns_mgmt_store(struct kobject *kobj,
                                    struct kobj_attribute *attr,
                                    const char *buf, size_t count)
@@ -2835,6 +2856,256 @@ static struct kobj_type scst_devt_ktype = {
        .default_attrs = scst_devt_default_attrs,
 };
 
+static ssize_t scst_devt_mgmt_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       char *help = "Usage: echo \"add_device device_name [parameters]\" "
+                               ">mgmt\n"
+                    "       echo \"del_device device_name\" >mgmt\n"
+                    "%s"
+                    "\n"
+                    "where parameters are one or more "
+                    "param_name=value pairs separated by ';'\n"
+                    "%s%s";
+       struct scst_dev_type *devt;
+
+       devt = container_of(kobj, struct scst_dev_type, devt_kobj);
+
+       if (devt->add_device_parameters_help != NULL)
+               return sprintf(buf, help,
+                       (devt->mgmt_cmd_help) ? devt->mgmt_cmd_help : "",
+                       "\nThe following parameters available: ",
+                       devt->add_device_parameters_help);
+       else
+               return sprintf(buf, help,
+                       (devt->mgmt_cmd_help) ? devt->mgmt_cmd_help : "",
+                       "", "");
+}
+
+static ssize_t scst_devt_mgmt_store(struct kobject *kobj,
+                                   struct kobj_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       int res;
+       char *buffer, *p, *pp, *device_name;
+       struct scst_dev_type *devt;
+
+       TRACE_ENTRY();
+
+       devt = container_of(kobj, struct scst_dev_type, devt_kobj);
+
+       buffer = kzalloc(count+1, GFP_KERNEL);
+       if (buffer == NULL) {
+               res = -ENOMEM;
+               goto out;
+       }
+
+       memcpy(buffer, buf, count);
+       buffer[count] = '\0';
+
+       pp = buffer;
+       if (pp[strlen(pp) - 1] == '\n')
+               pp[strlen(pp) - 1] = '\0';
+
+       p = scst_get_next_lexem(&pp);
+
+       if (strcasecmp("add_device", p) == 0) {
+               device_name = scst_get_next_lexem(&pp);
+               if (*device_name == '\0') {
+                       PRINT_ERROR("%s", "Device name required");
+                       res = -EINVAL;
+                       goto out_free;
+               }
+               res = devt->add_device(device_name, pp);
+       } else if (strcasecmp("del_device", p) == 0) {
+               device_name = scst_get_next_lexem(&pp);
+               if (*device_name == '\0') {
+                       PRINT_ERROR("%s", "Device name required");
+                       res = -EINVAL;
+                       goto out_free;
+               }
+
+               p = scst_get_next_lexem(&pp);
+               if (*p != '\0')
+                       goto out_syntax_err;
+
+               res = devt->del_device(device_name);
+       } else if (devt->mgmt_cmd != NULL) {
+               scst_restore_token_str(p, pp);
+               res = devt->mgmt_cmd(buffer);
+       } else {
+               PRINT_ERROR("Unknown action \"%s\"", p);
+               res = -EINVAL;
+               goto out_free;
+       }
+
+       if (res == 0)
+               res = count;
+
+out_free:
+       kfree(buffer);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_syntax_err:
+       PRINT_ERROR("Syntax error on \"%s\"", p);
+       res = -EINVAL;
+       goto out_free;
+}
+
+static struct kobj_attribute scst_devt_mgmt =
+       __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_devt_mgmt_show,
+              scst_devt_mgmt_store);
+
+static ssize_t scst_devt_pass_through_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       return sprintf(buf, "1");
+}
+
+static struct kobj_attribute scst_devt_pass_through =
+       __ATTR(pass_through, S_IRUGO, scst_devt_pass_through_show, NULL);
+
+static ssize_t scst_devt_pass_through_mgmt_show(struct kobject *kobj,
+       struct kobj_attribute *attr, char *buf)
+{
+       char *help = "Usage: echo \"assign H:C:I:L\" >mgmt\n"
+                    "       echo \"unassign H:C:I:L\" >mgmt\n";
+       return sprintf(buf, help);
+}
+
+static ssize_t scst_devt_pass_through_mgmt_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       int res;
+       char *buffer, *p, *pp, *action;
+       struct scst_dev_type *devt;
+       unsigned long host, channel, id, lun;
+       struct scst_device *d, *dev = NULL;
+
+       TRACE_ENTRY();
+
+       devt = container_of(kobj, struct scst_dev_type, devt_kobj);
+
+       buffer = kzalloc(count+1, GFP_KERNEL);
+       if (buffer == NULL) {
+               res = -ENOMEM;
+               goto out;
+       }
+
+       memcpy(buffer, buf, count);
+       buffer[count] = '\0';
+
+       pp = buffer;
+       if (pp[strlen(pp) - 1] == '\n')
+               pp[strlen(pp) - 1] = '\0';
+
+       action = scst_get_next_lexem(&pp);
+       p = scst_get_next_lexem(&pp);
+       if (*p == '\0') {
+               PRINT_ERROR("%s", "Device required");
+               res = -EINVAL;
+               goto out_free;
+       }
+
+       ;
+       if (*scst_get_next_lexem(&pp) != '\0') {
+               PRINT_ERROR("%s", "Too many parameters");
+               res = -EINVAL;
+               goto out_syntax_err;
+       }
+
+       host = simple_strtoul(p, &p, 0);
+       if ((host == ULONG_MAX) || (*p != ':'))
+               goto out_syntax_err;
+       p++;
+       channel = simple_strtoul(p, &p, 0);
+       if ((channel == ULONG_MAX) || (*p != ':'))
+               goto out_syntax_err;
+       p++;
+       id = simple_strtoul(p, &p, 0);
+       if ((channel == ULONG_MAX) || (*p != ':'))
+               goto out_syntax_err;
+       p++;
+       lun = simple_strtoul(p, &p, 0);
+       if (lun == ULONG_MAX)
+               goto out_syntax_err;
+
+       TRACE_DBG("Dev %ld:%ld:%ld:%ld", host, channel, id, lun);
+
+       if (mutex_lock_interruptible(&scst_mutex) != 0) {
+               res = -EINTR;
+               goto out_free;
+       }
+
+       list_for_each_entry(d, &scst_dev_list, dev_list_entry) {
+               if ((d->virt_id == 0) &&
+                   d->scsi_dev->host->host_no == host &&
+                   d->scsi_dev->channel == channel &&
+                   d->scsi_dev->id == id &&
+                   d->scsi_dev->lun == lun) {
+                       dev = d;
+                       TRACE_DBG("Dev %p (%ld:%ld:%ld:%ld) found",
+                                 dev, host, channel, id, lun);
+                       break;
+               }
+       }
+       if (dev == NULL) {
+               PRINT_ERROR("Device %ld:%ld:%ld:%ld not found",
+                              host, channel, id, lun);
+               res = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (dev->scsi_dev->type != devt->type) {
+               PRINT_ERROR("Type %d of device %s differs from type "
+                       "%d of dev handler %s", dev->type,
+                       dev->virt_name, devt->type, devt->name);
+               res = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (strcasecmp("assign", action) == 0)
+               res = scst_assign_dev_handler(dev, devt);
+       else if (strcasecmp("deassign", action) == 0) {
+               if (dev->handler != devt) {
+                       PRINT_ERROR("Device %s is not assigned to handler %s",
+                               dev->virt_name, devt->name);
+                       res = -EINVAL;
+                       goto out_unlock;
+               }
+               res = scst_assign_dev_handler(dev, &scst_null_devtype);
+       } else {
+               PRINT_ERROR("Unknown action \"%s\"", action);
+               res = -EINVAL;
+               goto out_unlock;
+       }
+
+       if (res == 0)
+               res = count;
+
+out_unlock:
+       mutex_unlock(&scst_mutex);
+
+out_free:
+       kfree(buffer);
+
+out:
+       TRACE_EXIT_RES(res);
+       return res;
+
+out_syntax_err:
+       PRINT_ERROR("Syntax error on \"%s\"", p);
+       res = -EINVAL;
+       goto out_free;
+}
+
+static struct kobj_attribute scst_devt_pass_through_mgmt =
+       __ATTR(mgmt, S_IRUGO | S_IWUSR, scst_devt_pass_through_mgmt_show,
+              scst_devt_pass_through_mgmt_store);
+
 int scst_create_devt_sysfs(struct scst_dev_type *devt)
 {
        int retval;
@@ -2864,6 +3135,32 @@ int scst_create_devt_sysfs(struct scst_dev_type *devt)
         * it will be done by the _put function() called by the caller.
         */
 
+       if (devt->add_device != NULL) {
+               retval = sysfs_create_file(&devt->devt_kobj,
+                               &scst_devt_mgmt.attr);
+               if (retval != 0) {
+                       PRINT_ERROR("Can't add mgmt attr for dev handler %s",
+                               devt->name);
+