Sysfs interface for targets made uniform. ISCSI-SCST made confirming the uniformity requirements. See README for details.

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1511 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Vladislav Bolkhovitin
2010-02-17 19:07:54 +00:00
parent 9b82ea19be
commit 4d96089e50
51 changed files with 4956 additions and 2044 deletions
-461
View File
@@ -1,461 +0,0 @@
Summary of changes from v0.4.15 to v0.4.16
=================================
Arne Redlich
o fix overzealous assert() in digest_data()
o add checking on return value of ISCSI_PARAM_GET
o 2.6.22, 2.6.23 and 2.6.24 compile fixes
o add conn->rwsize check
o avoid potential NULL-ptr dereferences in rx and tx buffer
o fix the shell syntax in init scripts
Dave Jiang
o fix digest endieness on LE archs
FUJITA Tomonori
o fix SPARC alignment issues (based on a patch from joel.bertrand@systella.fr)
Ross S. W. Walker
o fix DISTDIR in Makefile for /etc install
o add support to nullio for volumes > 2TB
o remove init.d memory size adjustment
o add error code reporting to blockio_open_path
o blockio gen_scsiid bug fix
o add verbosity to kernel output and task management
Summary of changes from v0.4.14 to v0.4.15
=================================
Juhani Rautiainen
o Add RELEASE/RESERVE support
Ross S. W. Walker
o Improve the build system to support several kernel versions
o Add block-io support
Summary of changes from v0.4.13 to v0.4.14
=================================
Arne Redlich
o Kill unused "state" in struct iscsi_cmnd.
o Fixed fileio_sync() to propagate error to the caller (and initiator).
o Don't attempt to show target/session params if communication with ietd
fails.
o Fixes to ietadm parameters handling.
FUJITA Tomonori
o rewritten iSNS code, many iSNS fixes.
o added iSNS SCN support.
o IPv6 fixes to userspace.
Ming Zhang
o Fix the READ_* commands error handling bug.
o fix the mode sense response.
o wrong #endif sequence in misc.h
Richard Bollinger
o add a patch to ietd.c that allows defunct sessions to go away.
o add write-back cache and read-only support.
Frederic Temporelli
o Fix for the combination of 32-bit userland and 64-bit kernel on mips.
Henry Liu
o corrected many task management functions, prevent crashing on
LUN RESET, TARGET WARM RESET.
K Chapman
o Fixed a typo in check_segment_length().
Emmanuel Florac
o Add ietadm manpage.
Summary of changes from v0.4.12 to v0.4.13
=================================
Arne Redlich
o patch to avoid digest calculation for PDUs whose data has been skipped
already for various reasons.
o Correct a bug managing non-default MaxRxDSL.
o added to ietadm ability to show target parameters.
o add on the workaround to AIX initiator MaxCmdSN bug.
FUJITA Tomonori
o added to ietadm ability to show the iSCSI parameters for an established
session.
Ming Zhang
o Fixed this bug : ietd should manage the iscsi name in a case insensitive
way to conform to the RFC.
o workaround to AIX initiator MaxCmdSN bug.
o Fixed socket() return value judgment.
Bastiaan Bakker
o add 'condrestart' command to the RedHat initscript.
Robert Whitehead
o correct the bug that prevents iet to start if there isn't
an /etc/ietd.conf file.
Summary of changes from v0.4.11 to v0.4.12
=================================
Arne Redlich
o Fix MaxRecvDataSegmentLength handling.
o Fix login parameter handling.
o Update man pages.
Bastiaan Bakker
o Add features to specify the listen address and port.
o Fix setuid and setgid bugs in ietd daemon.
FUJITA Tomonori
o Add IPv6 support.
Junjiro Okajima
o Fix a bug about getting parameters from kernel space.
Krzysztof Blaszkowski
o Fix for requests with unaligned to 4 length.
Summary of changes from v0.4.10 to v0.4.11
=================================
FUJITA Tomonori
o Fix Task Management Function bugs.
Ming Zhang
o Update man pages.
Summary of changes from v0.4.9 to v0.4.10
=================================
Arne Redlich
o Fix 0x83 inquiry output.
o Fix iSCSI parameter handling bugs.
FUJITA Tomonori
o Add the access control based on initiator address
and target name patterns.
Junjiro Okajima
o Fix parameter checking bugs.
Ming Zhang
o Add the nullio mode (only useful for performance evaluation).
Summary of changes from v0.4.8 to v0.4.9
=================================
FUJITA Tomonori
o Fix parameter negotiation handling bugs.
Wang Zhenyu
o Fix digest negotiation handling bugs.
Summary of changes from v0.4.7 to v0.4.8
=================================
FUJITA Tomonori
o Fix unsolicited data handling bugs.
o Rewrite parameter handling code.
o Rewrite ietadm tool.
o Improve dynamic configuration support.
o Cleanups on the kernel-user interface.
o Improve wrong PDU handling.
o Implement a framework to handle multiple configuration methods.
o Implement basic access control support.
Summary of changes from v0.4.6 to v0.4.7
=================================
Florian Zierer
o Add the startup script for Gentoo.
FUJITA Tomonori
o Rewrite parameter handling code.
o Fix task management code bug.
o Fix 0x83 inquiry output (Thanks to Christophe Varoqui).
Ming Zhang
o Acquire T10 ID.
o Fix parameter handling bugs.
o Some user-space cleanups.
Philipp Hug
o Fix ietd.8 man typo.
Summary of changes from v0.4.5 to v0.4.6
=================================
FUJITA Tomonori
o Replace the makeshift event notification code with netlink.
o Add task management code except for ACA and reassign stuff.
o Fix r2t lun bug (Thanks to Ming Zhang).
Summary of changes from v0.4.4 to v0.4.5
=================================
FUJITA Tomonori
o Rewrite the iSCSI command handling code.
o Rewrite the I/O data handling code.
o Fix worker thread.
o Several cleanups.
Summary of changes from v0.4.3 to v0.4.4
=================================
Krzysztof Blaszkowski
o Fix an out-of-memory bug.
Summary of changes from v0.4.2 to v0.4.3
=================================
Arne Redlich
o Fix header digest bug.
o Fix unexpected closed connection bug.
o Fix iSCSI parameter bug.
FUJITA Tomonori
o Fix network thread.
Summary of changes from v0.4.1 to v0.4.2
=================================
FUJITA Tomonori
o Fix network thread.
o Fix MaxOutstandingR2T handling.
Ming Zhang
o Add large volume support (over 2TB).
Summary of changes from v0.4.0 to v0.4.1
=================================
Arne Redlich
o Add mutual CHAP support. Note that you need to replace "User"
with "IncomingUser" in ietd.conf.
FUJITA Tomonori
o Fix InitialR2T=No support.
o Fix INQUIRY command handling.
o Fix network and worker thread.
o Start to split SCSI stuff.
o Rewrite the R2T handling code.
o Several cleanups.
Summary of changes from v0.3.8 to v0.4.0
=================================
Arne Redlich
o iSNS bug fix.
FUJITA Tomonori
o Move to 2.6 kernels.
o Rewrite the kernel thread performing network I/O.
o Add header and data digests (Thanks to Arne Redlich).
Ming Zhang
o Add mode sense page 0x3 and 0x4 support (Thanks to K Chapman).
o iSNS bug fix.
Summary of changes from v0.3.7 to v0.3.8
=================================
Arne Redlich
o Fix ietadm global option bug.
FUJITA Tomonori
o Fix TCP option bugs (Thanks to Chuck Berg).
o Fix REPORT LUN (handling lots of LUs).
Summary of changes from v0.3.6 to v0.3.7
=================================
Arne Redlich
o Fix target_alloc_pages().
FUJITA Tomonori
o Fix REPORT LUN bug.
Summary of changes from v0.3.5 to v0.3.6
=================================
Arne Redlich
o Fix bugs about rejecting PDUs.
FUJITA Tomonori
o Cleanups on target_cmnd structure.
o Kill highmem stuff.
o Fix REPORT LUN (handling lots of LUs).
Summary of changes from v0.3.4 to v0.3.5
=================================
Arne Redlich
o Fix ietd security hole.
o Fix REPORT LUN bug.
o FIX NOOP_OUT padding bug.
FUJITA Tomonori
o Rewrite event notification code.
Libor Vanek
o Add max_sessions option.
o Fix command parsing bug.
Ming Zhang
o Cleanups for 64-bit architectures.
Summary of changes from v0.3.3 to v0.3.4
=================================
FUJITA Tomonori
o Improve dynamic configuration support (adding targets and users).
Summary of changes from v0.3.2 to v0.3.3
=================================
FUJITA Tomonori
o Fix Makefile for the startup script.
Summary of changes from v0.3.1 to v0.3.2
=================================
Ali Lehmann
o Add a new startup script for Debian.
FUJITA Tomonori
o Fix the istd's handling of connections in out-of-memory situations.
o Fix bugs in regular file support.
o Fix `ietadm --mode del all`.
Libor Vanek
o Add a new startup script for RedHat.
Ming Zhang
o Add uid/gid option to ietd daemon.
o Fix a access freed-memory bug in kernel/daemon.c.
Summary of changes from v0.3.0 to v0.3.1
=================================
FUJITA Tomonori
o Fix memory leaks in ietd daemon (Thanks to Ming).
o Fix bugs about REPORT_LUNS commands (Thanks to Ming).
o Fix a bug about Target Task Tag.
o Add regular file support to fileio mode.
Summary of changes from v0.2.6 to v0.3.0
=================================
Ali Lehmann
o Update ietd.8 man page.
FUJITA Tomonori
o Fix shutdown code.
o Fix istd kernel thread bugs.
o Replace procfs interface with ioctl.
o Add dynamic configuration support.
o Update README and the boot script.
Ming Zhang
o Add config option to ietd daemon.
Summary of changes from v0.2.5 to v0.2.6
=================================
Ali Lehmann
o Add ietd.8 and ietd.conf.5 man pages.
FUJITA Tomonori
o Update README, Makefile, and the boot script.
Summary of changes from v0.2.4 to v0.2.5
=================================
FUJITA Tomonori
o Update README.
Summary of changes from v0.2.3 to v0.2.4
=================================
Ming Zhang
o Add a preliminary iSNS support.
o Fix merge mistakes that I made at the previous release.
Summary of changes from v0.2.2 to v0.2.3
=================================
Ming Zhang
o Improve INQUIRY, REQUEST_SENSE, and MODE_SENSE command supports
o Add fake RESERVE* and RELEASE* command supports
Summary of changes from v0.2.1 to v0.2.2
=================================
FUJITA Tomonori
o Improve the write performance of the file IO mode
Ming Zhang
o Fix unupdated pg_cnt when allocating a new tcmnd
o Several cleanups
Summary of changes from v0.2.0 to v0.2.1
=================================
FUJITA Tomonori
o Fix a bug that makes the target use CPU unnecessarily
o Add a feature that enable you to pass options to an IO mode
Summary of changes from v0.1.0 to v0.2.0
=================================
FUJITA Tomonori
o Rewrite read and write kernel threads which perform network IO
o Fix race issues in the proc interface
o Fix shutdown code
Ming Zhang
o Fix memory leaks in file and block IO modes
Summary of changes from the ardis v20040211 to v0.1.0
=================================
FUJITA Tomonori
o Remove a kernel patch. Multiple threads execute I/O operations
o Replace IO functions with the kernel standard functions
o Add multiple IO modes feature
o Fix several race issues
o Fix several out-of-memory situation bugs
+417 -32
View File
@@ -1,7 +1,7 @@
iSCSI SCST target driver
========================
Version 1.0.2, XX XXXXX 2009
Version 2.0.0, XX XXXXX 2010
----------------------------
ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
@@ -14,7 +14,7 @@ ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
See more info at http://scst.sourceforge.net/target_iscsi.html.
This version is compatible with SCST version 1.0.2 and higher.
This version is compatible with SCST version 2.0.0 and higher.
Installation if your Linux kernel already has iSCSI-SCST built-in
@@ -26,8 +26,7 @@ Simply run "make all", then "make install".
Installation out of Linux kernel tree
-------------------------------------
Basically as in README-IET, where file names are changed as specified
above.
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
@@ -52,20 +51,18 @@ recent kernel messages).
To use full power of TCP zero-copy transmit functions, especially
dealing with user space supplied via scst_user module memory, iSCSI-SCST
needs to be notified when Linux networking finished data transmission.
Patch put_page_callback-<kernel-version>.patch provides such
functionality. The corresponding version of it should be applied on your
kernel. Then you should enable CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
kernel config option. This is highly recommended, but not required. Basically,
you should consider using of this patch as some optimization, which IET
doesn't have, so if you don't use it, you will just revert to the
original IET behavior, when for data transmission:
For that you should enable CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
kernel config option. This is highly recommended, but not required.
Basically, iSCSI-SCST works fine with an unpatched Linux kernel with the
same or better speed as other open source iSCSI targets, including IET,
but if you want even better performance you have to patch and rebuild
the kernel. Without CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
enabled you will just revert to the original behavior of other open
source iSCSI targets, when for data transmission:
- For in-kernel allocated memory (scst_vdisk and pass-through
handlers) usage of SGV cache on transmit path (READ-type commands)
will be disabled, but data will still be sent in zero-copy manner.
The performance hit will be not big, but performance will still
remain better, than for IET, because SGV cache will remain used on
receive path while IET doesn't have such feature.
- For user space allocated memory (scst_user handler) all transmitted
data will be additionally copied into temporary TCP buffers. The
@@ -109,24 +106,16 @@ or
Usage
-----
See in doc/iscsi-scst-howto.txt examples how to configure iSCSI-SCST.
See HOWTOs in the doc/ subdirectory.
ISCSI parameters like iSNS, CHAP and target parameters are configured in
iscsi-scstd.conf. All LUN information is configured using the
corresponding SCST interface. See in SCST README file section "Access
and devices visibility management (LUN masking)" to find out how to do
it. It is highly recommended to use scstadmin utility for that purpose.
The LUN information in iscsi-scstd.conf will be ignored. This is because
now responsibilities are divided between the target driver (iSCSI-SCST)
and the SCST core as it logically should be: the target driver is
responsible for handling targets and their parameters, SCST core is
responsible for handling backstorage.
In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm 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: All LUN information (access control) MUST be configured
========= BEFORE iscsi-scstd started!
IMPORTANT: In the procfs build all LUN information (access control)
========= MUST be configured BEFORE iscsi-scstd started!
Also see SCST README file how to tune for the best performance.
@@ -134,6 +123,405 @@ CAUTION: Working of target and initiator on the same host isn't fully
======= supported. See SCST README file for details.
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.
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:
- None, one or more subdirectories for targets with name equal to names
of the corresponding targets.
- IncomingUser[num] - optional one or more attributes containing user
name and password for incoming discovery user name. Not exist by
default and can be added through "mgmt" entry, see below.
- OutgoingUser - optional attribute containing user name and password
for outgoing discovery user name. Not exist by default and can be
added through "mgmt" entry, see below.
- iSNSServer - contains name or IP address of iSNS server with optional
"AccessControl" attribute, which allows to enable iSNS access
control. Empty by default.
- enabled - using this attribute you can enable or disable iSCSI-SCST
accept new connections. It allows to finish configuring global
iSCSI-SCST attributes before it starts accepting new connections. 0
by default.
- open_state - read-only attribute, which allows to see if the user
space part of iSCSI-SCST connected to the kernel part.
- 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
iSCSI-SCST and enabled optional features.
- mgmt - main management entry, which allows to configure iSCSI-SCST.
Namely, add/delete targets as well as add/delete optional global and
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 target subdirectory contains the following entries:
- 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.
- IncomingUser[num] - optional one or more attributes containing user
name and password for incoming user name. Not exist by default and can
be added through the "mgmt" entry, see above.
- OutgoingUser - optional attribute containing user name and password
for outgoing user name. Not exist by default and can be added through
the "mgmt" entry, see above.
- Entries defining default iSCSI parameters values used during iSCSI
parameters negotiation.
- QueuedCommands - defines maximum number of commands queued to any
session of this target.
- 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.
- tid - TID of this target.
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:
- One subdirectory for each TCP connection in this session. ISCSI-SCST
supports 1 connection per session, but the session subdirectory can
contain several connections: one active and other being closed.
- Entries defining negotiated iSCSI parameters.
- initiator_name - contains initiator name
- sid - contains SID of this session
- reinstating - contains reinstatement state of this session
- force_close - write-only attribute, which allows to force close this
session. This is the only writable session attribute.
- 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.
Each connection subdirectory contains the following entries:
- cid - contains CID of this connection.
- ip - contains IP address of the connected initiator.
- state - contains processing state of this connection.
Below is a sample script, which configures 1 virtual disk "disk1" using
/disk1 image and one target iqn.2006-10.net.vlnb:tgt with all default
parameters:
#!/bin/bash
modprobe scst
modprobe scst_vdisk
echo "open disk1 /disk1 NV_CACHE" >/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
parameters, incoming and outgoing user names for CHAP authentification,
and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520,
which will see another set of devices. Also this sample configures CHAP
authentication for discovery sessions and iSNS server with access control.
#!/bin/bash
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
service iscsi-scst start
echo "192.168.1.16 AccessControl" >/sys/kernel/scst_tgt/targets/iscsi/iSNSServer
echo "add_attribute IncomingUser joeD 12charsecret" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
echo "add_attribute OutgoingUser jackD 12charsecret1" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
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 "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 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 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
echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/enabled
echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
The resulting overall SCST sysfs hierarchy with an initiator connected to
both iSCSI-SCST targets will look like:
/sys/kernel/scst_tgt
|-- devices
| |-- blockio
| | |-- block_size
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/0
| | |-- filename
| | |-- handler -> ../../handlers/vdisk_blockio
| | |-- read_only
| | |-- removable
| | |-- resync_size
| | |-- size
| | |-- t10_dev_id
| | `-- type
| |-- cdrom
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/1
| | |-- filename
| | |-- handler -> ../../handlers/vcdrom
| | |-- removable
| | |-- size
| | |-- t10_dev_id
| | `-- type
| |-- disk1
| | |-- block_size
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
| | |-- filename
| | |-- handler -> ../../handlers/vdisk_fileio
| | |-- nv_cache
| | |-- o_direct
| | |-- read_only
| | |-- removable
| | |-- resync_size
| | |-- size
| | |-- t10_dev_id
| | |-- type
| | `-- write_through
| |-- disk2
| | |-- block_size
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
| | |-- filename
| | |-- handler -> ../../handlers/vdisk_fileio
| | |-- nv_cache
| | |-- o_direct
| | |-- read_only
| | |-- removable
| | |-- resync_size
| | |-- size
| | |-- t10_dev_id
| | |-- type
| | `-- write_through
| `-- nullio
| |-- block_size
| |-- exported
| | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/26
| |-- handler -> ../../handlers/vdisk_nullio
| |-- read_only
| |-- removable
| |-- size
| |-- t10_dev_id
| `-- type
|-- handlers
| |-- vcdrom
| | |-- mgmt
| | |-- trace_level
| | `-- type
| |-- vdisk_blockio
| | |-- mgmt
| | |-- trace_level
| | `-- type
| |-- vdisk_fileio
| | |-- mgmt
| | |-- trace_level
| | `-- type
| `-- vdisk_nullio
| |-- mgmt
| |-- trace_level
| `-- type
|-- sgv
| |-- global_stats
| |-- sgv
| | `-- stats
| |-- sgv-clust
| | `-- stats
| `-- sgv-dma
| `-- stats
|-- targets
| `-- iscsi
| |-- IncomingUser
| |-- OutgoingUser
| |-- enabled
| |-- iSNSServer
| |-- iqn.2006-10.net.vlnb:tgt
| | |-- DataDigest
| | |-- FirstBurstLength
| | |-- HeaderDigest
| | |-- ImmediateData
| | |-- InitialR2T
| | |-- MaxBurstLength
| | |-- MaxOutstandingR2T
| | |-- MaxRecvDataSegmentLength
| | |-- MaxXmitDataSegmentLength
| | |-- QueuedCommands
| | |-- enabled
| | |-- ini_group
| | | `-- mgmt
| | |-- luns
| | | |-- 0
| | | | |-- device -> ../../../../../devices/disk1
| | | | `-- read_only
| | | |-- 1
| | | | |-- device -> ../../../../../devices/cdrom
| | | | `-- read_only
| | | `-- mgmt
| | |-- sessions
| | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520
| | | |-- 10.170.75.2
| | | | |-- cid
| | | | |-- ip
| | | | `-- state
| | | |-- DataDigest
| | | |-- FirstBurstLength
| | | |-- HeaderDigest
| | | |-- ImmediateData
| | | |-- InitialR2T
| | | |-- MaxBurstLength
| | | |-- MaxOutstandingR2T
| | | |-- MaxRecvDataSegmentLength
| | | |-- MaxXmitDataSegmentLength
| | | |-- active_commands
| | | |-- commands
| | | |-- force_close
| | | |-- initiator_name
| | | |-- reinstating
| | | `-- sid
| | `-- tid
| |-- iqn.2006-10.net.vlnb:tgt1
| | |-- DataDigest
| | |-- FirstBurstLength
| | |-- HeaderDigest
| | |-- ImmediateData
| | |-- IncomingUser
| | |-- IncomingUser1
| | |-- InitialR2T
| | |-- MaxBurstLength
| | |-- MaxOutstandingR2T
| | |-- MaxRecvDataSegmentLength
| | |-- MaxXmitDataSegmentLength
| | |-- OutgoingUser
| | |-- QueuedCommands
| | |-- enabled
| | |-- ini_group
| | | |-- mgmt
| | | `-- special_ini
| | | |-- initiators
| | | | |-- iqn.2005-03.org.open-iscsi:cacdcd2520
| | | | `-- mgmt
| | | `-- luns
| | | |-- 0
| | | | |-- device -> ../../../../../../../devices/blockio
| | | | `-- read_only
| | | `-- mgmt
| | |-- luns
| | | |-- 0
| | | | |-- device -> ../../../../../devices/disk2
| | | | `-- read_only
| | | |-- 26
| | | | |-- device -> ../../../../../devices/nullio
| | | | `-- read_only
| | | `-- mgmt
| | |-- sessions
| | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520
| | | |-- 10.170.75.2
| | | | |-- cid
| | | | |-- ip
| | | | `-- state
| | | |-- DataDigest
| | | |-- FirstBurstLength
| | | |-- HeaderDigest
| | | |-- ImmediateData
| | | |-- InitialR2T
| | | |-- MaxBurstLength
| | | |-- MaxOutstandingR2T
| | | |-- MaxRecvDataSegmentLength
| | | |-- MaxXmitDataSegmentLength
| | | |-- active_commands
| | | |-- commands
| | | |-- force_close
| | | |-- initiator_name
| | | |-- reinstating
| | | `-- sid
| | `-- tid
| |-- mgmt
| |-- open_state
| |-- trace_level
| `-- version
|-- threads
|-- trace_level
`-- version
Troubleshooting
---------------
@@ -160,9 +548,8 @@ abort or reset messages. It can happen under high I/O load, when your
target's backstorage gets overloaded, or working over a slow link, when
the link can't serve all the queued commands on time,
To workaround it you can reduce QueuedCommands parameter in
iscsi-scstd.conf file for the corresponding target to some lower value,
like 8 (default is 32).
To workaround it you can reduce QueuedCommands parameter for the
corresponding target to some lower value, like 8 (default is 32).
Also see SCST README file for more details about that issue and ways to
prevent it.
@@ -229,8 +616,6 @@ Credits
Thanks to:
* IET developers for IET
* Ming Zhang <blackmagic02881@gmail.com> for fixes
* Krzysztof Blaszkowski <kb@sysmikro.com.pl> for many fixes
-105
View File
@@ -1,105 +0,0 @@
Introduction
-------------
iSCSI Enterprise Target is for building an iSCSI storage system on
Linux. It is aimed at developing an iSCSI target satisfying enterprise
requirements.
We borrow code from an Ardis iSCSI target (with respect to the GPL).
Installation
-------------
The iSCSI target requires a host running the Linux operating system
with a kernel version of 2.6.19 (2.6.14 - 2.6.18 kernels using
backward compatibility patches, see below) or newer. You need to
enable "Cryptographic API" under "Cryptographic options" in the kernel
config. You also need to enable "CRC32c CRC algorithm" if you use
header or data digests. They are the kernel options, CONFIG_CRYPTO and
CONFIG_CRYPTO_CRC32C, respectively. The user-space code requires
OpenSSL library (http://www.openssl.org/).
The iSCSI target consists of kernel modules and a daemon. Since IET
is generally targeted at the latest stable mainline kernel, you might
need to apply a backward compatibility patch to compile it against
older kernel versions:
patch -p0 < patches/compat-2.6.14-2.6.18.patch
Compilation of the kernel modules require the path to the kernel
sources:
make KSRC=<kernel-src>
The path can also be set by editing the main Makefile. If KSRC is
omitted, make program will try to locate the kernel sources for
current running kernel. Be sure to check whether it finds the right
kernel sources.
This will build the modules, the daemon, and the control tool. To
install both, use:
make KSRC=<kernel-src> install
The kernel modules will be install in the module directory of the
kernel. The daemon and the control tool will be installed as ietd and
ietadm under /usr/sbin. The boot script will be installed as
iscsi-targt under /etc/init.d.
If you use Linux distribution that does not have /etc/init.d, the
boot script will not be installed. So you need to install it to an
appropriate directory manually.
Configuration
-------------
The daemon is configured via the configuration file /etc/ietd.conf.
See the man page and the example file for the current syntax.
The ietadm utility is for managing IET software dynamically. You can
change the configurations of running targets. See the help message.
The access control based on initiator address and target name patterns
is configured via two configuration files (/etc/initiators.allow and
/etc/initiators.deny). These files work like tcpd files
(/etc/hosts.allow and /etc/hosts.deny). This feature enables you to
hide a particular targets from some initiators. See the example files
for the supported expressions. You can change the configuration
dynamically. The modifications to the files become effective
immediately.
Starting
-------------
The target is not started automatically. So execute:
/etc/init.d/iscsi-target start
Note that you must edit the configuration file before starting the
target.
Stopping
-------------
Execute:
/etc/init.d/iscsi-target stop
Contact
-------------
Please send bug reports, comments, feature requests etc. to our
mailing list <iscsitarget-devel@lists.sourceforge.net>.
Developer Notes
----------------
The central resource for IET development is the
<iscsitarget-devel@lists.sourceforge.net> mailing list.
Our subversion repository can be found at: svn://svn.berlios.de/iscsitarget
When submitting patches, please diff against the code in our repository's
trunk and adhere otherwise to the same rules that apply to Linux kernel
development, in particular the Linux kernel coding style
($KSRC/Documentation/CodingStyle) and the rules for submitting patches
($KSRC/Documentation/SubmittingPatches), i.e. please send patches inline as
plain text.
+411 -62
View File
@@ -1,54 +1,15 @@
iSCSI SCST target driver
========================
Version 1.0.2, XX XXXXX 2009
----------------------------
ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
(http://iscsitarget.sourceforge.net). Reasons of the fork were:
This driver is a forked with all respects version of iSCSI Enterprise
Target (IET) (http://iscsitarget.sourceforge.net/) with updates to work
over SCST as well as with many improvements and bugfixes (see ChangeLog
file). The reason of fork is that the necessary changes are intrusive
and with the current IET merge policy, where only simple bugfix-like
patches, which doesn't touch the core code, could be merged, it is very
unlikely that they will be merged in the main IET trunk.
- To be able to use full power of SCST core.
To let it be installed and work at the same host together with IET
simultaneously all the driver's modules and files were renamed:
* ietd.conf -> iscsi-scstd.conf
* ietadm -> iscsi-scst-adm
* ietd -> iscsi-scstd
* iscsi-target -> iscsi-scst
* iscsi-target.ko -> iscsi-scst.ko
To use full power of TCP zero-copy transmit functions, especially
dealing with user space supplied via scst_user module memory, iSCSI-SCST
needs to be notified when Linux networking finished data transmission.
For that you should enable CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION
kernel config option. This is highly recommended, but not required. Basically,
you should consider usage of this option as some optimization, which IET
doesn't have, so if you don't use it, you will just revert to the
original IET behavior, when for data transmission:
- For in-kernel allocated memory (scst_vdisk and pass-through
handlers) usage of SGV cache on transmit path (READ-type commands)
will be disabled, but data will still be sent in zero-copy manner.
The performance hit will be not big, but performance will still
remain better, than for IET, because SGV cache will remain used on
receive path while IET doesn't have such feature.
- For user space allocated memory (scst_user handler) all transmitted
data will be additionally copied into temporary TCP buffers. The
performance hit will be quite noticeable.
Note, that if your network hardware does not support TX offload
functions or has them disabled, then TCP zero-copy transmit functions on
your system will not be used by Linux networking in any case, so
put_page_callback patch will not be able to improve performance for you.
You can check your network hardware offload capabilities by command
"ethtool -k ethX", where X is the network device number. At least
"tx-checksumming" and "scatter-gather" should be enabled.
- To fix all the problems, corner cases issues and iSCSI standard
violations which IET has.
See more info at http://scst.sourceforge.net/target_iscsi.html.
Usage
-----
@@ -56,22 +17,14 @@ Usage
See in http://scst.sourceforge.net/iscsi-scst-howto.txt how to configure
iSCSI-SCST.
ISCSI parameters like iSNS, CHAP and target parameters are configured in
iscsi-scstd.conf. All LUN information is configured using the
corresponding SCST interface. See in SCST README file section "Access
and devices visibility management (LUN masking)" to find out how to do
it. It is highly recommended to use scstadmin utility for that purpose.
The LUN information in iscsi-scstd.conf will be ignored. This is because
now responsibilities are divided between the target driver (iSCSI-SCST)
and the SCST core as it logically should be: the target driver is
responsible for handling targets and their parameters, SCST core is
responsible for handling backstorage.
In 2.0.0 usage of iscsi-scstd.conf as well as iscsi-scst-adm 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: All LUN information (access control) MUST be configured
========= BEFORE iscsi-scstd started!
IMPORTANT: In the procfs build all LUN information (access control)
========= MUST be configured BEFORE iscsi-scstd started!
Also see SCST README file how to tune for the best performance.
@@ -79,6 +32,405 @@ CAUTION: Working of target and initiator on the same host isn't fully
======= supported. See SCST README file for details.
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.
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:
- None, one or more subdirectories for targets with name equal to names
of the corresponding targets.
- IncomingUser[num] - optional one or more attributes containing user
name and password for incoming discovery user name. Not exist by
default and can be added through "mgmt" entry, see below.
- OutgoingUser - optional attribute containing user name and password
for outgoing discovery user name. Not exist by default and can be
added through "mgmt" entry, see below.
- iSNSServer - contains name or IP address of iSNS server with optional
"AccessControl" attribute, which allows to enable iSNS access
control. Empty by default.
- enabled - using this attribute you can enable or disable iSCSI-SCST
accept new connections. It allows to finish configuring global
iSCSI-SCST attributes before it starts accepting new connections. 0
by default.
- open_state - read-only attribute, which allows to see if the user
space part of iSCSI-SCST connected to the kernel part.
- 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
iSCSI-SCST and enabled optional features.
- mgmt - main management entry, which allows to configure iSCSI-SCST.
Namely, add/delete targets as well as add/delete optional global and
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 target subdirectory contains the following entries:
- 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.
- IncomingUser[num] - optional one or more attributes containing user
name and password for incoming user name. Not exist by default and can
be added through the "mgmt" entry, see above.
- OutgoingUser - optional attribute containing user name and password
for outgoing user name. Not exist by default and can be added through
the "mgmt" entry, see above.
- Entries defining default iSCSI parameters values used during iSCSI
parameters negotiation.
- QueuedCommands - defines maximum number of commands queued to any
session of this target.
- 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.
- tid - TID of this target.
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:
- One subdirectory for each TCP connection in this session. ISCSI-SCST
supports 1 connection per session, but the session subdirectory can
contain several connections: one active and other being closed.
- Entries defining negotiated iSCSI parameters.
- initiator_name - contains initiator name
- sid - contains SID of this session
- reinstating - contains reinstatement state of this session
- force_close - write-only attribute, which allows to force close this
session. This is the only writable session attribute.
- 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.
Each connection subdirectory contains the following entries:
- cid - contains CID of this connection.
- ip - contains IP address of the connected initiator.
- state - contains processing state of this connection.
Below is a sample script, which configures 1 virtual disk "disk1" using
/disk1 image and one target iqn.2006-10.net.vlnb:tgt with all default
parameters:
#!/bin/bash
modprobe scst
modprobe scst_vdisk
echo "open disk1 /disk1 NV_CACHE" >/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
parameters, incoming and outgoing user names for CHAP authentification,
and special permissions for initiator iqn.2005-03.org.open-iscsi:cacdcd2520,
which will see another set of devices. Also this sample configures CHAP
authentication for discovery sessions and iSNS server with access control.
#!/bin/bash
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
service iscsi-scst start
echo "192.168.1.16 AccessControl" >/sys/kernel/scst_tgt/targets/iscsi/iSNSServer
echo "add_attribute IncomingUser joeD 12charsecret" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
echo "add_attribute OutgoingUser jackD 12charsecret1" >/sys/kernel/scst_tgt/targets/iscsi/mgmt
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 "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 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 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
echo 1 >/sys/kernel/scst_tgt/targets/iscsi/iqn.2006-10.net.vlnb:tgt1/enabled
echo 1 >/sys/kernel/scst_tgt/targets/iscsi/enabled
The resulting overall SCST sysfs hierarchy with an initiator connected to
both iSCSI-SCST targets will look like:
/sys/kernel/scst_tgt
|-- devices
| |-- blockio
| | |-- block_size
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/ini_group/special_ini/luns/0
| | |-- filename
| | |-- handler -> ../../handlers/vdisk_blockio
| | |-- read_only
| | |-- removable
| | |-- resync_size
| | |-- size
| | |-- t10_dev_id
| | `-- type
| |-- cdrom
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/1
| | |-- filename
| | |-- handler -> ../../handlers/vcdrom
| | |-- removable
| | |-- size
| | |-- t10_dev_id
| | `-- type
| |-- disk1
| | |-- block_size
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt/luns/0
| | |-- filename
| | |-- handler -> ../../handlers/vdisk_fileio
| | |-- nv_cache
| | |-- o_direct
| | |-- read_only
| | |-- removable
| | |-- resync_size
| | |-- size
| | |-- t10_dev_id
| | |-- type
| | `-- write_through
| |-- disk2
| | |-- block_size
| | |-- exported
| | | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/0
| | |-- filename
| | |-- handler -> ../../handlers/vdisk_fileio
| | |-- nv_cache
| | |-- o_direct
| | |-- read_only
| | |-- removable
| | |-- resync_size
| | |-- size
| | |-- t10_dev_id
| | |-- type
| | `-- write_through
| `-- nullio
| |-- block_size
| |-- exported
| | `-- export0 -> ../../../targets/iscsi/iqn.2006-10.net.vlnb:tgt1/luns/26
| |-- handler -> ../../handlers/vdisk_nullio
| |-- read_only
| |-- removable
| |-- size
| |-- t10_dev_id
| `-- type
|-- handlers
| |-- vcdrom
| | |-- mgmt
| | |-- trace_level
| | `-- type
| |-- vdisk_blockio
| | |-- mgmt
| | |-- trace_level
| | `-- type
| |-- vdisk_fileio
| | |-- mgmt
| | |-- trace_level
| | `-- type
| `-- vdisk_nullio
| |-- mgmt
| |-- trace_level
| `-- type
|-- sgv
| |-- global_stats
| |-- sgv
| | `-- stats
| |-- sgv-clust
| | `-- stats
| `-- sgv-dma
| `-- stats
|-- targets
| `-- iscsi
| |-- IncomingUser
| |-- OutgoingUser
| |-- enabled
| |-- iSNSServer
| |-- iqn.2006-10.net.vlnb:tgt
| | |-- DataDigest
| | |-- FirstBurstLength
| | |-- HeaderDigest
| | |-- ImmediateData
| | |-- InitialR2T
| | |-- MaxBurstLength
| | |-- MaxOutstandingR2T
| | |-- MaxRecvDataSegmentLength
| | |-- MaxXmitDataSegmentLength
| | |-- QueuedCommands
| | |-- enabled
| | |-- ini_group
| | | `-- mgmt
| | |-- luns
| | | |-- 0
| | | | |-- device -> ../../../../../devices/disk1
| | | | `-- read_only
| | | |-- 1
| | | | |-- device -> ../../../../../devices/cdrom
| | | | `-- read_only
| | | `-- mgmt
| | |-- sessions
| | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520
| | | |-- 10.170.75.2
| | | | |-- cid
| | | | |-- ip
| | | | `-- state
| | | |-- DataDigest
| | | |-- FirstBurstLength
| | | |-- HeaderDigest
| | | |-- ImmediateData
| | | |-- InitialR2T
| | | |-- MaxBurstLength
| | | |-- MaxOutstandingR2T
| | | |-- MaxRecvDataSegmentLength
| | | |-- MaxXmitDataSegmentLength
| | | |-- active_commands
| | | |-- commands
| | | |-- force_close
| | | |-- initiator_name
| | | |-- reinstating
| | | `-- sid
| | `-- tid
| |-- iqn.2006-10.net.vlnb:tgt1
| | |-- DataDigest
| | |-- FirstBurstLength
| | |-- HeaderDigest
| | |-- ImmediateData
| | |-- IncomingUser
| | |-- IncomingUser1
| | |-- InitialR2T
| | |-- MaxBurstLength
| | |-- MaxOutstandingR2T
| | |-- MaxRecvDataSegmentLength
| | |-- MaxXmitDataSegmentLength
| | |-- OutgoingUser
| | |-- QueuedCommands
| | |-- enabled
| | |-- ini_group
| | | |-- mgmt
| | | `-- special_ini
| | | |-- initiators
| | | | |-- iqn.2005-03.org.open-iscsi:cacdcd2520
| | | | `-- mgmt
| | | `-- luns
| | | |-- 0
| | | | |-- device -> ../../../../../../../devices/blockio
| | | | `-- read_only
| | | `-- mgmt
| | |-- luns
| | | |-- 0
| | | | |-- device -> ../../../../../devices/disk2
| | | | `-- read_only
| | | |-- 26
| | | | |-- device -> ../../../../../devices/nullio
| | | | `-- read_only
| | | `-- mgmt
| | |-- sessions
| | | `-- iqn.2005-03.org.open-iscsi:cacdcd2520
| | | |-- 10.170.75.2
| | | | |-- cid
| | | | |-- ip
| | | | `-- state
| | | |-- DataDigest
| | | |-- FirstBurstLength
| | | |-- HeaderDigest
| | | |-- ImmediateData
| | | |-- InitialR2T
| | | |-- MaxBurstLength
| | | |-- MaxOutstandingR2T
| | | |-- MaxRecvDataSegmentLength
| | | |-- MaxXmitDataSegmentLength
| | | |-- active_commands
| | | |-- commands
| | | |-- force_close
| | | |-- initiator_name
| | | |-- reinstating
| | | `-- sid
| | `-- tid
| |-- mgmt
| |-- open_state
| |-- trace_level
| `-- version
|-- threads
|-- trace_level
`-- version
Troubleshooting
---------------
@@ -103,9 +455,8 @@ abort or reset messages. It can happen under high I/O load, when your
target's backstorage gets overloaded, or working over a slow link, when
the link can't serve all the queued commands on time,
To workaround it you can reduce QueuedCommands parameter in
iscsi-scstd.conf file for the corresponding target to some lower value,
like 8 (default is 32).
To workaround it you can reduce QueuedCommands parameter for the
corresponding target to some lower value, like 8 (default is 32).
Also see SCST README file for more details about that issue and ways to
prevent it.
@@ -152,8 +503,6 @@ Credits
Thanks to:
* IET developers for IET
* Ming Zhang <blackmagic02881@gmail.com> for fixes
* Krzysztof Blaszkowski <kb@sysmikro.com.pl> for many fixes
+18 -1
View File
@@ -1,3 +1,20 @@
- Fix support of ranges in parameters negotiation.
- __cmnd_abort() must be called by SCST core through new callback
instead of the driver itself.
- Remove the "poor man solution" suspending. Instead, immediate/unsolicited
data for suspended commands should be received in the dummy_page and
then, when they are resumed, aborted with BUSY status. All other
commands should proceed normally.
- Separate iSCSI request and response structures. Currently they share the
same structure. This architecture decision derived from IET and makes
the code a lot less readable, maintainable and effective as well as more
errors prone, so it must be corrected.
- Code beautifying, i.e. make it be written in the same nice looking style.
Particularly, all functions and variables names should share the same style.
- Fix support of ranges in parameters negotiation. Are there any initiators who
use ranges and, hence, can be used for testing?
- Minor "ToDo"'s spread in the code.
+90 -43
View File
@@ -34,33 +34,8 @@
#define aligned_u64 uint64_t __attribute__((aligned(8)))
#endif
struct iscsi_kern_target_info {
u32 tid;
char name[ISCSI_NAME_LEN];
};
struct iscsi_kern_session_info {
u32 tid;
aligned_u64 sid;
char initiator_name[ISCSI_NAME_LEN];
char user_name[ISCSI_NAME_LEN];
u32 exp_cmd_sn;
};
#define DIGEST_ALL (DIGEST_NONE | DIGEST_CRC32C)
#define DIGEST_NONE (1 << 0)
#define DIGEST_CRC32C (1 << 1)
struct iscsi_kern_conn_info {
u32 tid;
aligned_u64 sid;
u32 cid;
u32 stat_sn;
u32 exp_stat_sn;
int fd;
};
#define ISCSI_MAX_ATTR_NAME_LEN 50
#define ISCSI_MAX_ATTR_VALUE_LEN 512
enum {
key_initial_r2t,
@@ -95,20 +70,74 @@ enum {
key_target,
};
struct iscsi_kern_param_info {
struct iscsi_kern_target_info {
u32 tid;
u32 cookie;
char name[ISCSI_NAME_LEN];
u32 attrs_num;
aligned_u64 attrs_ptr;
};
struct iscsi_kern_session_info {
u32 tid;
aligned_u64 sid;
char initiator_name[ISCSI_NAME_LEN];
#ifdef CONFIG_SCST_PROC
char user_name[ISCSI_NAME_LEN];
#endif
u32 exp_cmd_sn;
s32 session_params[session_key_last];
s32 target_params[target_key_last];
};
#define DIGEST_ALL (DIGEST_NONE | DIGEST_CRC32C)
#define DIGEST_NONE (1 << 0)
#define DIGEST_CRC32C (1 << 1)
struct iscsi_kern_conn_info {
u32 tid;
aligned_u64 sid;
u32 param_type;
u32 cid;
u32 stat_sn;
u32 exp_stat_sn;
int fd;
};
struct iscsi_kern_attr {
u32 mode;
char name[ISCSI_MAX_ATTR_NAME_LEN];
};
struct iscsi_kern_mgmt_cmd_res_info {
u32 tid;
u32 cookie;
u32 req_cmd;
u32 result;
char value[ISCSI_MAX_ATTR_VALUE_LEN];
};
struct iscsi_kern_params_info {
u32 tid;
aligned_u64 sid;
u32 params_type;
u32 partial;
s32 session_param[session_key_last];
s32 target_param[target_key_last];
s32 session_params[session_key_last];
s32 target_params[target_key_last];
};
enum iscsi_kern_event_code {
#ifndef CONFIG_SCST_PROC
E_ADD_TARGET,
E_DEL_TARGET,
E_MGMT_CMD,
E_ENABLE_TARGET,
E_DISABLE_TARGET,
E_GET_ATTR_VALUE,
E_SET_ATTR_VALUE,
#endif
E_CONN_CLOSE,
};
@@ -117,10 +146,26 @@ struct iscsi_kern_event {
aligned_u64 sid;
u32 cid;
u32 code;
u32 cookie;
char target_name[ISCSI_NAME_LEN];
u32 param1_size;
u32 param2_size;
};
struct iscsi_kern_register_info {
aligned_u64 version;
union {
aligned_u64 version;
struct {
int max_data_seg_len;
int max_queued_cmds;
};
};
};
struct iscsi_kern_attr_info {
u32 tid;
u32 cookie;
struct iscsi_kern_attr attr;
};
#define DEFAULT_NR_QUEUED_CMNDS 32
@@ -129,19 +174,21 @@ struct iscsi_kern_register_info {
#define NETLINK_ISCSI_SCST 25
/* On success returns MAX_DATA_SEG_LEN */
#define REGISTER_USERD _IOW('s', 0, struct iscsi_kern_register_info)
#define REGISTER_USERD _IOWR('s', 0, struct iscsi_kern_register_info)
#define ADD_TARGET _IOW('s', 1, struct iscsi_kern_target_info)
#define DEL_TARGET _IOW('s', 2, struct iscsi_kern_target_info)
#define ENABLE_TARGET _IOW('s', 3, struct iscsi_kern_target_info)
#define DISABLE_TARGET _IOW('s', 4, struct iscsi_kern_target_info)
#define ADD_SESSION _IOW('s', 5, struct iscsi_kern_session_info)
#define DEL_SESSION _IOW('s', 6, struct iscsi_kern_session_info)
#define ADD_CONN _IOW('s', 7, struct iscsi_kern_conn_info)
#define DEL_CONN _IOW('s', 8, struct iscsi_kern_conn_info)
#define ISCSI_PARAM_SET _IOW('s', 9, struct iscsi_kern_param_info)
#define ISCSI_PARAM_GET _IOWR('s', 10, struct iscsi_kern_param_info)
#define ADD_SESSION _IOW('s', 3, struct iscsi_kern_session_info)
#define DEL_SESSION _IOW('s', 4, struct iscsi_kern_session_info)
#define ADD_CONN _IOW('s', 5, struct iscsi_kern_conn_info)
#define DEL_CONN _IOW('s', 6, struct iscsi_kern_conn_info)
#define ISCSI_PARAM_SET _IOW('s', 7, struct iscsi_kern_params_info)
#define ISCSI_PARAM_GET _IOWR('s', 8, struct iscsi_kern_params_info)
#ifndef CONFIG_SCST_PROC
#define ISCSI_ATTR_ADD _IOW('s', 9, struct iscsi_kern_attr_info)
#define ISCSI_ATTR_DEL _IOW('s', 10, struct iscsi_kern_attr_info)
#define MGMT_CMD_CALLBACK _IOW('s', 11, struct iscsi_kern_mgmt_cmd_res_info)
#endif
static inline int iscsi_is_key_internal(int key)
{
+1 -1
View File
@@ -21,4 +21,4 @@
#define ISCSI_VERSION_STRING_SUFFIX
#endif
#define ISCSI_VERSION_STRING "1.0.2/0.4.17r214" ISCSI_VERSION_STRING_SUFFIX
#define ISCSI_VERSION_STRING "2.0.0/0.4.17r214" ISCSI_VERSION_STRING_SUFFIX
+752 -184
View File
File diff suppressed because it is too large Load Diff
+18 -4
View File
@@ -62,6 +62,20 @@ out:
#ifdef CONFIG_SCST_PROC
static int print_digest_state(char *p, size_t size, unsigned long flags)
{
int pos;
if (DIGEST_NONE & flags)
pos = scnprintf(p, size, "%s", "none");
else if (DIGEST_CRC32C & flags)
pos = scnprintf(p, size, "%s", "crc32c");
else
pos = scnprintf(p, size, "%s", "unknown");
return pos;
}
/* target_mutex supposed to be locked */
void conn_info_show(struct seq_file *seq, struct iscsi_session *session)
{
@@ -666,8 +680,8 @@ static int iscsi_conn_alloc(struct iscsi_session *session,
conn->rd_state = ISCSI_CONN_RD_STATE_IDLE;
conn->wr_state = ISCSI_CONN_WR_STATE_IDLE;
conn->hdigest_type = session->sess_param.header_digest;
conn->ddigest_type = session->sess_param.data_digest;
conn->hdigest_type = session->sess_params.header_digest;
conn->ddigest_type = session->sess_params.data_digest;
res = digest_init(conn);
if (res != 0)
goto out_err_free1;
@@ -770,7 +784,7 @@ out_err:
}
/* target_mutex supposed to be locked */
int conn_add(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
{
struct iscsi_conn *conn, *new_conn = NULL;
int err;
@@ -805,7 +819,7 @@ out:
}
/* target_mutex supposed to be locked */
int conn_del(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
int __del_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
{
struct iscsi_conn *conn;
int err = -EEXIST;
+60 -8
View File
@@ -86,36 +86,88 @@ static void event_recv(struct sock *sk, int length)
}
#endif
static int notify(void *data, int len)
/* event_mutex supposed to be held */
static int __event_send(const void *buf, int buf_len)
{
int res = 0, len;
struct sk_buff *skb;
struct nlmsghdr *nlh;
static u32 seq;
static u32 seq; /* protected by event_mutex */
TRACE_ENTRY();
if (ctr_open_state != ISCSI_CTR_OPEN_STATE_OPEN)
goto out;
len = NLMSG_SPACE(buf_len);
skb = alloc_skb(NLMSG_SPACE(len), GFP_KERNEL);
if (!skb)
return -ENOMEM;
if (skb == NULL) {
PRINT_ERROR("alloc_skb() failed (len %d)", len);
res = -ENOMEM;
goto out;
}
nlh = __nlmsg_put(skb, iscsid_pid, seq++, NLMSG_DONE,
len - sizeof(*nlh), 0);
memcpy(NLMSG_DATA(nlh), data, len);
memcpy(NLMSG_DATA(nlh), buf, buf_len);
res = netlink_unicast(nl, skb, iscsid_pid, 0);
if (res <= 0) {
if (res != -ECONNREFUSED)
PRINT_ERROR("netlink_unicast() failed: %d", res);
else
TRACE(TRACE_MINOR, "netlink_unicast() failed: %s. "
"Not functioning user space?",
"Connection refused");
goto out;
}
return netlink_unicast(nl, skb, iscsid_pid, 0);
out:
TRACE_EXIT_RES(res);
return res;
}
int event_send(u32 tid, u64 sid, u32 cid, enum iscsi_kern_event_code code)
int event_send(u32 tid, u64 sid, u32 cid, u32 cookie,
enum iscsi_kern_event_code code,
const char *param1, const char *param2)
{
int err;
static DEFINE_MUTEX(event_mutex);
struct iscsi_kern_event event;
int param1_size, param2_size;
param1_size = (param1 != NULL) ? strlen(param1) : 0;
param2_size = (param2 != NULL) ? strlen(param2) : 0;
event.tid = tid;
event.sid = sid;
event.cid = cid;
event.code = code;
event.cookie = cookie;
event.param1_size = param1_size;
event.param2_size = param2_size;
err = notify(&event, NLMSG_SPACE(sizeof(struct iscsi_kern_event)));
mutex_lock(&event_mutex);
err = __event_send(&event, sizeof(event));
if (err <= 0)
goto out_unlock;
if (param1_size > 0) {
err = __event_send(param1, param1_size);
if (err <= 0)
goto out_unlock;
}
if (param2_size > 0) {
err = __event_send(param2, param2_size);
if (err <= 0)
goto out_unlock;
}
out_unlock:
mutex_unlock(&event_mutex);
return err;
}
+29 -18
View File
@@ -37,7 +37,6 @@
static int ctr_major;
static char ctr_name[] = "iscsi-scst-ctl";
static int iscsi_template_registered;
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
unsigned long iscsi_trace_flag = ISCSI_DEFAULT_LOG_FLAGS;
@@ -725,7 +724,7 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status)
TRACE_DBG("req %p", req);
pdusize = req->conn->session->sess_param.max_xmit_data_length;
pdusize = req->conn->session->sess_params.max_xmit_data_length;
expsize = req->read_size;
size = min(expsize, (u32)req->bufflen);
offset = 0;
@@ -1007,7 +1006,7 @@ out:
static inline int iscsi_get_allowed_cmds(struct iscsi_session *sess)
{
int res = max(-1, (int)sess->max_queued_cmnds -
int res = max(-1, (int)sess->tgt_params.queued_cmnds -
atomic_read(&sess->active_cmds)-1);
TRACE_DBG("allowed cmds %d (sess %p, active_cmds %d)", res,
sess, atomic_read(&sess->active_cmds));
@@ -1461,12 +1460,12 @@ static void send_r2t(struct iscsi_cmnd *req)
*/
EXTRACHECKS_BUG_ON(req->outstanding_r2t >
sess->sess_param.max_outstanding_r2t);
sess->sess_params.max_outstanding_r2t);
if (req->outstanding_r2t == sess->sess_param.max_outstanding_r2t)
if (req->outstanding_r2t == sess->sess_params.max_outstanding_r2t)
goto out;
burst = sess->sess_param.max_burst_length;
burst = sess->sess_params.max_burst_length;
offset = be32_to_cpu(cmnd_hdr(req)->data_length) -
req->r2t_len_to_send;
@@ -1498,7 +1497,7 @@ static void send_r2t(struct iscsi_cmnd *req)
list_add_tail(&rsp->write_list_entry, &send);
req->outstanding_r2t++;
} while ((req->outstanding_r2t < sess->sess_param.max_outstanding_r2t) &&
} while ((req->outstanding_r2t < sess->sess_params.max_outstanding_r2t) &&
(req->r2t_len_to_send != 0));
iscsi_cmnds_init_write(&send, ISCSI_INIT_WRITE_WAKE);
@@ -1679,7 +1678,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
if (dir & SCST_DATA_WRITE) {
unsolicited_data_expected = !(req_hdr->flags & ISCSI_CMD_FINAL);
if (unlikely(session->sess_param.initial_r2t &&
if (unlikely(session->sess_params.initial_r2t &&
unsolicited_data_expected)) {
PRINT_ERROR("Initiator %s violated negotiated "
"parameters: initial R2T is required (ITT %x, "
@@ -1688,7 +1687,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
goto out_close;
}
if (unlikely(!session->sess_param.immediate_data &&
if (unlikely(!session->sess_params.immediate_data &&
req->pdu.datasize)) {
PRINT_ERROR("Initiator %s violated negotiated "
"parameters: forbidden immediate data sent "
@@ -1697,13 +1696,13 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
goto out_close;
}
if (unlikely(session->sess_param.first_burst_length < req->pdu.datasize)) {
if (unlikely(session->sess_params.first_burst_length < req->pdu.datasize)) {
PRINT_ERROR("Initiator %s violated negotiated "
"parameters: immediate data len (%d) > "
"first_burst_length (%d) (ITT %x, op %x)",
session->initiator_name,
req->pdu.datasize,
session->sess_param.first_burst_length,
session->sess_params.first_burst_length,
cmnd_itt(req), req_hdr->scb[0]);
goto out_close;
}
@@ -1732,7 +1731,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
req->outstanding_r2t = 1;
req->r2t_len_to_send = req->r2t_len_to_receive -
min_t(unsigned int,
session->sess_param.first_burst_length -
session->sess_params.first_burst_length -
req->pdu.datasize,
req->r2t_len_to_receive);
} else
@@ -2853,12 +2852,12 @@ static int check_segment_length(struct iscsi_cmnd *cmnd)
struct iscsi_conn *conn = cmnd->conn;
struct iscsi_session *session = conn->session;
if (unlikely(cmnd->pdu.datasize > session->sess_param.max_recv_data_length)) {
if (unlikely(cmnd->pdu.datasize > session->sess_params.max_recv_data_length)) {
PRINT_ERROR("Initiator %s violated negotiated parameters: "
"data too long (ITT %x, datasize %u, "
"max_recv_data_length %u", session->initiator_name,
cmnd_itt(cmnd), cmnd->pdu.datasize,
session->sess_param.max_recv_data_length);
session->sess_params.max_recv_data_length);
mark_conn_closed(conn);
return -EINVAL;
}
@@ -3463,6 +3462,16 @@ static struct scst_trace_log iscsi_local_trace_tbl[] = {
#define ISCSI_TRACE_TLB_HELP ", d_read, d_write, conn, conn_dbg, iov, pdu, net_page"
#endif
#define ISCSI_MGMT_CMD_HELP \
" echo \"add_attribute IncomingUser name password\" >mgmt\n" \
" echo \"del_attribute IncomingUser name\" >mgmt\n" \
" echo \"add_attribute OutgoingUser name password\" >mgmt\n" \
" echo \"del_attribute OutgoingUser name\" >mgmt\n" \
" echo \"add_target_attribute target_name IncomingUser name password\" >mgmt\n" \
" echo \"del_target_attribute target_name IncomingUser name\" >mgmt\n" \
" echo \"add_target_attribute target_name OutgoingUser name password\" >mgmt\n" \
" echo \"del_target_attribute target_name OutgoingUser name\" >mgmt\n"
struct scst_tgt_template iscsi_template = {
.name = "iscsi",
.sg_tablesize = 0xFFFF /* no limit */,
@@ -3473,8 +3482,12 @@ struct scst_tgt_template iscsi_template = {
.tgtt_attrs = iscsi_attrs,
.tgt_attrs = iscsi_tgt_attrs,
.sess_attrs = iscsi_sess_attrs,
.enable_tgt = iscsi_enable_target,
.is_tgt_enabled = iscsi_is_target_enabled,
.enable_target = iscsi_enable_target,
.is_target_enabled = iscsi_is_target_enabled,
.add_target = iscsi_sysfs_add_target,
.del_target = iscsi_sysfs_del_target,
.mgmt_cmd = iscsi_sysfs_mgmt_cmd,
.mgmt_cmd_help = ISCSI_MGMT_CMD_HELP,
#endif
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
.default_trace_flags = ISCSI_DEFAULT_LOG_FLAGS,
@@ -3594,8 +3607,6 @@ static int __init iscsi_init(void)
if (err < 0)
goto out_kmem;
iscsi_template_registered = 1;
#ifdef CONFIG_SCST_PROC
err = iscsi_procfs_init();
if (err < 0)
+62 -35
View File
@@ -33,7 +33,11 @@
#define iscsi_sense_unexpected_unsolicited_data ABORTED_COMMAND, 0x0C, 0x0C
#define iscsi_sense_incorrect_amount_of_data ABORTED_COMMAND, 0x0C, 0x0D
struct iscsi_sess_param {
/*
* All members must have int type to match expectations of iscsi_tgt_store_*()
* functions!
*/
struct iscsi_sess_params {
int initial_r2t;
int immediate_data;
int max_connections;
@@ -55,7 +59,11 @@ struct iscsi_sess_param {
int ifmarkint;
};
struct iscsi_trgt_param {
/*
* All members must have int type to match expectations of iscsi_tgt_store_*()
* functions!
*/
struct iscsi_tgt_params {
int queued_cmnds;
};
@@ -64,8 +72,18 @@ struct network_thread_info {
unsigned int ready;
};
struct iscsi_target;
struct iscsi_cmnd;
#ifndef CONFIG_SCST_PROC
struct iscsi_attr {
struct list_head attrs_list_entry;
struct kobj_attribute attr;
struct iscsi_target *target;
const char *name;
};
#endif
struct iscsi_target {
struct scst_tgt *scst_tgt;
@@ -73,24 +91,16 @@ struct iscsi_target {
struct list_head session_list; /* protected by target_mutex */
/* Both protected by target_mgmt_mutex */
struct iscsi_trgt_param trgt_param;
/*
* Put here to have uniform parameters checking and assigning
* from various places, including iscsi-scst-adm.
*/
struct iscsi_sess_param trgt_sess_param;
struct list_head target_list_entry;
u32 tid;
/* All protected by target_sysfs_mutex */
/* Protected by iscsi_sysfs_mutex */
unsigned int tgt_enabled:1;
unsigned int expected_ioctl;
int ioctl_res;
struct completion *target_enabling_cmpl;
struct mutex target_sysfs_mutex;
#ifndef CONFIG_SCST_PROC
/* Protected by target_mutex */
struct list_head attrs_list;
#endif
char name[ISCSI_NAME_LEN];
};
@@ -107,7 +117,8 @@ struct iscsi_session {
/* Unprotected, since accessed only from a single read thread */
u32 next_ttt;
u32 max_queued_cmnds; /* unprotected, since read-only */
/* Read only, if there are connection(s) */
struct iscsi_tgt_params tgt_params;
atomic_t active_cmds;
spinlock_t sn_lock;
@@ -119,7 +130,7 @@ struct iscsi_session {
struct iscsi_cmnd *tm_rsp;
/* Read only, if there are connection(s) */
struct iscsi_sess_param sess_param;
struct iscsi_sess_params sess_params;
/*
* In some corner cases commands can be deleted from the hash
@@ -440,8 +451,13 @@ struct iscsi_cmnd {
#define ISCSI_TM_DATA_WAIT_TIMEOUT (10 * HZ)
#define ISCSI_TM_DATA_WAIT_SCHED_TIMEOUT (ISCSI_TM_DATA_WAIT_TIMEOUT + HZ)
#define ISCSI_CTR_OPEN_STATE_CLOSED 0
#define ISCSI_CTR_OPEN_STATE_OPEN 1
#define ISCSI_CTR_OPEN_STATE_CLOSING 2
extern struct mutex target_mgmt_mutex;
extern int ctr_open_state;
extern const struct file_operations ctr_fops;
extern spinlock_t iscsi_rd_lock;
@@ -470,8 +486,8 @@ extern void iscsi_fail_data_waiting_cmnd(struct iscsi_cmnd *cmnd);
/* conn.c */
extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
extern void conn_reinst_finished(struct iscsi_conn *);
extern int conn_add(struct iscsi_session *, struct iscsi_kern_conn_info *);
extern int conn_del(struct iscsi_session *, struct iscsi_kern_conn_info *);
extern int __add_conn(struct iscsi_session *, struct iscsi_kern_conn_info *);
extern int __del_conn(struct iscsi_session *, struct iscsi_kern_conn_info *);
#ifdef CONFIG_SCST_PROC
extern int conn_free(struct iscsi_conn *);
#endif
@@ -499,51 +515,62 @@ extern void iscsi_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *scst_mcmd);
extern void req_add_to_write_timeout_list(struct iscsi_cmnd *req);
/* target.c */
#ifndef CONFIG_SCST_PROC
#ifdef CONFIG_SCST_PROC
extern const struct seq_operations iscsi_seq_op;
#else
extern const struct attribute *iscsi_tgt_attrs[];
extern ssize_t iscsi_enable_target(struct scst_tgt *scst_tgt, const char *buf,
size_t size);
extern bool iscsi_is_target_enabled(struct scst_tgt *scst_tgt);
extern ssize_t iscsi_sysfs_send_event(uint32_t tid,
enum iscsi_kern_event_code code,
const char *param1, const char *param2, void **data);
#endif
struct iscsi_target *target_lookup_by_id(u32);
extern int target_add(struct iscsi_kern_target_info *);
extern int target_enable(struct iscsi_kern_target_info *);
extern int target_disable(struct iscsi_kern_target_info *);
extern int target_del(u32 id);
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_del_target(const char *target_name);
extern ssize_t iscsi_sysfs_mgmt_cmd(const 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);
extern void target_del_all(void);
extern const struct seq_operations iscsi_seq_op;
/* config.c */
#ifdef CONFIG_SCST_PROC
extern int iscsi_procfs_init(void);
extern void iscsi_procfs_exit(void);
#else
extern const struct attribute *iscsi_attrs[];
extern int iscsi_add_attr(struct iscsi_target *target,
const struct iscsi_kern_attr *user_info);
extern void __iscsi_del_attr(struct iscsi_target *target,
struct iscsi_attr *tgt_attr);
#endif
/* session.c */
#ifdef CONFIG_SCST_PROC
int print_digest_state(char *p, size_t size, unsigned long flags);
#else
#ifndef CONFIG_SCST_PROC
extern const struct attribute *iscsi_sess_attrs[];
#endif
extern const struct file_operations session_seq_fops;
extern struct iscsi_session *session_lookup(struct iscsi_target *, u64);
extern void sess_reinst_finished(struct iscsi_session *);
extern int session_add(struct iscsi_target *, struct iscsi_kern_session_info *);
extern int session_del(struct iscsi_target *, u64);
extern int __add_session(struct iscsi_target *,
struct iscsi_kern_session_info *);
extern int __del_session(struct iscsi_target *, u64);
extern int session_free(struct iscsi_session *session, bool del);
/* params.c */
extern int iscsi_param_set(struct iscsi_target *,
struct iscsi_kern_param_info *, int);
extern const char *iscsi_get_digest_name(int val, char *res);
extern const char *iscsi_get_bool_value(int val);
extern int iscsi_params_set(struct iscsi_target *,
struct iscsi_kern_params_info *, int);
/* event.c */
extern int event_send(u32, u64, u32, enum iscsi_kern_event_code);
extern int event_send(u32, u64, u32, u32, enum iscsi_kern_event_code,
const char *param1, const char *param2);
extern int event_init(void);
extern void event_exit(void);
+3 -2
View File
@@ -382,7 +382,7 @@ static void close_conn(struct iscsi_conn *conn)
TRACE_ENTRY();
TRACE_CONN_CLOSE("Closing connection %p (conn_ref_cnt=%d)", conn,
TRACE_MGMT_DBG("Closing connection %p (conn_ref_cnt=%d)", conn,
atomic_read(&conn->conn_ref_cnt));
iscsi_extracheck_is_rd_thread(conn);
@@ -550,7 +550,8 @@ static void close_conn(struct iscsi_conn *conn)
TRACE_CONN_CLOSE("Notifying user space about closing connection %p",
conn);
event_send(target->tid, session->sid, conn->cid, E_CONN_CLOSE);
event_send(target->tid, session->sid, conn->cid, 0, E_CONN_CLOSE,
NULL, NULL);
#ifdef CONFIG_SCST_PROC
mutex_lock(&target->target_mutex);
+184 -157
View File
@@ -16,37 +16,40 @@
#include "iscsi.h"
#include "digest.h"
#define CHECK_PARAM(info, iparam, word, min, max) \
do { \
if (!(info)->partial || ((info)->partial & 1 << key_##word)) \
if ((iparam)[key_##word] < (min) || \
(iparam)[key_##word] > (max)) { \
PRINT_ERROR("%s: %u is out of range (%u %u)", \
#word, (iparam)[key_##word], (min), (max)); \
if ((iparam)[key_##word] < (min)) \
(iparam)[key_##word] = (min); \
else \
(iparam)[key_##word] = (max); \
} \
#define CHECK_PARAM(info, iparams, word, min, max) \
do { \
if (!(info)->partial || ((info)->partial & 1 << key_##word)) { \
TRACE_DBG("%s: %u", #word, (iparams)[key_##word]); \
if ((iparams)[key_##word] < (min) || \
(iparams)[key_##word] > (max)) { \
PRINT_ERROR("%s: %u is out of range (%u %u)", \
#word, (iparams)[key_##word], (min), (max)); \
if ((iparams)[key_##word] < (min)) \
(iparams)[key_##word] = (min); \
else \
(iparams)[key_##word] = (max); \
} \
} \
} while (0)
#define SET_PARAM(param, info, iparam, word) \
({ \
int changed = 0; \
if (!(info)->partial || ((info)->partial & 1 << key_##word)) { \
if ((param)->word != (iparam)[key_##word]) \
changed = 1; \
(param)->word = (iparam)[key_##word]; \
} \
changed; \
#define SET_PARAM(params, info, iparams, word) \
({ \
int changed = 0; \
if (!(info)->partial || ((info)->partial & 1 << key_##word)) { \
if ((params)->word != (iparams)[key_##word]) \
changed = 1; \
(params)->word = (iparams)[key_##word]; \
TRACE_DBG("%s set to %u", #word, (iparams)[key_##word]); \
} \
changed; \
})
#define GET_PARAM(param, info, iparam, word) \
do { \
(iparam)[key_##word] = (param)->word; \
#define GET_PARAM(params, info, iparams, word) \
do { \
(iparams)[key_##word] = (params)->word; \
} while (0)
static const char *get_bool_name(int val)
const char *iscsi_get_bool_value(int val)
{
if (val)
return "Yes";
@@ -54,210 +57,234 @@ static const char *get_bool_name(int val)
return "No";
}
static const char *get_digest_name(int val)
const char *iscsi_get_digest_name(int val, char *res)
{
if (val == DIGEST_NONE)
return "None";
else if (val == DIGEST_CRC32C)
return "CRC32C";
else
return "Unknown";
int pos = 0;
if (val & DIGEST_NONE)
pos = sprintf(&res[pos], "%s", "None");
if (val & DIGEST_CRC32C)
pos += sprintf(&res[pos], "%s%s", (pos != 0) ? ", " : "",
"CRC32C");
if (pos == 0)
sprintf(&res[pos], "%s", "Unknown");
return res;
}
static void log_params(struct iscsi_sess_param *param)
static void log_params(struct iscsi_sess_params *params)
{
char digest_name[64];
PRINT_INFO("Negotiated parameters: InitialR2T %s, ImmediateData %s, "
"MaxConnections %d, MaxRecvDataSegmentLength %d, "
"MaxXmitDataSegmentLength %d, ",
get_bool_name(param->initial_r2t),
get_bool_name(param->immediate_data), param->max_connections,
param->max_recv_data_length, param->max_xmit_data_length);
iscsi_get_bool_value(params->initial_r2t),
iscsi_get_bool_value(params->immediate_data), params->max_connections,
params->max_recv_data_length, params->max_xmit_data_length);
PRINT_INFO(" MaxBurstLength %d, FirstBurstLength %d, "
"DefaultTime2Wait %d, DefaultTime2Retain %d, ",
param->max_burst_length, param->first_burst_length,
param->default_wait_time, param->default_retain_time);
params->max_burst_length, params->first_burst_length,
params->default_wait_time, params->default_retain_time);
PRINT_INFO(" MaxOutstandingR2T %d, DataPDUInOrder %s, "
"DataSequenceInOrder %s, ErrorRecoveryLevel %d, ",
param->max_outstanding_r2t,
get_bool_name(param->data_pdu_inorder),
get_bool_name(param->data_sequence_inorder),
param->error_recovery_level);
params->max_outstanding_r2t,
iscsi_get_bool_value(params->data_pdu_inorder),
iscsi_get_bool_value(params->data_sequence_inorder),
params->error_recovery_level);
PRINT_INFO(" HeaderDigest %s, DataDigest %s, OFMarker %s, "
"IFMarker %s, OFMarkInt %d, IFMarkInt %d",
get_digest_name(param->header_digest),
get_digest_name(param->data_digest),
get_bool_name(param->ofmarker),
get_bool_name(param->ifmarker),
param->ofmarkint, param->ifmarkint);
iscsi_get_digest_name(params->header_digest, digest_name),
iscsi_get_digest_name(params->data_digest, digest_name),
iscsi_get_bool_value(params->ofmarker),
iscsi_get_bool_value(params->ifmarker),
params->ofmarkint, params->ifmarkint);
}
/* target_mutex supposed to be locked */
static void sess_param_check(struct iscsi_kern_param_info *info)
static void sess_params_check(struct iscsi_kern_params_info *info)
{
int32_t *iparam = info->session_param;
int32_t *iparams = info->session_params;
const int max_len = ISCSI_CONN_IOV_MAX * PAGE_SIZE;
CHECK_PARAM(info, iparam, max_connections, 1, 1);
CHECK_PARAM(info, iparam, max_recv_data_length, 512,
(int32_t) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
CHECK_PARAM(info, iparam, max_xmit_data_length, 512,
(int32_t) (ISCSI_CONN_IOV_MAX * PAGE_SIZE));
CHECK_PARAM(info, iparam, error_recovery_level, 0, 0);
CHECK_PARAM(info, iparam, data_pdu_inorder, 0, 1);
CHECK_PARAM(info, iparam, data_sequence_inorder, 0, 1);
CHECK_PARAM(info, iparams, initial_r2t, 0, 1);
CHECK_PARAM(info, iparams, immediate_data, 0, 1);
CHECK_PARAM(info, iparams, max_connections, 1, 1);
CHECK_PARAM(info, iparams, max_recv_data_length, 512, max_len);
CHECK_PARAM(info, iparams, max_xmit_data_length, 512, max_len);
CHECK_PARAM(info, iparams, max_burst_length, 512, max_len);
CHECK_PARAM(info, iparams, first_burst_length, 512, max_len);
CHECK_PARAM(info, iparams, max_outstanding_r2t, 1, 65535);
CHECK_PARAM(info, iparams, error_recovery_level, 0, 0);
CHECK_PARAM(info, iparams, data_pdu_inorder, 0, 1);
CHECK_PARAM(info, iparams, data_sequence_inorder, 0, 1);
digest_alg_available(&iparam[key_header_digest]);
digest_alg_available(&iparam[key_data_digest]);
digest_alg_available(&iparams[key_header_digest]);
digest_alg_available(&iparams[key_data_digest]);
CHECK_PARAM(info, iparam, ofmarker, 0, 0);
CHECK_PARAM(info, iparam, ifmarker, 0, 0);
CHECK_PARAM(info, iparams, ofmarker, 0, 0);
CHECK_PARAM(info, iparams, ifmarker, 0, 0);
return;
}
/* target_mutex supposed to be locked */
static void sess_param_set(struct iscsi_sess_param *param,
struct iscsi_kern_param_info *info)
static void sess_params_set(struct iscsi_sess_params *params,
struct iscsi_kern_params_info *info)
{
int32_t *iparam = info->session_param;
int32_t *iparams = info->session_params;
SET_PARAM(param, info, iparam, initial_r2t);
SET_PARAM(param, info, iparam, immediate_data);
SET_PARAM(param, info, iparam, max_connections);
SET_PARAM(param, info, iparam, max_recv_data_length);
SET_PARAM(param, info, iparam, max_xmit_data_length);
SET_PARAM(param, info, iparam, max_burst_length);
SET_PARAM(param, info, iparam, first_burst_length);
SET_PARAM(param, info, iparam, default_wait_time);
SET_PARAM(param, info, iparam, default_retain_time);
SET_PARAM(param, info, iparam, max_outstanding_r2t);
SET_PARAM(param, info, iparam, data_pdu_inorder);
SET_PARAM(param, info, iparam, data_sequence_inorder);
SET_PARAM(param, info, iparam, error_recovery_level);
SET_PARAM(param, info, iparam, header_digest);
SET_PARAM(param, info, iparam, data_digest);
SET_PARAM(param, info, iparam, ofmarker);
SET_PARAM(param, info, iparam, ifmarker);
SET_PARAM(param, info, iparam, ofmarkint);
SET_PARAM(param, info, iparam, ifmarkint);
SET_PARAM(params, info, iparams, initial_r2t);
SET_PARAM(params, info, iparams, immediate_data);
SET_PARAM(params, info, iparams, max_connections);
SET_PARAM(params, info, iparams, max_recv_data_length);
SET_PARAM(params, info, iparams, max_xmit_data_length);
SET_PARAM(params, info, iparams, max_burst_length);
SET_PARAM(params, info, iparams, first_burst_length);
SET_PARAM(params, info, iparams, default_wait_time);
SET_PARAM(params, info, iparams, default_retain_time);
SET_PARAM(params, info, iparams, max_outstanding_r2t);
SET_PARAM(params, info, iparams, data_pdu_inorder);
SET_PARAM(params, info, iparams, data_sequence_inorder);
SET_PARAM(params, info, iparams, error_recovery_level);
SET_PARAM(params, info, iparams, header_digest);
SET_PARAM(params, info, iparams, data_digest);
SET_PARAM(params, info, iparams, ofmarker);
SET_PARAM(params, info, iparams, ifmarker);
SET_PARAM(params, info, iparams, ofmarkint);
SET_PARAM(params, info, iparams, ifmarkint);
return;
}
static void sess_param_get(struct iscsi_sess_param *param,
struct iscsi_kern_param_info *info)
static void sess_params_get(struct iscsi_sess_params *params,
struct iscsi_kern_params_info *info)
{
int32_t *iparam = info->session_param;
int32_t *iparams = info->session_params;
GET_PARAM(param, info, iparam, initial_r2t);
GET_PARAM(param, info, iparam, immediate_data);
GET_PARAM(param, info, iparam, max_connections);
GET_PARAM(param, info, iparam, max_recv_data_length);
GET_PARAM(param, info, iparam, max_xmit_data_length);
GET_PARAM(param, info, iparam, max_burst_length);
GET_PARAM(param, info, iparam, first_burst_length);
GET_PARAM(param, info, iparam, default_wait_time);
GET_PARAM(param, info, iparam, default_retain_time);
GET_PARAM(param, info, iparam, max_outstanding_r2t);
GET_PARAM(param, info, iparam, data_pdu_inorder);
GET_PARAM(param, info, iparam, data_sequence_inorder);
GET_PARAM(param, info, iparam, error_recovery_level);
GET_PARAM(param, info, iparam, header_digest);
GET_PARAM(param, info, iparam, data_digest);
GET_PARAM(param, info, iparam, ofmarker);
GET_PARAM(param, info, iparam, ifmarker);
GET_PARAM(param, info, iparam, ofmarkint);
GET_PARAM(param, info, iparam, ifmarkint);
GET_PARAM(params, info, iparams, initial_r2t);
GET_PARAM(params, info, iparams, immediate_data);
GET_PARAM(params, info, iparams, max_connections);
GET_PARAM(params, info, iparams, max_recv_data_length);
GET_PARAM(params, info, iparams, max_xmit_data_length);
GET_PARAM(params, info, iparams, max_burst_length);
GET_PARAM(params, info, iparams, first_burst_length);
GET_PARAM(params, info, iparams, default_wait_time);
GET_PARAM(params, info, iparams, default_retain_time);
GET_PARAM(params, info, iparams, max_outstanding_r2t);
GET_PARAM(params, info, iparams, data_pdu_inorder);
GET_PARAM(params, info, iparams, data_sequence_inorder);
GET_PARAM(params, info, iparams, error_recovery_level);
GET_PARAM(params, info, iparams, header_digest);
GET_PARAM(params, info, iparams, data_digest);
GET_PARAM(params, info, iparams, ofmarker);
GET_PARAM(params, info, iparams, ifmarker);
GET_PARAM(params, info, iparams, ofmarkint);
GET_PARAM(params, info, iparams, ifmarkint);
return;
}
/* target_mutex supposed to be locked */
static void trgt_param_check(struct iscsi_kern_param_info *info)
static void tgt_params_check(struct iscsi_kern_params_info *info)
{
int32_t *iparam = info->target_param;
int32_t *iparams = info->target_params;
CHECK_PARAM(info, iparam, queued_cmnds, MIN_NR_QUEUED_CMNDS,
CHECK_PARAM(info, iparams, queued_cmnds, MIN_NR_QUEUED_CMNDS,
MAX_NR_QUEUED_CMNDS);
return;
}
/* target_mutex supposed to be locked */
static void trgt_param_set(struct iscsi_target *target,
struct iscsi_kern_param_info *info)
static void tgt_params_set(struct iscsi_tgt_params *params,
struct iscsi_kern_params_info *info)
{
struct iscsi_trgt_param *param = &target->trgt_param;
int32_t *iparam = info->target_param;
int32_t *iparams = info->target_params;
SET_PARAM(param, info, iparam, queued_cmnds);
SET_PARAM(params, info, iparams, queued_cmnds);
return;
}
/* target_mutex supposed to be locked */
static void trgt_param_get(struct iscsi_trgt_param *param,
struct iscsi_kern_param_info *info)
static void tgt_params_get(struct iscsi_tgt_params *params,
struct iscsi_kern_params_info *info)
{
int32_t *iparam = info->target_param;
int32_t *iparams = info->target_params;
GET_PARAM(param, info, iparam, queued_cmnds);
GET_PARAM(params, info, iparams, queued_cmnds);
return;
}
/* target_mutex supposed to be locked */
static int trgt_param(struct iscsi_target *target,
struct iscsi_kern_param_info *info, int set)
static int iscsi_tgt_params_set(struct iscsi_session *session,
struct iscsi_kern_params_info *info, int set)
{
struct iscsi_tgt_params *params = &session->tgt_params;
if (set) {
struct iscsi_trgt_param *prm;
trgt_param_check(info);
trgt_param_set(target, info);
prm = &target->trgt_param;
PRINT_INFO("Target parameter changed: QueuedCommands %d",
prm->queued_cmnds);
tgt_params_check(info);
tgt_params_set(params, info);
PRINT_INFO("Target parameters set for session %llx: "
"QueuedCommands %d", session->sid,
params->queued_cmnds);
} else
trgt_param_get(&target->trgt_param, info);
tgt_params_get(params, info);
return 0;
}
/* target_mutex supposed to be locked */
static int sess_param(struct iscsi_target *target,
struct iscsi_kern_param_info *info, int set)
static int iscsi_sess_params_set(struct iscsi_session *session,
struct iscsi_kern_params_info *info, int set)
{
struct iscsi_session *session = NULL;
struct iscsi_sess_param *param;
int err = -ENOENT;
struct iscsi_sess_params *params;
if (set)
sess_param_check(info);
sess_params_check(info);
if (info->sid) {
session = session_lookup(target, info->sid);
if (!session)
goto out;
if (set && !list_empty(&session->conn_list)) {
err = -EBUSY;
goto out;
}
param = &session->sess_param;
} else
param = &target->trgt_sess_param;
params = &session->sess_params;
if (set) {
sess_param_set(param, info);
if (session != NULL)
log_params(param);
sess_params_set(params, info);
log_params(params);
} else
sess_param_get(param, info);
sess_params_get(params, info);
err = 0;
out:
return err;
return 0;
}
/* target_mutex supposed to be locked */
int iscsi_param_set(struct iscsi_target *target,
struct iscsi_kern_param_info *info, int set)
int iscsi_params_set(struct iscsi_target *target,
struct iscsi_kern_params_info *info, int set)
{
int err;
struct iscsi_session *session;
if (info->param_type == key_session)
err = sess_param(target, info, set);
else if (info->param_type == key_target)
err = trgt_param(target, info, set);
if (info->sid == 0) {
PRINT_ERROR("sid must not be %d", 0);
err = -EINVAL;
goto out;
}
session = session_lookup(target, info->sid);
if (session == NULL) {
PRINT_ERROR("Session for sid %llx not found", info->sid);
err = -ENOENT;
goto out;
}
if (set && !list_empty(&session->conn_list)) {
err = -EBUSY;
goto out;
}
if (info->params_type == key_session)
err = iscsi_sess_params_set(session, info, set);
else if (info->params_type == key_target)
err = iscsi_tgt_params_set(session, info, set);
else
err = -EINVAL;
out:
return err;
}
+158 -61
View File
@@ -16,24 +16,6 @@
#include "iscsi.h"
#ifdef CONFIG_SCST_PROC
int print_digest_state(char *p, size_t size, unsigned long flags)
#else
static int print_digest_state(char *p, size_t size, unsigned long flags)
#endif
{
int pos;
if (DIGEST_NONE & flags)
pos = scnprintf(p, size, "%s", "none");
else if (DIGEST_CRC32C & flags)
pos = scnprintf(p, size, "%s", "crc32c");
else
pos = scnprintf(p, size, "%s", "unknown");
return pos;
}
/* target_mutex supposed to be locked */
struct iscsi_session *session_lookup(struct iscsi_target *target, u64 sid)
{
@@ -62,10 +44,7 @@ static int iscsi_session_alloc(struct iscsi_target *target,
session->target = target;
session->sid = info->sid;
session->sess_param = target->trgt_sess_param;
session->max_queued_cmnds = target->trgt_param.queued_cmnds;
atomic_set(&session->active_cmds, 0);
session->exp_cmd_sn = info->exp_cmd_sn;
session->initiator_name = kstrdup(info->initiator_name, GFP_KERNEL);
@@ -74,6 +53,7 @@ static int iscsi_session_alloc(struct iscsi_target *target,
goto err;
}
#ifdef CONFIG_SCST_PROC
name = kmalloc(strlen(info->user_name) + strlen(info->initiator_name) +
1, GFP_KERNEL);
if (name == NULL) {
@@ -85,6 +65,9 @@ static int iscsi_session_alloc(struct iscsi_target *target,
sprintf(name, "%s@%s", info->user_name, info->initiator_name);
else
sprintf(name, "%s", info->initiator_name);
#else
name = info->initiator_name;
#endif
INIT_LIST_HEAD(&session->conn_list);
INIT_LIST_HEAD(&session->pending_list);
@@ -105,7 +88,9 @@ static int iscsi_session_alloc(struct iscsi_target *target,
goto err;
}
#ifdef CONFIG_SCST_PROC
kfree(name);
#endif
scst_sess_set_tgt_priv(session->scst_sess, session);
@@ -119,7 +104,9 @@ err:
if (session) {
kfree(session->initiator_name);
kfree(session);
#ifdef CONFIG_SCST_PROC
kfree(name);
#endif
}
return err;
}
@@ -145,13 +132,14 @@ void sess_reinst_finished(struct iscsi_session *sess)
}
/* target_mgmt_mutex supposed to be locked */
int session_add(struct iscsi_target *target,
int __add_session(struct iscsi_target *target,
struct iscsi_kern_session_info *info)
{
struct iscsi_session *new_sess = NULL, *sess, *old_sess;
int err = 0;
int err = 0, i;
union iscsi_sid sid;
bool reinstatement = false;
struct iscsi_kern_params_info *params_info;
TRACE_MGMT_DBG("Adding session SID %llx", info->sid);
@@ -169,6 +157,14 @@ int session_add(struct iscsi_target *target,
goto out_err_unlock;
}
params_info = kmalloc(sizeof(*params_info), GFP_KERNEL);
if (params_info == NULL) {
PRINT_ERROR("Unable to allocate params info (size %d)",
sizeof(*params_info));
err = -ENOMEM;
goto out_err_unlock;
}
sid = *(union iscsi_sid *)&info->sid;
sid.id.tsih = 0;
old_sess = NULL;
@@ -194,6 +190,31 @@ int session_add(struct iscsi_target *target,
list_add_tail(&new_sess->session_list_entry, &target->session_list);
memset(params_info, 0, sizeof(*params_info));
params_info->tid = target->tid;
params_info->sid = info->sid;
params_info->params_type = key_session;
for (i = 0; i < session_key_last; i++)
params_info->session_params[i] = info->session_params[i];
err = iscsi_params_set(target, params_info, 1);
if (err != 0)
goto out_del;
memset(params_info, 0, sizeof(*params_info));
params_info->tid = target->tid;
params_info->sid = info->sid;
params_info->params_type = key_target;
for (i = 0; i < target_key_last; i++)
params_info->target_params[i] = info->target_params[i];
err = iscsi_params_set(target, params_info, 1);
if (err != 0)
goto out_del;
kfree(params_info);
params_info = NULL;
if (old_sess != NULL) {
reinstatement = true;
@@ -226,6 +247,10 @@ int session_add(struct iscsi_target *target,
out:
return err;
out_del:
list_del(&new_sess->session_list_entry);
kfree(params_info);
out_err_unlock:
mutex_unlock(&target->target_mutex);
@@ -312,7 +337,7 @@ int session_free(struct iscsi_session *session, bool del)
}
/* target_mutex supposed to be locked */
int session_del(struct iscsi_target *target, u64 sid)
int __del_session(struct iscsi_target *target, u64 sid)
{
struct iscsi_session *session;
@@ -368,6 +393,76 @@ const struct file_operations session_seq_fops = {
#else /* CONFIG_SCST_PROC */
#define ISCSI_SESS_BOOL_PARAM_ATTR(name, exported_name) \
static ssize_t iscsi_sess_show_##name(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf) \
{ \
int pos; \
struct scst_session *scst_sess; \
struct iscsi_session *sess; \
\
scst_sess = container_of(kobj, struct scst_session, sess_kobj); \
sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess); \
\
pos = sprintf(buf, "%s\n", \
iscsi_get_bool_value(sess->sess_params.name)); \
\
return pos; \
} \
\
static struct kobj_attribute iscsi_sess_attr_##name = \
__ATTR(exported_name, S_IRUGO, iscsi_sess_show_##name, NULL);
#define ISCSI_SESS_INT_PARAM_ATTR(name, exported_name) \
static ssize_t iscsi_sess_show_##name(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf) \
{ \
int pos; \
struct scst_session *scst_sess; \
struct iscsi_session *sess; \
\
scst_sess = container_of(kobj, struct scst_session, sess_kobj); \
sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess); \
\
pos = sprintf(buf, "%d\n", sess->sess_params.name); \
\
return pos; \
} \
\
static struct kobj_attribute iscsi_sess_attr_##name = \
__ATTR(exported_name, S_IRUGO, iscsi_sess_show_##name, NULL);
#define ISCSI_SESS_DIGEST_PARAM_ATTR(name, exported_name) \
static ssize_t iscsi_sess_show_##name(struct kobject *kobj, \
struct kobj_attribute *attr, char *buf) \
{ \
int pos; \
struct scst_session *scst_sess; \
struct iscsi_session *sess; \
char digest_name[64]; \
\
scst_sess = container_of(kobj, struct scst_session, sess_kobj); \
sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess); \
\
pos = sprintf(buf, "%s\n", iscsi_get_digest_name( \
sess->sess_params.name, digest_name)); \
\
return pos; \
} \
\
static struct kobj_attribute iscsi_sess_attr_##name = \
__ATTR(exported_name, S_IRUGO, iscsi_sess_show_##name, NULL);
ISCSI_SESS_BOOL_PARAM_ATTR(initial_r2t, InitialR2T);
ISCSI_SESS_BOOL_PARAM_ATTR(immediate_data, ImmediateData);
ISCSI_SESS_INT_PARAM_ATTR(max_recv_data_length, MaxRecvDataSegmentLength);
ISCSI_SESS_INT_PARAM_ATTR(max_xmit_data_length, MaxXmitDataSegmentLength);
ISCSI_SESS_INT_PARAM_ATTR(max_burst_length, MaxBurstLength);
ISCSI_SESS_INT_PARAM_ATTR(first_burst_length, FirstBurstLength);
ISCSI_SESS_INT_PARAM_ATTR(max_outstanding_r2t, MaxOutstandingR2T);
ISCSI_SESS_DIGEST_PARAM_ATTR(header_digest, HeaderDigest);
ISCSI_SESS_DIGEST_PARAM_ATTR(data_digest, DataDigest);
static ssize_t iscsi_sess_sid_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
@@ -386,7 +481,7 @@ static ssize_t iscsi_sess_sid_show(struct kobject *kobj,
return pos;
}
static struct kobj_attribute iscsi_sess_sid_attr =
static struct kobj_attribute iscsi_attr_sess_sid =
__ATTR(sid, S_IRUGO, iscsi_sess_sid_show, NULL);
static ssize_t iscsi_sess_reinstating_show(struct kobject *kobj,
@@ -407,58 +502,60 @@ static ssize_t iscsi_sess_reinstating_show(struct kobject *kobj,
return pos;
}
static struct kobj_attribute iscsi_sess_reinstating_attr =
static struct kobj_attribute iscsi_sess_attr_reinstating =
__ATTR(reinstating, S_IRUGO, iscsi_sess_reinstating_show, NULL);
static ssize_t iscsi_sess_hdigest_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
static ssize_t iscsi_sess_force_close_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
int pos;
int res;
struct scst_session *scst_sess;
struct iscsi_session *sess;
struct iscsi_conn *conn;
TRACE_ENTRY();
scst_sess = container_of(kobj, struct scst_session, sess_kobj);
sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
pos = print_digest_state(buf, SCST_SYSFS_BLOCK_SIZE,
sess->sess_param.header_digest);
if (mutex_lock_interruptible(&sess->target->target_mutex) != 0) {
res = -EINTR;
goto out;
}
TRACE_EXIT_RES(pos);
return pos;
PRINT_INFO("Deleting session %llu with initiator %s (%p)",
(long long unsigned int)sess->sid, sess->initiator_name, sess);
list_for_each_entry(conn, &sess->conn_list, conn_list_entry) {
TRACE_MGMT_DBG("Deleting connection with initiator %p", conn);
__mark_conn_closed(conn, ISCSI_CONN_ACTIVE_CLOSE|ISCSI_CONN_DELETING);
}
mutex_unlock(&sess->target->target_mutex);
res = count;
out:
TRACE_EXIT_RES(res);
return res;
}
static struct kobj_attribute iscsi_sess_hdigest_attr =
__ATTR(hdigest, S_IRUGO, iscsi_sess_hdigest_show, NULL);
static ssize_t iscsi_sess_ddigest_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int pos;
struct scst_session *scst_sess;
struct iscsi_session *sess;
TRACE_ENTRY();
scst_sess = container_of(kobj, struct scst_session, sess_kobj);
sess = (struct iscsi_session *)scst_sess_get_tgt_priv(scst_sess);
pos = print_digest_state(buf, SCST_SYSFS_BLOCK_SIZE,
sess->sess_param.data_digest);
TRACE_EXIT_RES(pos);
return pos;
}
static struct kobj_attribute iscsi_sess_ddigest_attr =
__ATTR(ddigest, S_IRUGO, iscsi_sess_ddigest_show, NULL);
static struct kobj_attribute iscsi_sess_attr_force_close =
__ATTR(force_close, S_IWUSR, NULL, iscsi_sess_force_close_store);
const struct attribute *iscsi_sess_attrs[] = {
&iscsi_sess_sid_attr.attr,
&iscsi_sess_reinstating_attr.attr,
&iscsi_sess_hdigest_attr.attr,
&iscsi_sess_ddigest_attr.attr,
&iscsi_sess_attr_initial_r2t.attr,
&iscsi_sess_attr_immediate_data.attr,
&iscsi_sess_attr_max_recv_data_length.attr,
&iscsi_sess_attr_max_xmit_data_length.attr,
&iscsi_sess_attr_max_burst_length.attr,
&iscsi_sess_attr_first_burst_length.attr,
&iscsi_sess_attr_max_outstanding_r2t.attr,
&iscsi_sess_attr_header_digest.attr,
&iscsi_sess_attr_data_digest.attr,
&iscsi_attr_sess_sid.attr,
&iscsi_sess_attr_reinstating.attr,
&iscsi_sess_attr_force_close.attr,
NULL,
};
+219 -178
View File
@@ -20,7 +20,6 @@
#include "digest.h"
#define MAX_NR_TARGETS (1UL << 30)
#define SYSFS_WAIT_TIMEOUT (15 * HZ)
DEFINE_MUTEX(target_mgmt_mutex);
@@ -42,7 +41,7 @@ struct iscsi_target *target_lookup_by_id(u32 id)
}
/* target_mgmt_mutex supposed to be locked */
static struct iscsi_target *target_lookup_by_name(char *name)
static struct iscsi_target *target_lookup_by_name(const char *name)
{
struct iscsi_target *target;
@@ -54,7 +53,8 @@ static struct iscsi_target *target_lookup_by_name(char *name)
}
/* target_mgmt_mutex supposed to be locked */
static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid)
static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid,
struct iscsi_target **out_target)
{
int err = -EINVAL, len;
char *name = info->name;
@@ -81,11 +81,13 @@ static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid)
target->tid = info->tid = tid;
strncpy(target->name, name, sizeof(target->name) - 1);
strlcpy(target->name, name, sizeof(target->name));
mutex_init(&target->target_mutex);
mutex_init(&target->target_sysfs_mutex);
INIT_LIST_HEAD(&target->session_list);
#ifndef CONFIG_SCST_PROC
INIT_LIST_HEAD(&target->attrs_list);
#endif
target->scst_tgt = scst_register(&iscsi_template, target->name);
if (!target->scst_tgt) {
@@ -98,6 +100,8 @@ static int iscsi_target_create(struct iscsi_kern_target_info *info, u32 tid)
list_add_tail(&target->target_list_entry, &target_list);
*out_target = target;
return 0;
out_free:
@@ -111,23 +115,51 @@ out:
}
/* target_mgmt_mutex supposed to be locked */
int target_add(struct iscsi_kern_target_info *info)
int __add_target(struct iscsi_kern_target_info *info)
{
int err = -EEXIST;
int err;
u32 tid = info->tid;
struct iscsi_target *target;
struct iscsi_kern_params_info *params_info;
struct iscsi_kern_attr *attr_info;
union add_info_union {
struct iscsi_kern_params_info params_info;
struct iscsi_kern_attr attr_info;
} *add_info;
#ifndef CONFIG_SCST_PROC
int i, rc;
unsigned long attrs_ptr_long;
struct iscsi_kern_attr __user *attrs_ptr;
#endif
if (nr_targets > MAX_NR_TARGETS) {
err = -EBUSY;
goto out;
}
if (target_lookup_by_name(info->name))
if (target_lookup_by_name(info->name)) {
PRINT_ERROR("Target %s already exist!", info->name);
err = -EEXIST;
goto out;
}
if (tid && target_lookup_by_id(tid))
if (tid && target_lookup_by_id(tid)) {
PRINT_ERROR("Target %u already exist!", tid);
err = -EEXIST;
goto out;
}
if (!tid) {
add_info = kmalloc(sizeof(*add_info), GFP_KERNEL);
if (add_info == NULL) {
PRINT_ERROR("Unable to allocate additional info (size %d)",
sizeof(*add_info));
err = -ENOMEM;
goto out;
}
params_info = (struct iscsi_kern_params_info *)add_info;
attr_info = (struct iscsi_kern_attr *)add_info;
if (tid == 0) {
do {
if (!++next_target_id)
++next_target_id;
@@ -136,116 +168,81 @@ int target_add(struct iscsi_kern_target_info *info)
tid = next_target_id;
}
err = iscsi_target_create(info, tid);
if (!err)
nr_targets++;
err = iscsi_target_create(info, tid, &target);
if (err != 0)
goto out_free;
nr_targets++;
#ifndef CONFIG_SCST_PROC
mutex_lock(&target->target_mutex);
attrs_ptr_long = info->attrs_ptr;
attrs_ptr = (struct iscsi_kern_attr __user *)attrs_ptr_long;
for (i = 0; i < info->attrs_num; i++) {
memset(attr_info, 0, sizeof(*attr_info));
rc = copy_from_user(attr_info, attrs_ptr, sizeof(*attr_info));
if (rc != 0) {
PRINT_ERROR("copy_from_user() of users of target %s "
"failed", info->name);
err = -EFAULT;
goto out_del_unlock;
}
attr_info->name[sizeof(attr_info->name)-1] = '\0';
err = iscsi_add_attr(target, attr_info);
if (err != 0)
goto out_del_unlock;
attrs_ptr++;
}
mutex_unlock(&target->target_mutex);
#endif
err = tid;
out_free:
kfree(add_info);
out:
return err;
#ifndef CONFIG_SCST_PROC
out_del_unlock:
mutex_unlock(&target->target_mutex);
__del_target(tid);
goto out_free;
#endif
}
static void target_destroy(struct iscsi_target *target)
{
#ifndef CONFIG_SCST_PROC
struct iscsi_attr *attr, *t;
#endif
TRACE_MGMT_DBG("Destroying target tid %u", target->tid);
#ifndef CONFIG_SCST_PROC
list_for_each_entry_safe(attr, t, &target->attrs_list,
attrs_list_entry) {
__iscsi_del_attr(target, attr);
}
#endif
scst_unregister(target->scst_tgt);
kfree(target);
module_put(THIS_MODULE);
return;
}
/* target_mgmt_mutex supposed to be locked */
int target_enable(struct iscsi_kern_target_info *info)
{
int res = 0;
struct iscsi_target *tgt;
TRACE_ENTRY();
tgt = target_lookup_by_id(info->tid);
if (tgt == NULL) {
PRINT_ERROR("Target %d not found", info->tid);
res = -EINVAL;
goto out;
}
mutex_lock(&tgt->target_sysfs_mutex);
if (tgt->expected_ioctl != ENABLE_TARGET) {
PRINT_ERROR("Unexpected ENABLE_TARGET IOCTL for target %d",
tgt->tid);
res = -EINVAL;
goto out_unlock;
}
tgt->expected_ioctl = 0;
WARN_ON(tgt->tgt_enabled);
tgt->tgt_enabled = 1;
tgt->ioctl_res = 0;
complete_all(tgt->target_enabling_cmpl);
out_unlock:
mutex_unlock(&tgt->target_sysfs_mutex);
out:
TRACE_EXIT_RES(res);
return res;
}
/* target_mgmt_mutex supposed to be locked */
int target_disable(struct iscsi_kern_target_info *info)
{
int res = 0;
struct iscsi_target *tgt;
TRACE_ENTRY();
tgt = target_lookup_by_id(info->tid);
if (tgt == NULL) {
PRINT_ERROR("Target %d not found", info->tid);
res = -EINVAL;
goto out;
}
mutex_lock(&tgt->target_sysfs_mutex);
if (tgt->expected_ioctl != DISABLE_TARGET) {
PRINT_ERROR("Unexpected DISABLE_TARGET IOCTL for target %d",
tgt->tid);
res = -EINVAL;
goto out_unlock;
}
mutex_unlock(&tgt->target_sysfs_mutex);
mutex_lock(&tgt->target_mutex);
target_del_all_sess(tgt, ISCSI_CONN_ACTIVE_CLOSE | ISCSI_CONN_DELETING);
mutex_unlock(&tgt->target_mutex);
mutex_lock(&tgt->target_sysfs_mutex);
tgt->expected_ioctl = 0;
WARN_ON(!tgt->tgt_enabled);
tgt->tgt_enabled = 0;
tgt->ioctl_res = 0;
complete_all(tgt->target_enabling_cmpl);
out_unlock:
mutex_unlock(&tgt->target_sysfs_mutex);
out:
TRACE_EXIT_RES(res);
return res;
}
/* target_mgmt_mutex supposed to be locked */
int target_del(u32 id)
int __del_target(u32 id)
{
struct iscsi_target *target;
int err;
@@ -296,7 +293,7 @@ void target_del_session(struct iscsi_target *target,
} else {
TRACE_MGMT_DBG("Freeing session %p without connections",
session);
session_del(target, session->sid);
__del_session(target, session->sid);
}
TRACE_EXIT();
@@ -454,107 +451,90 @@ static ssize_t iscsi_tgt_tid_show(struct kobject *kobj,
return pos;
}
static struct kobj_attribute iscsi_tgt_tid_attr =
static struct kobj_attribute iscsi_tgt_attr_tid =
__ATTR(tid, S_IRUGO, iscsi_tgt_tid_show, NULL);
const struct attribute *iscsi_tgt_attrs[] = {
&iscsi_tgt_tid_attr.attr,
&iscsi_tgt_attr_tid.attr,
NULL,
};
ssize_t iscsi_sysfs_send_event(uint32_t tid, enum iscsi_kern_event_code code,
const char *param1, const char *param2, void **data)
{
int res;
struct scst_sysfs_user_info *info;
TRACE_ENTRY();
if (ctr_open_state != ISCSI_CTR_OPEN_STATE_OPEN) {
PRINT_ERROR("%s", "User space process not connected");
res = -EPERM;
goto out;
}
res = scst_sysfs_user_add_info(&info);
if (res != 0)
goto out;
TRACE_DBG("Sending event %d (tid %d, param1 %s, param2 %s, cookie %d, "
"info %p)", tid, code, param1, param2, info->info_cookie, info);
res = event_send(tid, 0, 0, info->info_cookie, code, param1, param2);
if (res <= 0) {
PRINT_ERROR("event_send() failed: %d", res);
goto out_free;
}
/*
* It may wait 30 secs in blocking connect to an unreacheable
* iSNS server. It must be fixed, but not now. ToDo.
*/
res = scst_wait_info_completion(info, 31 * HZ);
if (data != NULL)
*data = info->data;
out_free:
scst_sysfs_user_del_info(info);
out:
TRACE_EXIT_RES(res);
return res;
}
ssize_t iscsi_enable_target(struct scst_tgt *scst_tgt, const char *buf,
size_t size)
{
struct iscsi_target *tgt =
(struct iscsi_target *)scst_tgt_get_tgt_priv(scst_tgt);
DECLARE_COMPLETION_ONSTACK(enabling_cmpl);
int res = 0, rc, ioctl_res = 0;
int res;
bool enable;
uint32_t type;
TRACE_ENTRY();
mutex_lock(&tgt->target_sysfs_mutex);
if (tgt->target_enabling_cmpl != NULL) {
TRACE_DBG("A sysfs command is being processed for target %d",
tgt->tid);
res = -ETXTBSY;
goto out_unlock;
}
tgt->target_enabling_cmpl = &enabling_cmpl;
switch (buf[0]) {
case '0':
if (!tgt->tgt_enabled) {
TRACE_DBG("Target %d already disabled", tgt->tid);
goto out_null_unlock;
}
enable = true;
tgt->expected_ioctl = DISABLE_TARGET;
res = event_send(tgt->tid, 0, 0, E_DISABLE_TARGET);
if (res <= 0) {
PRINT_ERROR("event_send() failed: %d", res);
goto out_null_unlock;
}
type = E_DISABLE_TARGET;
enable = false;
break;
case '1':
if (tgt->tgt_enabled) {
TRACE_DBG("Target %d already enabled", tgt->tid);
goto out_null_unlock;
}
enable = false;
tgt->expected_ioctl = ENABLE_TARGET;
res = event_send(tgt->tid, 0, 0, E_ENABLE_TARGET);
if (res <= 0) {
PRINT_ERROR("event_send() failed: %d", res);
goto out_null_unlock;
}
type = E_ENABLE_TARGET;
enable = true;
break;
default:
PRINT_ERROR("%s: Requested action not understood: %s",
__func__, buf);
res = -EINVAL;
goto out_null_unlock;
goto out;
}
mutex_unlock(&tgt->target_sysfs_mutex);
TRACE_DBG("%s target %d", enable ? "Enabling" : "Disabling", tgt->tid);
TRACE_DBG("Waiting for completion of enable/disable (%d) "
"target %d", enable, tgt->tid);
rc = wait_for_completion_interruptible_timeout(&enabling_cmpl,
SYSFS_WAIT_TIMEOUT);
if (res == 0) {
PRINT_ERROR("Timeout attempting to %s target %d",
enable ? "enable" : "disable", tgt->tid);
res = -EBUSY;
/* go through */
} else if (res < 0) {
if (res != -ERESTARTSYS)
PRINT_ERROR("wait_for_completion() failed: %d", res);
/* go through */
}
TRACE_DBG("Waiting for completion of enable/disable (%d) "
"target %d finished with res %d", enable, tgt->tid, res);
mutex_lock(&tgt->target_sysfs_mutex);
ioctl_res = tgt->ioctl_res;
out_null_unlock:
tgt->target_enabling_cmpl = NULL;
out_unlock:
mutex_unlock(&tgt->target_sysfs_mutex);
if (res == 0) {
res = ioctl_res;
if (res == 0)
res = size;
}
res = iscsi_sysfs_send_event(tgt->tid, type, NULL, NULL, NULL);
out:
TRACE_EXIT_RES(res);
return res;
}
@@ -567,4 +547,65 @@ 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)
{
int res;
TRACE_ENTRY();
res = iscsi_sysfs_send_event(0, E_ADD_TARGET, target_name,
params, NULL);
if (res > 0) {
/* It's tid */
res = 0;
}
TRACE_EXIT_RES(res);
return res;
}
ssize_t iscsi_sysfs_del_target(const char *target_name)
{
int res = 0, tid;
TRACE_ENTRY();
/* We don't want to have tgt visible after the mutex unlock */
{
struct iscsi_target *tgt;
mutex_lock(&target_mgmt_mutex);
tgt = target_lookup_by_name(target_name);
if (tgt == NULL) {
PRINT_ERROR("Target %s not found", target_name);
mutex_unlock(&target_mgmt_mutex);
res = -ENOENT;
goto out;
}
tid = tgt->tid;
mutex_unlock(&target_mgmt_mutex);
}
TRACE_DBG("Deleting target %s (tid %d)", target_name, tid);
res = iscsi_sysfs_send_event(tid, E_DEL_TARGET, NULL, NULL, NULL);
out:
TRACE_EXIT_RES(res);
return res;
}
ssize_t iscsi_sysfs_mgmt_cmd(const char *cmd)
{
int res;
TRACE_ENTRY();
TRACE_DBG("Sending mgmt cmd %s", cmd);
res = iscsi_sysfs_send_event(0, E_MGMT_CMD, cmd, NULL, NULL);
TRACE_EXIT_RES(res);
return res;
}
#endif /* CONFIG_SCST_PROC */
+4 -1
View File
@@ -21,9 +21,12 @@ OBJS_D = $(SRCS_D:.c=.o)
SRCS_ADM = iscsi_adm.c param.c
OBJS_ADM = $(SRCS_ADM:.c=.o)
SCST_INC_DIR := ../../scst/include
#SCST_INC_DIR := /usr/local/include/scst
CFLAGS += -O2 -fno-inline -Wall -Wextra -Wstrict-prototypes -Wno-sign-compare \
-Wimplicit-function-declaration -Wno-unused-parameter \
-Wno-missing-field-initializers -g -I../include
-Wno-missing-field-initializers -g -I../include -I$(SCST_INC_DIR)
CFLAGS += -D_GNU_SOURCE # required for glibc >= 2.8
PROGRAMS = iscsi-scstd iscsi-scst-adm
+13 -13
View File
@@ -307,7 +307,8 @@ static inline void chap_encode_string(u8 *intnum, int buf_len, char *encode_buf,
}
}
static inline void chap_calc_digest_md5(char chap_id, char *secret, int secret_len, u8 *challenge, int challenge_len, u8 *digest)
static inline void chap_calc_digest_md5(char chap_id, const char *secret, int secret_len,
const u8 *challenge, int challenge_len, u8 *digest)
{
MD5_CTX ctx;
@@ -318,7 +319,8 @@ static inline void chap_calc_digest_md5(char chap_id, char *secret, int secret_l
MD5_Final(digest, &ctx);
}
static inline void chap_calc_digest_sha1(char chap_id, char *secret, int secret_len, u8 *challenge, int challenge_len, u8 *digest)
static inline void chap_calc_digest_sha1(char chap_id, const char *secret, int secret_len,
const u8 *challenge, int challenge_len, u8 *digest)
{
SHA_CTX ctx;
@@ -389,8 +391,7 @@ static int chap_initiator_auth_check_response(struct connection *conn)
int digest_len = 0, retval = 0, encoding_format;
char pass[ISCSI_NAME_LEN];
memset(pass, 0, sizeof(pass));
if (config_account_query(conn->tid, AUTH_DIR_INCOMING, pass, pass) < 0) {
if (accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING)) {
log_warning("CHAP initiator auth.: "
"No CHAP credentials configured");
retval = CHAP_TARGET_ERROR;
@@ -408,7 +409,7 @@ static int chap_initiator_auth_check_response(struct connection *conn)
}
memset(pass, 0, sizeof(pass));
if (config_account_query(conn->tid, AUTH_DIR_INCOMING, value, pass) < 0) {
if (config_account_query(conn->tid, ISCSI_USER_DIR_INCOMING, value, pass) < 0) {
log_warning("CHAP initiator auth.: "
"No valid user/pass combination for initiator %s "
"found", conn->initiator);
@@ -493,19 +494,18 @@ static int chap_target_auth_create_response(struct connection *conn)
u8 *challenge = NULL, *digest = NULL;
int encoding_format, response_len;
int challenge_len = 0, digest_len = 0, retval = 0;
char pass[ISCSI_NAME_LEN], name[ISCSI_NAME_LEN];
struct iscsi_user *user;
if (!(value = text_key_find(conn, "CHAP_I"))) {
/* initiator doesn't want target auth!? */
/* Initiator doesn't want target auth!? */
conn->state = STATE_SECURITY_DONE;
retval = 0;
goto out;
}
chap_id = strtol(value, &value, 10);
memset(pass, 0, sizeof(pass));
memset(name, 0, sizeof(name));
if (config_account_query(conn->tid, AUTH_DIR_OUTGOING, name, pass) < 0) {
user = account_get_first(conn->tid, ISCSI_USER_DIR_OUTGOING);
if (user == NULL) {
log_warning("CHAP target auth.: "
"no outgoing credentials configured%s",
conn->tid ? "." : " for discovery.");
@@ -588,11 +588,11 @@ static int chap_target_auth_create_response(struct connection *conn)
switch (conn->auth.chap.digest_alg) {
case CHAP_DIGEST_ALG_MD5:
chap_calc_digest_md5(chap_id, pass, strlen(pass),
chap_calc_digest_md5(chap_id, user->password, strlen(user->password),
challenge, challenge_len, digest);
break;
case CHAP_DIGEST_ALG_SHA1:
chap_calc_digest_sha1(chap_id, pass, strlen(pass),
chap_calc_digest_sha1(chap_id, user->password, strlen(user->password),
challenge, challenge_len, digest);
break;
default:
@@ -602,7 +602,7 @@ static int chap_target_auth_create_response(struct connection *conn)
memset(response, 0x0, response_len);
chap_encode_string(digest, digest_len, response, encoding_format);
text_key_add(conn, "CHAP_N", name);
text_key_add(conn, "CHAP_N", user->name);
text_key_add(conn, "CHAP_R", response);
conn->state = STATE_SECURITY_DONE;
+670 -242
View File
File diff suppressed because it is too large Load Diff
+8 -2
View File
@@ -30,6 +30,8 @@
struct connection *conn_alloc(void)
{
struct connection *conn;
unsigned int session_params[session_key_last];
int i;
conn = malloc(sizeof(*conn));
if (conn == NULL)
@@ -37,16 +39,20 @@ struct connection *conn_alloc(void)
memset(conn, 0, sizeof(*conn));
conn->state = STATE_FREE;
param_set_defaults(conn->session_param, session_keys);
INIT_LIST_HEAD(&conn->rsp_buf_list);
params_set_defaults(session_params, session_keys);
for (i = 0; i < session_key_last; i++)
conn->session_params[i].val = session_params[i];
out:
return conn;
}
void conn_free(struct connection *conn)
{
remque(&conn->clist);
list_del(&conn->clist);
free(conn->initiator);
free(conn->user);
free(conn);
+219 -34
View File
@@ -17,6 +17,7 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -36,7 +37,7 @@ int kernel_open(int *max_data_seg_len)
int devn;
int ctlfd = -1;
int err;
struct iscsi_kern_register_info reg = { 0 };
struct iscsi_kern_register_info reg;
if (!(f = fopen("/proc/devices", "r"))) {
err = -errno;
@@ -80,17 +81,18 @@ int kernel_open(int *max_data_seg_len)
goto out_err;
}
memset(&reg, 0, sizeof(reg));
reg.version = (uintptr_t)ISCSI_SCST_INTERFACE_VERSION;
err = ioctl(ctlfd, REGISTER_USERD, &reg);
if (err < 0) {
if (err != 0) {
err = -errno;
log_error("Unable to register: %s. Incompatible version of the "
"kernel module?\n", strerror(errno));
goto out_close;
} else {
log_debug(0, "MAX_DATA_SEG_LEN %d", err);
*max_data_seg_len = err;
*max_data_seg_len = reg.max_data_seg_len;
}
out:
@@ -104,32 +106,98 @@ out_err:
goto out;
}
int kernel_target_create(u32 *tid, char *name)
int kernel_target_create(struct target *target, u32 *tid, u32 cookie)
{
int err;
int err, i, j;
struct iscsi_kern_target_info info;
struct iscsi_user *user;
struct iscsi_kern_attr *kern_attrs;
memset(&info, 0, sizeof(info));
strlcpy(info.name, target->name, sizeof(info.name));
info.tid = (tid != NULL) ? *tid : 0;
info.cookie = cookie;
for (j = 0; j < session_key_last; j++) {
if (session_keys[j].show_in_sysfs)
info.attrs_num++;;
}
for (j = 0; j < target_key_last; j++) {
if (target_keys[j].show_in_sysfs)
info.attrs_num++;;
}
list_for_each_entry(user, &target->target_in_accounts, ulist) {
info.attrs_num++;
}
list_for_each_entry(user, &target->target_out_accounts, ulist) {
info.attrs_num++;
}
kern_attrs = calloc(info.attrs_num, sizeof(*kern_attrs));
if (kern_attrs == NULL) {
err = -ENOMEM;
goto out;
}
info.attrs_ptr = (unsigned long)kern_attrs;
i = 0;
for (j = 0; j < session_key_last; j++) {
if (!session_keys[j].show_in_sysfs)
continue;
kern_attrs[i].mode = 0644;
strlcpy(kern_attrs[i].name, session_keys[j].name,
sizeof(kern_attrs[i].name));
i++;
}
for (j = 0; j < target_key_last; j++) {
if (!target_keys[j].show_in_sysfs)
continue;
kern_attrs[i].mode = 0644;
strlcpy(kern_attrs[i].name, target_keys[j].name,
sizeof(kern_attrs[i].name));
i++;
}
list_for_each_entry(user, &target->target_in_accounts, ulist) {
kern_attrs[i].mode = 0600;
strlcpy(kern_attrs[i].name, user->sysfs_name,
sizeof(kern_attrs[i].name));
i++;
}
list_for_each_entry(user, &target->target_out_accounts, ulist) {
kern_attrs[i].mode = 0600;
strlcpy(kern_attrs[i].name, user->sysfs_name,
sizeof(kern_attrs[i].name));
i++;
}
log_debug(1, "Adding target %s (attrs_num %d)", target->name,
info.attrs_num);
memcpy(info.name, name, sizeof(info.name) - 1);
info.tid = *tid;
if ((err = ioctl(ctrl_fd, ADD_TARGET, &info)) < 0) {
err = -errno;
log_error("Can't create target %u: %s\n", *tid,
log_error("Can't create target %s: %s\n", target->name,
strerror(errno));
} else
*tid = info.tid;
} else {
target->tid = err;
if (tid != NULL)
*tid = err;
err = 0;
}
free(kern_attrs);
out:
return err;
}
int kernel_target_destroy(u32 tid)
int kernel_target_destroy(u32 tid, u32 cookie)
{
struct iscsi_kern_target_info info;
int res;
memset(&info, 0, sizeof(info));
info.tid = tid;
info.cookie = cookie;
res = ioctl(ctrl_fd, DEL_TARGET, &info);
if (res < 0) {
@@ -140,6 +208,88 @@ int kernel_target_destroy(u32 tid)
return res;
}
#ifndef CONFIG_SCST_PROC
int kernel_attr_add(struct target *target, const char *name, u32 mode,
u32 cookie)
{
struct iscsi_kern_attr_info info;
int res;
memset(&info, 0, sizeof(info));
if (target != NULL)
info.tid = target->tid;
info.cookie = cookie;
info.attr.mode = mode;
strlcpy(info.attr.name, name, sizeof(info.attr.name));
res = ioctl(ctrl_fd, ISCSI_ATTR_ADD, &info);
if (res < 0)
res = -errno;
return res;
}
int kernel_attr_del(struct target *target, const char *name, u32 cookie)
{
struct iscsi_kern_attr_info info;
int res;
memset(&info, 0, sizeof(info));
if (target != NULL)
info.tid = target->tid;
info.cookie = cookie;
strlcpy(info.attr.name, name, sizeof(info.attr.name));
res = ioctl(ctrl_fd, ISCSI_ATTR_DEL, &info);
if (res < 0)
res = -errno;
return res;
}
static int __kernel_user_cmd(struct iscsi_user *user, u32 cookie, unsigned int cmd)
{
struct iscsi_kern_attr_info info;
int res;
memset(&info, 0, sizeof(info));
if (user->target != NULL)
info.tid = user->target->tid;
info.cookie = cookie;
info.attr.mode = 0600;
strlcpy(info.attr.name, user->sysfs_name, sizeof(info.attr.name));
res = ioctl(ctrl_fd, cmd, &info);
if (res < 0)
res = -errno;
return res;
}
int kernel_user_add(struct iscsi_user *user, u32 cookie)
{
int res;
res = __kernel_user_cmd(user, cookie, ISCSI_ATTR_ADD);
if (res != 0)
log_error("Can't add user %s (%d)\n", user->name, res);
return res;
}
int kernel_user_del(struct iscsi_user *user, u32 cookie)
{
int res;
res = __kernel_user_cmd(user, cookie, ISCSI_ATTR_DEL);
if (res != 0)
log_error("Can't del user %s (%d)\n", user->name, res);
return res;
}
#endif /* CONFIG_SCST_PROC */
int kernel_conn_destroy(u32 tid, u64 sid, u32 cid)
{
int err;
@@ -158,82 +308,117 @@ int kernel_conn_destroy(u32 tid, u64 sid, u32 cid)
return err;
}
int kernel_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param)
int kernel_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params)
{
int err, i;
struct iscsi_kern_param_info info;
struct iscsi_kern_params_info info;
if (sid == 0) {
log_error("kernel_params_get(): sid must be not %d", 0);
err = -EINVAL;
goto out;
}
memset(&info, 0, sizeof(info));
info.tid = tid;
info.sid = sid;
info.param_type = type;
info.params_type = type;
if ((err = ioctl(ctrl_fd, ISCSI_PARAM_GET, &info)) < 0) {
err = -errno;
log_debug(1, "Can't get session param for session 0x%" PRIu64
log_debug(1, "Can't get session params for session 0x%" PRIu64
" (tid %u, err %d): %s\n", sid, tid, err, strerror(errno));
}
if (type == key_session)
for (i = 0; i < session_key_last; i++)
param[i].val = info.session_param[i];
params[i].val = info.session_params[i];
else
for (i = 0; i < target_key_last; i++)
param[i].val = info.target_param[i];
params[i].val = info.target_params[i];
out:
return err;
}
int kernel_param_set(u32 tid, u64 sid, int type, u32 partial,
struct iscsi_param *param)
int kernel_params_set(u32 tid, u64 sid, int type, u32 partial,
const struct iscsi_param *params)
{
int i, err;
struct iscsi_kern_param_info info;
struct iscsi_kern_params_info info;
if (sid == 0) {
log_error("kernel_params_set(): sid must be not %d", 0);
err = -EINVAL;
goto out;
}
memset(&info, 0, sizeof(info));
info.tid = tid;
info.sid = sid;
info.param_type = type;
info.params_type = type;
info.partial = partial;
if (info.param_type == key_session)
if (info.params_type == key_session)
for (i = 0; i < session_key_last; i++)
info.session_param[i] = param[i].val;
info.session_params[i] = params[i].val;
else
for (i = 0; i < target_key_last; i++)
info.target_param[i] = param[i].val;
info.target_params[i] = params[i].val;
if ((err = ioctl(ctrl_fd, ISCSI_PARAM_SET, &info)) < 0) {
err = -errno;
log_error("Can't set session param for session 0x%" PRIu64
log_error("Can't set session params for session 0x%" PRIu64
" (tid %u, type %d, partial %d, err %d): %s\n", sid,
tid, type, partial, err, strerror(errno));
}
out:
return err;
}
int kernel_session_create(u32 tid, u64 sid, u32 exp_cmd_sn,
char *name, char *user)
int kernel_session_create(struct connection *conn)
{
struct iscsi_kern_session_info info;
int res;
int res, i;
struct target *target;
memset(&info, 0, sizeof(info));
info.tid = tid;
info.sid = sid;
info.exp_cmd_sn = exp_cmd_sn;
strncpy(info.initiator_name, name, sizeof(info.initiator_name) - 1);
strncpy(info.user_name, user, sizeof(info.user_name) - 1);
info.tid = conn->tid;
info.sid = conn->sess->sid.id64;
info.exp_cmd_sn = conn->exp_cmd_sn;
strlcpy(info.initiator_name, conn->sess->initiator, sizeof(info.initiator_name));
#ifdef CONFIG_SCST_PROC
if (conn->user != NULL)
strlcpy(info.user_name, conn->user, sizeof(info.user_name));
else
info.user_name[0] = '\0';
#endif
target = target_find_by_id(conn->tid);
if (target == NULL) {
log_error("Target %d not found", conn->tid);
res = -EINVAL;
goto out;
}
for (i = 0; i < session_key_last; i++)
info.session_params[i] = conn->session_params[i].val;
for (i = 0; i < target_key_last; i++)
info.target_params[i] = target->target_params[i];
res = ioctl(ctrl_fd, ADD_SESSION, &info);
if (res < 0) {
res = -errno;
log_error("Can't create sess 0x%" PRIu64 " (tid %d, "
"initiator %s): %s\n", sid, tid, name, strerror(errno));
"initiator %s): %s\n", conn->sess->sid.id64, conn->tid,
conn->sess->initiator, strerror(errno));
}
out:
return res;
}
+691 -43
View File
@@ -24,13 +24,18 @@
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <scst_const.h>
#include "iscsid.h"
#define ISCSI_ISNS_SYSFS_ACCESS_CONTROL_ENABLED "AccessControl"
static struct sockaddr_nl src_addr, dest_addr;
static int nl_write(int fd, void *data, int len)
@@ -63,6 +68,7 @@ static int nl_read(int fd, void *data, int len)
struct iovec iov[2];
struct msghdr msg;
struct nlmsghdr nlh;
int res;
iov[0].iov_base = &nlh;
iov[0].iov_len = sizeof(nlh);
@@ -75,14 +81,643 @@ static int nl_read(int fd, void *data, int len)
msg.msg_iov = iov;
msg.msg_iovlen = 2;
return recvmsg(fd, &msg, MSG_DONTWAIT);
res = recvmsg(fd, &msg, iscsi_enabled ? MSG_DONTWAIT : 0);
if (res > 0) {
res -= sizeof(nlh);
if (res < 0)
res = -EPIPE;
}
return res;
}
#ifndef CONFIG_SCST_PROC
static int send_mgmt_cmd_res(u32 tid, u32 cookie, u32 req_cmd, int result,
const char *res_str)
{
struct iscsi_kern_mgmt_cmd_res_info cinfo;
int res;
memset(&cinfo, 0, sizeof(cinfo));
cinfo.tid = tid;
cinfo.cookie = cookie;
cinfo.req_cmd = req_cmd;
cinfo.result = result;
if (res_str != NULL)
strlcpy(cinfo.value, res_str, sizeof(cinfo.value));
log_debug(1, "Sending result %d (cookie %d)", result, cookie);
res = ioctl(ctrl_fd, MGMT_CMD_CALLBACK, &cinfo);
if (res != 0) {
log_error("Can't send mgmt reply (cookie %d, result %d, "
"res %d): %s\n", cookie, result, res, strerror(errno));
}
return res;
}
static int handle_e_add_target(int fd, const struct iscsi_kern_event *event)
{
int res, rc;
char *buf;
int size, offs;
if (event->param1_size == 0) {
log_error("Incorrect E_ADD_TARGET: %s", "Target name expected");
res = -EINVAL;
goto out;
}
/* Params are not 0-terminated */
size = strlen("Target ") + event->param1_size + 2 + event->param2_size + 1;
buf = malloc(size);
if (buf == NULL) {
log_error("Unable to allocate tmp buffer (size %d)", size);
res = -ENOMEM;
goto out;
}
offs = sprintf(buf, "Target ");
while (1) {
if ((rc = nl_read(fd, &buf[offs], event->param1_size)) < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
log_error("read netlink fd (%d) failed: %s", fd,
strerror(errno));
send_mgmt_cmd_res(0, event->cookie, E_ADD_TARGET, -errno, NULL);
exit(1);
}
break;
}
offs += rc;
offs += sprintf(&buf[offs], "; ");
if (event->param2_size > 0) {
while (1) {
if ((rc = nl_read(fd, &buf[offs], event->param2_size)) < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
log_error("read netlink fd (%d) failed: %s", fd,
strerror(errno));
send_mgmt_cmd_res(0, event->cookie, E_ADD_TARGET, -errno, NULL);
exit(1);
}
break;
}
offs += rc;
}
buf[offs] = '\0';
log_debug(1, "Going to parse %s", buf);
res = config_parse_main(buf, event->cookie);
if (res != 0)
goto out_free;
out_free:
free(buf);
out:
return res;
}
static int handle_e_del_target(int fd, const struct iscsi_kern_event *event)
{
int res;
log_debug(2, "Going to delete target %d", event->tid);
res = target_del(event->tid, event->cookie);
return res;
}
static int handle_add_user(struct target *target, int dir, char *sysfs_name,
char *p, u32 cookie)
{
int res;
char *name, *pass;
name = config_sep_string(&p);
pass = config_sep_string(&p);
res = __config_account_add(target, dir, name, pass, sysfs_name, 1, cookie);
return res;
}
static int handle_add_param(struct target *target, char *p, u32 cookie)
{
int res, dir;
char *pp;
pp = config_sep_string(&p);
dir = params_index_by_name_numwild(pp, user_keys);
if (dir >= 0)
res = handle_add_user(target, dir, pp, p, cookie);
else {
log_error("Syntax error at %s", pp);
res = -EINVAL;
goto out;
}
out:
return res;
}
static int handle_del_user(struct target *target, int dir, char *p, u32 cookie)
{
int res;
char *name;
name = config_sep_string(&p);
res = config_account_del((target != NULL) ? target->tid : 0, dir,
name, cookie);
return res;
}
static int handle_del_param(struct target *target, char *p, u32 cookie)
{
int res, dir;
char *pp;
pp = config_sep_string(&p);
dir = params_index_by_name_numwild(pp, user_keys);
if (dir >= 0)
res = handle_del_user(target, dir, p, cookie);
else {
log_error("Syntax error at %s", pp);
res = -EINVAL;
goto out;
}
out:
return res;
}
static int handle_e_mgmt_cmd(int fd, const struct iscsi_kern_event *event)
{
int res, rc;
char *buf, *p, *pp;
int size;
if (event->param1_size == 0) {
log_error("Incorrect E_MGMT_CMD: %s", "command expected");
res = -EINVAL;
goto out;
}
/* Params are not 0-terminated */
size = event->param1_size + 1;
buf = malloc(size);
if (buf == NULL) {
log_error("Unable to allocate tmp buffer (size %d)", size);
res = -ENOMEM;
goto out;
}
while (1) {
if ((rc = nl_read(fd, buf, event->param1_size)) < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
log_error("read netlink fd (%d) failed: %s", fd,
strerror(errno));
send_mgmt_cmd_res(0, event->cookie, E_MGMT_CMD, -errno, NULL);
exit(1);
}
break;
}
buf[rc] = '\0';
log_debug(1, "Going to parse %s", buf);
p = buf;
pp = config_sep_string(&p);
if (strcasecmp("add_attribute", pp) == 0) {
res = handle_add_param(NULL, p, event->cookie);
} else if (strcasecmp("add_target_attribute", pp) == 0) {
struct target *target;
pp = config_sep_string(&p);
target = target_find_by_name(pp);
if (target == NULL) {
log_error("Target %s not found", pp);
res = -ENOENT;
goto out_free;
}
res = handle_add_param(target, p, event->cookie);
} else if (strcasecmp("del_attribute", pp) == 0) {
res = handle_del_param(NULL, p, event->cookie);
} else if (strcasecmp("del_target_parameter", pp) == 0) {
struct target *target;
pp = config_sep_string(&p);
target = target_find_by_name(pp);
if (target == NULL) {
log_error("Target %s not found", pp);
res = -ENOENT;
goto out_free;
}
res = handle_del_param(target, p, event->cookie);
} else {
log_error("Syntax error at %s", p);
res = -EINVAL;
}
out_free:
free(buf);
out:
return res;
}
static void add_key_mark(char *res_str, int res_str_len, int new_line)
{
int offs = strlen(res_str);
snprintf(&res_str[offs], res_str_len - offs, "%s%s\n",
new_line ? "\n" : "", SCST_SYSFS_KEY_MARK);
return;
}
static int handle_e_get_attr_value(int fd, const struct iscsi_kern_event *event)
{
int res = 0, rc, idx;
char *buf, *p, *pp;
int size;
struct target *target;
char res_str[ISCSI_MAX_ATTR_VALUE_LEN];
memset(res_str, 0, sizeof(res_str));
if (event->param1_size == 0) {
log_error("Incorrect E_GET_ATTR_VALUE: %s", "attr name expected");
res = -EINVAL;
goto out;
}
/* Params are not 0-terminated */
size = event->param1_size + 1;
buf = malloc(size);
if (buf == NULL) {
log_error("Unable to allocate tmp buffer (size %d)", size);
res = -ENOMEM;
goto out;
}
while (1) {
if ((rc = nl_read(fd, buf, event->param1_size)) < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
log_error("read netlink fd (%d) failed: %s", fd,
strerror(errno));
send_mgmt_cmd_res(0, event->cookie, E_GET_ATTR_VALUE, -errno, NULL);
exit(1);
}
break;
}
buf[rc] = '\0';
log_debug(1, "Going to parse name %s", buf);
target = target_find_by_id(event->tid);
p = buf;
pp = config_sep_string(&p);
if (!((idx = params_index_by_name(pp, target_keys)) < 0)) {
if (target == NULL) {
log_error("Target expected for attr %s", pp);
res = -EINVAL;
goto out_free;
}
params_val_to_str(target_keys, idx, target->target_params[idx],
res_str, sizeof(res_str));
if (target->target_params[idx] != target_keys[idx].local_def)
add_key_mark(res_str, sizeof(res_str), 1);
} else if (!((idx = params_index_by_name(pp, session_keys)) < 0)) {
if (target == NULL) {
log_error("Target expected for attr %s", pp);
res = -EINVAL;
goto out_free;
}
params_val_to_str(session_keys, idx, target->session_params[idx],
res_str, sizeof(res_str));
if (target->session_params[idx] != session_keys[idx].local_def)
add_key_mark(res_str, sizeof(res_str), 1);
} else if (!((idx = params_index_by_name_numwild(pp, user_keys)) < 0)) {
struct iscsi_user *user;
user = account_lookup_by_sysfs_name(target, idx, pp);
if (user == NULL) {
log_error("Unknown user attribute %s", pp);
res = -EINVAL;
goto out_free;
}
snprintf(res_str, sizeof(res_str), "%s %s\n", user->name,
user->password);
add_key_mark(res_str, sizeof(res_str), 0);
} else if (strcasecmp(ISCSI_ENABLED_ATTR_NAME, pp) == 0) {
if (target != NULL) {
log_error("Not NULL target %s for global attribute %s",
target->name, pp);
res = -EINVAL;
goto out_free;
}
snprintf(res_str, sizeof(res_str), "%d\n", iscsi_enabled);
} else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) {
if (target != NULL) {
log_error("Not NULL target %s for global attribute %s",
target->name, pp);
res = -EINVAL;
goto out_free;
}
if (isns_server != NULL) {
snprintf(res_str, sizeof(res_str), "%s %s\n", isns_server,
isns_access_control ? ISCSI_ISNS_SYSFS_ACCESS_CONTROL_ENABLED : "");
add_key_mark(res_str, sizeof(res_str), 0);
} else
snprintf(res_str, sizeof(res_str), "%s\n", "");
} else {
log_error("Unknown attribute %s", pp);
res = -EINVAL;
goto out_free;
}
send_mgmt_cmd_res(event->tid, event->cookie, E_GET_ATTR_VALUE, 0, res_str);
out_free:
free(buf);
out:
return res;
}
static int handle_e_set_attr_value(int fd, const struct iscsi_kern_event *event)
{
int res = 0, rc, idx;
char *buf, *p, *pp, *n;
struct target *target;
int size, offs;
u32 val;
if (event->param1_size == 0) {
log_error("Incorrect E_SET_ATTR_VALUE: %s", "attr name expected");
res = -EINVAL;
goto out;
}
if (event->param2_size == 0) {
log_error("Incorrect E_SET_ATTR_VALUE: %s", "attr value expected");
res = -EINVAL;
goto out;
}
/* Params are not 0-terminated */
size = event->param1_size + 1 + 1 + event->param2_size + 1;
buf = malloc(size);
if (buf == NULL) {
log_error("Unable to allocate tmp buffer (size %d)", size);
res = -ENOMEM;
goto out;
}
while (1) {
if ((rc = nl_read(fd, buf, event->param1_size)) < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
log_error("read netlink fd (%d) failed: %s", fd,
strerror(errno));
send_mgmt_cmd_res(0, event->cookie, E_SET_ATTR_VALUE, -errno, NULL);
exit(1);
}
break;
}
offs = rc;
offs += sprintf(&buf[offs], " ");
while (1) {
if ((rc = nl_read(fd, &buf[offs], event->param2_size)) < 0) {
if ((errno == EINTR) || (errno == EAGAIN))
continue;
log_error("read netlink fd (%d) failed: %s", fd,
strerror(errno));
send_mgmt_cmd_res(0, event->cookie, E_SET_ATTR_VALUE, -errno, NULL);
exit(1);
}
break;
}
offs += rc;
buf[offs] = '\0';
log_debug(1, "Going to parse %s", buf);
target = target_find_by_id(event->tid);
p = buf;
pp = config_sep_string(&p);
if (!((idx = params_index_by_name(pp, target_keys)) < 0)) {
if (target == NULL) {
log_error("Target expected for attr %s", pp);
res = -EINVAL;
goto out_free;
}
pp = config_sep_string(&p);
n = config_sep_string(&p);
if (*n != '\0') {
log_error("Unexpected parameter value %s\n", n);
res = -EINVAL;
goto out_free;
}
res = params_str_to_val(target_keys, idx, pp, &val);
if (res < 0) {
log_error("Wrong value %s for parameter %s\n",
pp, target_keys[idx].name);
goto out_free;
}
res = params_check_val(target_keys, idx, &val);
if (res < 0) {
log_error("Wrong value %u for parameter %s\n",
val, target_keys[idx].name);
goto out_free;
}
target->target_params[idx] = val;
} else if (!((idx = params_index_by_name(pp, session_keys)) < 0)) {
if (target == NULL) {
log_error("Target expected for attr %s", pp);
res = -EINVAL;
goto out_free;
}
pp = config_sep_string(&p);
n = config_sep_string(&p);
if (*n != '\0') {
log_error("Unexpected parameter value %s\n", n);
res = -EINVAL;
goto out_free;
}
res = params_str_to_val(session_keys, idx, pp, &val);
if (res < 0) {
log_error("Wrong value %s for parameter %s\n",
pp, session_keys[idx].name);
goto out_free;
}
res = params_check_val(session_keys, idx, &val);
if (res < 0) {
log_error("Wrong value %u for parameter %s\n",
val, session_keys[idx].name);
goto out_free;
}
target->session_params[idx] = val;
} else if (!((idx = params_index_by_name_numwild(pp, user_keys)) < 0)) {
struct iscsi_user *user;
user = account_lookup_by_sysfs_name(target, idx, pp);
if (user == NULL) {
log_error("Unknown user attribute %s", pp);
res = -EINVAL;
goto out_free;
}
res = account_replace(target, idx, pp, p);
if (res != 0)
goto out_free;
} else if (strcasecmp(ISCSI_ENABLED_ATTR_NAME, pp) == 0) {
if (target != NULL) {
log_error("Not NULL target %s for global attribute %s",
target->name, pp);
res = -EINVAL;
goto out_free;
}
pp = config_sep_string(&p);
if (strcmp(pp, "1") == 0)
iscsi_enabled = 1;
else if (strcmp(pp, "0") == 0)
iscsi_enabled = 0;
else {
log_error("Unknown value %s", pp);
res = -EINVAL;
goto out_free;
}
} else if (strcasecmp(ISCSI_ISNS_SERVER_PARAM_NAME, pp) == 0) {
if (target != NULL) {
log_error("Not NULL target %s for global attribute %s",
target->name, pp);
res = -EINVAL;
goto out_free;
}
if (isns_server != NULL)
isns_exit();
pp = config_sep_string(&p);
if (*pp == '\0') {
goto done;
}
isns_access_control = 0;
isns_server = strdup(pp);
if (isns_server == NULL) {
log_error("Unable to dublicate iSNS server name %s", pp);
res = -ENOMEM;
goto out_free;
}
pp = config_sep_string(&p);
if (strcasecmp(ISCSI_ISNS_SYSFS_ACCESS_CONTROL_ENABLED, pp) == 0) {
pp = config_sep_string(&p);
if (strcasecmp(pp, "No") == 0)
isns_access_control = 0;
else
isns_access_control = 1;
} else if (*pp != '\0') {
log_error("Unknown parameter %s", pp);
res = -EINVAL;
goto out_free_server;
}
res = isns_init();
if (res == 0) {
struct target *t;
int rc;
list_for_each_entry(t, &targets_list, tlist) {
rc = isns_target_register(t->name);
if (rc < 0) {
/*
* iSNS server can be temporary not
* available.
*/
goto out_free_isns_exit;
}
}
} else
goto out_free_server;
} else {
log_error("Unknown attribute %s", pp);
res = -EINVAL;
goto out_free;
}
done:
send_mgmt_cmd_res(event->tid, event->cookie, E_SET_ATTR_VALUE, 0, NULL);
out_free:
free(buf);
out:
return res;
out_free_isns_exit:
isns_exit();
out_free_server:
free(isns_server);
isns_server = NULL;
goto out;
}
#endif /* CONFIG_SCST_PROC */
void handle_iscsi_events(int fd)
{
struct session *session;
struct connection *conn;
struct iscsi_kern_event event;
#ifndef CONFIG_SCST_PROC
struct target *target;
#endif
int rc;
retry:
@@ -91,63 +726,77 @@ retry:
return;
if (errno == EINTR)
goto retry;
log_error("read netlink fd (%d)", errno);
log_error("read netlink fd (%d) failed: %s", fd, strerror(errno));
exit(1);
}
log_debug(1, "conn %u session %#" PRIx64 " target %u, code %u",
event.cid, event.sid, event.tid, event.code);
log_debug(1, "target %u, session %#" PRIx64 ", conn %u, code %u, cookie %d",
event.tid, event.sid, event.cid, event.code, event.cookie);
/*
* Let's always report errors through send_mgmt_cmd_res(). If the error
* was returned by the corresponding ioctl(), it will lead to blank
* MGMT_CMD_CALLBACK ioctl()'s, but that's OK, because kernel will
* not reuse the cookie. Better to have extra return call, than no call
* at all.
*/
switch (event.code) {
case E_ENABLE_TARGET:
{
struct target *target;
struct iscsi_kern_target_info info;
#ifndef CONFIG_SCST_PROC
case E_ADD_TARGET:
rc = handle_e_add_target(fd, &event);
if (rc != 0)
send_mgmt_cmd_res(event.tid, event.cookie, E_ADD_TARGET, rc, NULL);
break;
case E_DEL_TARGET:
rc = handle_e_del_target(fd, &event);
if (rc != 0)
send_mgmt_cmd_res(event.tid, event.cookie, E_DEL_TARGET, rc, NULL);
break;
case E_MGMT_CMD:
rc = handle_e_mgmt_cmd(fd, &event);
if (rc != 0)
send_mgmt_cmd_res(event.tid, event.cookie, E_MGMT_CMD, rc, NULL);
break;
case E_ENABLE_TARGET:
target = target_find_by_id(event.tid);
if (target == NULL) {
log_error("Target %d not found", event.tid);
goto out;
}
target->tgt_enabled = 1;
memset(&info, 0, sizeof(info));
info.tid = event.tid;
rc = ioctl(ctrl_fd, ENABLE_TARGET, &info);
if (rc < 0) {
log_error("Can't enable target %u: %s\n", event.tid,
strerror(errno));
goto out;
}
rc = -ENOENT;
} else
rc = 0;
rc |= send_mgmt_cmd_res(event.tid, event.cookie, E_ENABLE_TARGET, rc, NULL);
if (rc == 0)
target->tgt_enabled = 1;
break;
}
case E_DISABLE_TARGET:
{
struct target *target;
struct iscsi_kern_target_info info;
target = target_find_by_id(event.tid);
if (target == NULL) {
log_error("Target %d not found", event.tid);
goto out;
}
target->tgt_enabled = 0;
memset(&info, 0, sizeof(info));
info.tid = event.tid;
rc = ioctl(ctrl_fd, DISABLE_TARGET, &info);
if (rc < 0) {
log_error("Can't disable target %u: %s\n", event.tid,
strerror(errno));
goto out;
}
rc = -ENOENT;
} else
rc = 0;
rc |= send_mgmt_cmd_res(event.tid, event.cookie, E_DISABLE_TARGET, rc, NULL);
if (rc == 0)
target->tgt_enabled = 0;
break;
}
case E_GET_ATTR_VALUE:
rc = handle_e_get_attr_value(fd, &event);
if (rc != 0)
send_mgmt_cmd_res(event.tid, event.cookie, E_GET_ATTR_VALUE, rc, NULL);
break;
case E_SET_ATTR_VALUE:
rc = handle_e_set_attr_value(fd, &event);
if (rc != 0)
send_mgmt_cmd_res(event.tid, event.cookie, E_SET_ATTR_VALUE, rc, NULL);
break;
#endif /* CONFIG_SCST_PROC */
case E_CONN_CLOSE:
session = session_find_id(event.tid, event.sid);
@@ -175,7 +824,6 @@ retry:
break;
}
out:
return;
}
+27 -30
View File
@@ -245,9 +245,9 @@ out_close:
return err;
}
static void show_iscsi_param(int type, struct iscsi_param *param)
static void show_iscsi_params(int type, struct iscsi_param *param)
{
int i, nr;
int i, nr, len;
char buf[1024], *p;
struct iscsi_key *keys;
@@ -261,10 +261,11 @@ static void show_iscsi_param(int type, struct iscsi_param *param)
for (i = 0; i < nr; i++) {
memset(buf, 0, sizeof(buf));
strcpy(buf, keys[i].name);
p = buf + strlen(buf);
strlcpy(buf, keys[i].name, sizeof(buf));
len = strlen(buf);
p = buf + len;
*p++ = '=';
param_val_to_str(keys, i, param[i].val, p);
params_val_to_str(keys, i, param[i].val, p, sizeof(buf) - (len + 1));
printf("%s\n", buf);
}
}
@@ -282,31 +283,31 @@ static int parse_trgt_params(struct msg_trgt *msg, char *params)
continue;
*q++ = '\0';
if (!((idx = param_index_by_name(p, target_keys)) < 0)) {
if (param_str_to_val(target_keys, idx, q, &val)) {
if (!((idx = params_index_by_name(p, target_keys)) < 0)) {
if (params_str_to_val(target_keys, idx, q, &val)) {
fprintf(stderr,
"Invalid %s value \"%s\".\n",
target_keys[idx].name, q);
return -EINVAL;
}
if (!param_check_val(target_keys, idx, &val))
if (!params_check_val(target_keys, idx, &val))
msg->target_partial |= (1 << idx);
msg->target_param[idx].val = val;
msg->target_params[idx].val = val;
msg->type |= 1 << key_target;
continue;
}
if (!((idx = param_index_by_name(p, session_keys)) < 0)) {
if (param_str_to_val(session_keys, idx, q, &val)) {
if (!((idx = params_index_by_name(p, session_keys)) < 0)) {
if (params_str_to_val(session_keys, idx, q, &val)) {
fprintf(stderr,
"Invalid %s value \"%s\".\n",
session_keys[idx].name, q);
return -EINVAL;
}
if (!param_check_val(session_keys, idx, &val))
if (!params_check_val(session_keys, idx, &val))
msg->session_partial |= (1 << idx);
msg->session_param[idx].val = val;
msg->session_params[idx].val = val;
msg->type |= 1 << key_session;
continue;
@@ -346,7 +347,7 @@ static int trgt_handle(int op, u32 set, u32 tid, char *params)
goto out;
}
req.rcmnd = C_TRGT_NEW;
strncpy(req.u.trgt.name, p, sizeof(req.u.trgt.name) - 1);
strlcpy(req.u.trgt.name, p, sizeof(req.u.trgt.name));
break;
}
case OP_DELETE:
@@ -364,7 +365,7 @@ static int trgt_handle(int op, u32 set, u32 tid, char *params)
err = iscsid_request(&req, NULL, 0);
if (!err && req.rcmnd == C_TRGT_SHOW)
show_iscsi_param(key_target, req.u.trgt.target_param);
show_iscsi_params(key_target, req.u.trgt.target_params);
out:
return err;
@@ -395,7 +396,7 @@ static int sess_handle(int op, u32 set, u32 tid, u64 sid, char *params)
req.rcmnd = C_SESS_SHOW;
err = iscsid_request(&req, NULL, 0);
if (!err)
show_iscsi_param(key_session, req.u.trgt.session_param);
show_iscsi_params(key_session, req.u.trgt.session_params);
break;
}
@@ -424,14 +425,14 @@ static int parse_user_params(char *params, u32 *auth_dir, char **user,
"Already specified IncomingUser %s\n",
q);
*user = q;
*auth_dir = AUTH_DIR_INCOMING;
*auth_dir = ISCSI_USER_DIR_INCOMING;
} else if (!strcasecmp(p, "OutgoingUser")) {
if (*user)
fprintf(stderr,
"Already specified OutgoingUser %s\n",
q);
*user = q;
*auth_dir = AUTH_DIR_OUTGOING;
*auth_dir = ISCSI_USER_DIR_OUTGOING;
} else if (!strcasecmp(p, "Password")) {
if (*pass)
fprintf(stderr,
@@ -453,7 +454,7 @@ static void show_account(int auth_dir, char *user, char *pass)
if (pass)
snprintf(buf + strlen(buf), ISCSI_NAME_LEN, " %s", pass);
printf("%sUser %s\n", (auth_dir == AUTH_DIR_INCOMING) ?
printf("%sUser %s\n", (auth_dir == ISCSI_USER_DIR_INCOMING) ?
"Incoming" : "Outgoing", buf);
}
@@ -462,8 +463,7 @@ static int user_handle_show_user(struct iscsi_adm_req *req, char *user)
int err;
req->rcmnd = C_ACCT_SHOW;
strncpy(req->u.acnt.u.user.name, user,
sizeof(req->u.acnt.u.user.name) - 1);
strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
err = iscsid_request(req, NULL, 0);
if (!err)
@@ -479,7 +479,7 @@ static int user_handle_show_list(struct iscsi_adm_req *req)
size_t buf_sz = 0;
char *buf;
req->u.acnt.auth_dir = AUTH_DIR_INCOMING;
req->u.acnt.auth_dir = ISCSI_USER_DIR_INCOMING;
req->rcmnd = C_ACCT_LIST;
do {
@@ -513,8 +513,8 @@ static int user_handle_show_list(struct iscsi_adm_req *req)
show_account(req->u.acnt.auth_dir,
&buf[i * ISCSI_NAME_LEN], NULL);
if (req->u.acnt.auth_dir == AUTH_DIR_INCOMING) {
req->u.acnt.auth_dir = AUTH_DIR_OUTGOING;
if (req->u.acnt.auth_dir == ISCSI_USER_DIR_INCOMING) {
req->u.acnt.auth_dir = ISCSI_USER_DIR_OUTGOING;
buf_sz = 0;
retry = 1;
}
@@ -546,10 +546,8 @@ static int user_handle_new(struct iscsi_adm_req *req, char *user, char *pass)
req->rcmnd = C_ACCT_NEW;
strncpy(req->u.acnt.u.user.name, user,
sizeof(req->u.acnt.u.user.name) - 1);
strncpy(req->u.acnt.u.user.pass, pass,
sizeof(req->u.acnt.u.user.pass) - 1);
strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
strlcpy(req->u.acnt.u.user.pass, pass, sizeof(req->u.acnt.u.user.pass));
return iscsid_request(req, NULL, 0);
}
@@ -566,8 +564,7 @@ static int user_handle_del(struct iscsi_adm_req *req, char *user, char *pass)
req->rcmnd = C_ACCT_DEL;
strncpy(req->u.acnt.u.user.name, user,
sizeof(req->u.acnt.u.user.name) - 1);
strlcpy(req->u.acnt.u.user.name, user, sizeof(req->u.acnt.u.user.name));
return iscsid_request(req, NULL, 0);
}
+2 -2
View File
@@ -25,8 +25,8 @@ struct msg_trgt {
u32 type;
u32 session_partial;
u32 target_partial;
struct iscsi_param session_param[session_key_last];
struct iscsi_param target_param[target_key_last];
struct iscsi_param session_params[session_key_last];
struct iscsi_param target_params[target_key_last];
};
struct msg_acnt {
+30 -14
View File
@@ -465,7 +465,7 @@ out:
return;
}
static void event_loop(int timeout)
static void event_loop(void)
{
int res, i;
@@ -491,9 +491,13 @@ static void event_loop(int timeout)
close(init_report_pipe[1]);
while (1) {
res = poll(poll_array, POLL_MAX, timeout);
if (!iscsi_enabled) {
handle_iscsi_events(nl_fd);
continue;
}
res = poll(poll_array, POLL_MAX, isns_timeout);
if (res == 0) {
isns_handle(1, &timeout);
isns_handle(1);
continue;
} else if (res < 0) {
if (errno == EINTR)
@@ -531,7 +535,7 @@ static void event_loop(int timeout)
iscsi_adm_request_handle(ipc_fd);
if (poll_array[POLL_ISNS].revents)
isns_handle(0, &timeout);
isns_handle(0);
if (poll_array[POLL_SCN_LISTEN].revents)
isns_scn_handle(1);
@@ -562,7 +566,7 @@ static void event_loop(int timeout)
}
}
void init_max_data_seg_len(int max_data_seg_len)
static void init_max_data_seg_len(int max_data_seg_len)
{
if ((session_keys[key_max_recv_data_length].local_def != -1) ||
(session_keys[key_max_recv_data_length].max != -1) ||
@@ -597,13 +601,16 @@ void init_max_data_seg_len(int max_data_seg_len)
int main(int argc, char **argv)
{
int ch, longindex, timeout = -1;
int ch, longindex;
char *config = NULL;
uid_t uid = 0;
gid_t gid = 0;
char *isns = NULL;
int isns_ac = 0;
int max_data_seg_len = -1;
int err;
#ifdef CONFIG_SCST_PROC
iscsi_enabled = 1;
#endif
if (pipe(init_report_pipe) == -1) {
perror("pipe failed");
@@ -666,6 +673,18 @@ int main(int argc, char **argv)
init_max_data_seg_len(max_data_seg_len);
#ifndef CONFIG_SCST_PROC
err = kernel_attr_add(NULL, ISCSI_ISNS_SERVER_PARAM_NAME,
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0);
if (err != 0)
exit(err);
err = kernel_attr_add(NULL, ISCSI_ENABLED_ATTR_NAME,
S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR, 0);
if (err != 0)
exit(err);
#endif
if ((ipc_fd = iscsi_adm_request_listen()) < 0) {
perror("ipc failed\n");
exit(-1);
@@ -722,11 +741,8 @@ int main(int argc, char **argv)
setsid();
}
config_isns_load(config, &isns, &isns_ac);
if (isns)
timeout = isns_init(isns, isns_ac);
if (config_load(config) != 0)
err = config_load(config);
if (err != 0)
exit(1);
if (gid && setgid(gid) < 0)
@@ -735,7 +751,7 @@ int main(int argc, char **argv)
if (uid && setuid(uid) < 0)
perror("setuid failed");
event_loop(timeout);
event_loop();
return 0;
}
+56 -49
View File
@@ -26,6 +26,8 @@
#include "iscsid.h"
int iscsi_enabled;
static u32 ttt;
static u32 get_next_ttt(struct connection *conn __attribute__((unused)))
@@ -39,7 +41,7 @@ static struct iscsi_key login_keys[] = {
{"InitiatorAlias",},
{"SessionType",},
{"TargetName",},
{NULL, 0, 0, 0, 0, NULL},
{NULL,},
};
char *text_key_find(struct connection *conn, char *searchKey)
@@ -67,7 +69,7 @@ char *text_key_find(struct connection *conn, char *searchKey)
datasize--;
if (keylen == value - key - 1
&& !strncmp(key, searchKey, keylen))
&& !strncasecmp(key, searchKey, keylen))
return value;
}
}
@@ -114,7 +116,7 @@ static struct buf_segment *conn_alloc_buf_segment(struct connection *conn,
return seg;
}
void text_key_add(struct connection *conn, char *key, char *value)
void text_key_add(struct connection *conn, char *key, const char *value)
{
struct buf_segment *seg;
int keylen = strlen(key);
@@ -126,7 +128,7 @@ void text_key_add(struct connection *conn, char *key, char *value)
size_t data_sz;
data_sz = (conn->state == STATE_FULL) ?
conn->session_param[key_max_xmit_data_length].val :
conn->session_params[key_max_xmit_data_length].val :
INCOMING_BUFSIZE;
seg = list_empty(&conn->rsp_buf_list) ? NULL :
@@ -190,14 +192,6 @@ static void text_key_add_reject(struct connection *conn, char *key)
text_key_add(conn, key, "Reject");
}
static int account_empty(u32 tid, int dir)
{
char pass[ISCSI_NAME_LEN];
memset(pass, 0, sizeof(pass));
return config_account_query(tid, dir, pass, pass) < 0 ? 1 : 0;
}
static void text_scan_security(struct connection *conn)
{
struct iscsi_login_rsp_hdr *rsp = (struct iscsi_login_rsp_hdr *)&conn->rsp.bhs;
@@ -208,7 +202,7 @@ static void text_scan_security(struct connection *conn)
datasize = conn->req.datasize;
while ((key = next_key(&data, &datasize, &value))) {
if (!(param_index_by_name(key, login_keys) < 0))
if (!(params_index_by_name(key, login_keys) < 0))
;
else if (!strcmp(key, "AuthMethod")) {
do {
@@ -217,13 +211,13 @@ static void text_scan_security(struct connection *conn)
*nextValue++ = 0;
if (!strcmp(value, "None")) {
if (!account_empty(conn->tid, AUTH_DIR_INCOMING))
if (!accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING))
continue;
conn->auth_method = AUTH_NONE;
text_key_add(conn, key, "None");
break;
} else if (!strcmp(value, "CHAP")) {
if (account_empty(conn->tid, AUTH_DIR_INCOMING))
if (accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING))
continue;
conn->auth_method = AUTH_CHAP;
text_key_add(conn, key, "CHAP");
@@ -285,7 +279,7 @@ static int login_check_reinstatement(struct connection *conn)
"initiator %s)", conn->cid, conn->tid,
req->sid.id64, conn->initiator);
conn->sess = session;
insque(&conn->clist, &session->conn_list);
list_add_tail(&conn->clist, &session->conn_list);
} else {
log_error("Only a single connection supported "
"(initiator %s)", conn->initiator);
@@ -329,11 +323,11 @@ static void text_scan_login(struct connection *conn)
datasize = conn->req.datasize;
while ((key = next_key(&data, &datasize, &value))) {
if (!(param_index_by_name(key, login_keys) < 0))
if (!(params_index_by_name(key, login_keys) < 0))
;
else if (!strcmp(key, "AuthMethod"))
;
else if (!((idx = param_index_by_name(key, session_keys)) < 0)) {
else if (!((idx = params_index_by_name(key, session_keys)) < 0)) {
unsigned int val;
char buf[32];
@@ -342,12 +336,12 @@ static void text_scan_login(struct connection *conn)
continue;
}
if (idx == key_max_recv_data_length) {
conn->session_param[idx].key_state = KEY_STATE_DONE;
conn->session_params[idx].key_state = KEY_STATE_DONE;
idx = key_max_xmit_data_length;
};
if (param_str_to_val(session_keys, idx, value, &val) < 0) {
if (conn->session_param[idx].key_state == KEY_STATE_START) {
if (params_str_to_val(session_keys, idx, value, &val) < 0) {
if (conn->session_params[idx].key_state == KEY_STATE_START) {
text_key_add_reject(conn, key);
continue;
} else {
@@ -358,30 +352,30 @@ static void text_scan_login(struct connection *conn)
}
}
param_check_val(session_keys, idx, &val);
param_set_val(session_keys, conn->session_param, idx, &val);
params_check_val(session_keys, idx, &val);
params_set_val(session_keys, conn->session_params, idx, &val);
switch (conn->session_param[idx].key_state) {
switch (conn->session_params[idx].key_state) {
case KEY_STATE_START:
if (iscsi_is_key_internal(idx)) {
conn->session_param[idx].key_state = KEY_STATE_DONE;
conn->session_params[idx].key_state = KEY_STATE_DONE;
break;
}
memset(buf, 0, sizeof(buf));
param_val_to_str(session_keys, idx, val, buf);
params_val_to_str(session_keys, idx, val, buf, sizeof(buf));
text_key_add(conn, key, buf);
conn->session_param[idx].key_state = KEY_STATE_DONE_ADDED;
conn->session_params[idx].key_state = KEY_STATE_DONE_ADDED;
break;
case KEY_STATE_REQUEST:
if (val != conn->session_param[idx].val) {
if (val != conn->session_params[idx].val) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_INIT_ERR;
conn->state = STATE_EXIT;
log_warning("%s %u %u\n", key,
val, conn->session_param[idx].val);
val, conn->session_params[idx].val);
goto out;
}
conn->session_param[idx].key_state = KEY_STATE_DONE;
conn->session_params[idx].key_state = KEY_STATE_DONE;
break;
case KEY_STATE_DONE_ADDED:
case KEY_STATE_DONE:
@@ -395,9 +389,9 @@ out:
return;
}
static int text_check_param(struct connection *conn)
static int text_check_params(struct connection *conn)
{
struct iscsi_param *p = conn->session_param;
struct iscsi_param *p = conn->session_params;
char buf[32];
int i, cnt;
@@ -426,7 +420,7 @@ static int text_check_param(struct connection *conn)
if (iscsi_is_key_internal(i))
continue;
memset(buf, 0, sizeof(buf));
param_val_to_str(session_keys, i, p[i].val, buf);
params_val_to_str(session_keys, i, p[i].val, buf, sizeof(buf));
text_key_add(conn, session_keys[i].name, buf);
if (i == key_max_recv_data_length) {
p[i].key_state = KEY_STATE_DONE;
@@ -445,6 +439,25 @@ static int text_check_param(struct connection *conn)
return cnt;
}
static int init_conn_session_params(struct connection *conn)
{
int res = 0, i;
struct target *target;
target = target_find_by_id(conn->tid);
if (target == NULL) {
log_error("target %d not found", conn->tid);
res = -EINVAL;
goto out;
}
for (i = 0; i < session_key_last; i++)
conn->session_params[i].val = target->session_params[i];
out:
return res;
}
static void login_start(struct connection *conn)
{
struct iscsi_login_req_hdr *req = (struct iscsi_login_req_hdr *)&conn->req.bhs;
@@ -479,9 +492,9 @@ static void login_start(struct connection *conn)
conn->session_type = SESSION_NORMAL;
if (session_type) {
if (!strcmp(session_type, "Discovery"))
if (!strcmp(session_type, "Discovery")) {
conn->session_type = SESSION_DISCOVERY;
else if (strcmp(session_type, "Normal")) {
} else if (strcmp(session_type, "Normal")) {
rsp->status_class = ISCSI_STATUS_INITIATOR_ERR;
rsp->status_detail = ISCSI_STATUS_INV_SESSION_TYPE;
conn->state = STATE_EXIT;
@@ -509,7 +522,7 @@ static void login_start(struct connection *conn)
}
if (!target->tgt_enabled) {
log_debug(1, "Connect from %s to disabled target %s",
log_info("Connect from %s to disabled target %s refused",
name, target_name);
rsp->status_class = ISCSI_STATUS_TARGET_ERR;
conn->state = STATE_CLOSE;
@@ -527,15 +540,9 @@ static void login_start(struct connection *conn)
return;
}
err = kernel_param_get(conn->tid, conn->sid.id64, key_session,
conn->session_param);
if (err == -ENOENT) {
err = kernel_param_get(conn->tid, 0, key_session,
conn->session_param);
}
err = init_conn_session_params(conn);
if (err != 0) {
log_error("Can't get session param for session 0x%" PRIu64
log_error("Can't get session params for session 0x%" PRIu64
" (err %d): %s\n", conn->sid.id64, err,
strerror(-err));
rsp->status_class = ISCSI_STATUS_TARGET_ERR;
@@ -694,20 +701,20 @@ static void cmnd_exec_login(struct connection *conn)
conn->state = STATE_LOGIN;
login_start(conn);
if (!account_empty(conn->tid, AUTH_DIR_INCOMING))
if (!accounts_empty(conn->tid, ISCSI_USER_DIR_INCOMING))
goto auth_err;
if (rsp->status_class)
return;
text_scan_login(conn);
if (rsp->status_class)
return;
stay = text_check_param(conn);
stay = text_check_params(conn);
break;
case STATE_LOGIN:
text_scan_login(conn);
if (rsp->status_class)
return;
stay = text_check_param(conn);
stay = text_check_params(conn);
break;
default:
goto init_err;
@@ -737,7 +744,7 @@ static void cmnd_exec_login(struct connection *conn)
switch (conn->state) {
case STATE_SECURITY:
case STATE_SECURITY_DONE:
if ((nsg_disagree = text_check_param(conn))) {
if ((nsg_disagree = text_check_params(conn))) {
conn->state = STATE_LOGIN;
nsg = ISCSI_FLG_NSG_LOGIN;
break;
@@ -755,7 +762,7 @@ static void cmnd_exec_login(struct connection *conn)
}
if (!stay && !nsg_disagree) {
int err;
text_check_param(conn);
text_check_params(conn);
err = login_finish(conn);
if (err != 0) {
log_debug(1, "login_finish() failed: %d", err);
+67 -28
View File
@@ -68,7 +68,9 @@ struct connection {
struct session *sess;
u32 tid;
struct iscsi_param session_param[session_key_last];
/* Put here, because negotiations is done before session created */
struct iscsi_param session_params[session_key_last];
char *initiator;
char *user;
@@ -128,10 +130,6 @@ struct connection {
#define AUTH_STATE_START 0
#define AUTH_STATE_CHALLENGE 1
/* don't touch these */
#define AUTH_DIR_INCOMING 0
#define AUTH_DIR_OUTGOING 1
#define SESSION_NORMAL 0
#define SESSION_DISCOVERY 1
#define AUTH_UNKNOWN -1
@@ -148,6 +146,21 @@ struct connection {
*/
#define INCOMING_BUFSIZE 8192
struct iscsi_user {
struct __qelem ulist;
struct target *target;
#define ISCSI_USER_DIR_INCOMING 0
#define ISCSI_USER_DIR_OUTGOING 1
int direction;
const char *name;
const char *password;
char sysfs_name[64];
};
struct target {
struct __qelem tlist;
@@ -155,10 +168,16 @@ struct target {
unsigned int tgt_enabled:1;
unsigned int target_params[target_key_last];
unsigned int session_params[session_key_last];
u32 tid;
char name[ISCSI_NAME_LEN];
char *alias;
struct __qelem target_in_accounts;
struct __qelem target_out_accounts;
struct __qelem isns_head;
};
@@ -182,12 +201,12 @@ extern uint16_t server_port;
extern void isns_set_fd(int isns, int scn_listen, int scn);
/* iscsid.c */
extern int iscsi_debug;
extern int iscsi_enabled;
extern int cmnd_execute(struct connection *conn);
extern void cmnd_finish(struct connection *conn);
extern char *text_key_find(struct connection *conn, char *searchKey);
extern void text_key_add(struct connection *conn, char *key, char *value);
extern void text_key_add(struct connection *conn, char *key, const char *value);
/* log.c */
extern int log_daemon;
@@ -219,8 +238,10 @@ extern struct connection *conn_find(struct session *session, u16 cid);
/* target.c */
extern struct __qelem targets_list;
extern int target_add(u32 *, char *);
extern int target_del(u32);
extern int target_create(const char *name, struct target **out_target);
extern void target_free(struct target *target);
extern int target_add(struct target *target, u32 *tid, u32 cookie);
extern int target_del(u32 tid, u32 cookie);
extern u32 target_find_id_by_name(const char *name);
extern struct target *target_find_by_name(const char *name);
struct target *target_find_by_id(u32);
@@ -232,13 +253,19 @@ extern int iscsi_adm_request_handle(int accept_fd);
/* ctldev.c */
extern int kernel_open(int *max_data_seg_len);
extern int kernel_param_get(u32 tid, u64 sid, int type, struct iscsi_param *param);
extern int kernel_param_set(u32 tid, u64 sid, int type, u32 partial,
struct iscsi_param *param);
extern int kernel_target_create(u32 *tid, char *name);
extern int kernel_target_destroy(u32 tid);
extern int kernel_session_create(u32 tid, u64 sid, u32 exp_cmd_sn,
char *name, char *user);
extern int kernel_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params);
extern int kernel_params_set(u32 tid, u64 sid, int type, u32 partial,
const struct iscsi_param *params);
extern int kernel_target_create(struct target *target, u32 *tid, u32 cookie);
extern int kernel_target_destroy(u32 tid, u32 cookie);
#ifndef CONFIG_SCST_PROC
extern int kernel_user_add(struct iscsi_user *user, u32 cookie);
extern int kernel_user_del(struct iscsi_user *user, u32 cookie);
extern int kernel_attr_add(struct target *target, const char *name,
u32 mode, u32 cookie);
extern int kernel_attr_del(struct target *target, const char *name, u32 cookie);
#endif
extern int kernel_session_create(struct connection *conn);
extern int kernel_session_destroy(u32 tid, u64 sid);
extern int kernel_conn_create(u32 tid, u64 sid, u32 cid, u32 stat_sn, u32 exp_stat_sn,
int fd);
@@ -248,26 +275,38 @@ extern int kernel_conn_destroy(u32 tid, u64 sid, u32 cid);
extern void handle_iscsi_events(int fd);
extern int nl_open(void);
/* param.c */
extern int param_index_by_name(char *name, struct iscsi_key *keys);
/* config.c */
extern int config_isns_load(char *params, char **isns, int *isns_ac);
extern int config_load(char *params);
extern char *config_sep_string(char **pp);
extern int config_parse_main(const char *data, u32 cookie);
extern int config_load(const char *config_name);
extern int config_target_create(u32 *tid, char *name);
extern int config_target_destroy(u32 tid);
int config_account_add(u32 tid, int dir, char *name, char *pass);
extern int config_account_query(u32 tid, int dir, char *name, char *pass);
extern int config_account_add(u32 tid, int dir, char *name, char *pass,
char *sysfs_name, u32 cookie);
extern int __config_account_add(struct target *target, int dir, char *name,
char *pass, char *sysfs_name, int send_to_kern, u32 cookie);
extern int config_account_query(u32 tid, int dir, const char *name, char *pass);
extern int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow,
char *buf, size_t buf_sz);
extern int config_account_del(u32 tid, int dir, char *name);
extern int config_param_set(u32 tid, u64 sid, int type, u32 partial,
struct iscsi_param *param);
extern int config_account_del(u32 tid, int dir, char *name, u32 cookie);
extern int config_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params);
extern int config_params_set(u32 tid, u64 sid, int type, u32 partial,
struct iscsi_param *params);
extern int config_initiator_access(u32 tid, int fd);
extern int accounts_empty(u32 tid, int dir);
extern struct iscsi_user *account_get_first(u32 tid, int dir);
extern struct iscsi_user *account_lookup_by_sysfs_name(struct target *target,
int dir, const char *sysfs_name);
extern int account_replace(struct target *target, int direction,
const char *sysfs_name, char *value);
extern void accounts_free(struct __qelem *accounts_list);
/* isns.c */
extern int isns_init(char *addr, int isns_ac);
extern int isns_handle(int is_timeout, int *timeout);
extern char *isns_server;
extern int isns_access_control;
extern int isns_timeout;
extern int isns_init(void);
extern int isns_handle(int is_timeout);
extern int isns_scn_handle(int accept);
extern int isns_scn_access(u32 tid, int fd, char *name);
extern int isns_target_register(char *name);
+86 -38
View File
@@ -54,9 +54,13 @@ struct isns_initiator {
struct __qelem ilist;
};
char *isns_server;
int isns_access_control;
int isns_timeout = -1;
static LIST_HEAD(qry_list);
static uint16_t scn_listen_port;
static int use_isns, use_isns_ac, isns_fd, scn_listen_fd, scn_fd;
static int isns_fd, scn_listen_fd, scn_fd;
static struct isns_io isns_rx, scn_rx;
static char *rxbuf;
static uint16_t transaction;
@@ -70,11 +74,11 @@ int isns_scn_access(uint32_t tid, int fd, char *name)
struct isns_initiator *ini;
struct target *target = target_find_by_id(tid);
if (!use_isns || !use_isns_ac)
if ((isns_server == NULL) || !isns_access_control)
return 0;
if (!target)
return -EPERM;
return -ENOENT;
list_for_each_entry(ini, &target->isns_head, ilist) {
if (!strcmp(ini->name, name))
@@ -130,19 +134,25 @@ static int isns_connect(void)
{
int fd, err;
log_debug(1, "Going to connect to iSNS server %s", isns_server);
fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
log_error("unable to create (%s) %d!", strerror(errno),
ss.ss_family);
return -1;
return -errno;
}
/*
* ToDo: must be made non-blocking, otherwise for an unreacheable
* server it blocks all other events processing until timeout (30 secs).
*/
err = connect(fd, (struct sockaddr *) &ss, sizeof(ss));
if (err < 0) {
log_error("unable to connect (%s) %d!", strerror(errno),
ss.ss_family);
close(fd);
return -1;
return -errno;
}
log_error("%s %d: new connection %d", __func__, __LINE__, fd);
@@ -151,7 +161,7 @@ static int isns_connect(void)
err = isns_get_ip(fd);
if (err) {
close(fd);
return -1;
return err;
}
}
@@ -301,7 +311,7 @@ static int isns_attr_query(char *name)
mgmt = malloc(sizeof(*mgmt));
if (!mgmt)
return 0;
insque(&mgmt->qlist, &qry_list);
list_add_tail(&mgmt->qlist, &qry_list);
memset(buf, 0, sizeof(buf));
tlv = (struct isns_tlv *) hdr->pdu;
@@ -384,12 +394,14 @@ int isns_target_register(char *name)
struct target *target;
int err, initial = list_length_is_one(&targets_list);
if (!use_isns)
if (isns_server == NULL)
return 0;
if (!isns_fd)
if (isns_connect() < 0)
return 0;
if (!isns_fd) {
err = isns_connect();
if (err < 0)
return err;
}
memset(buf, 0, sizeof(buf));
tlv = (struct isns_tlv *) hdr->pdu;
@@ -447,7 +459,7 @@ static void free_all_acl(struct target *target)
while (!list_empty(&target->isns_head)) {
ini = list_entry(target->isns_head.q_forw, typeof(*ini), ilist);
remque(&ini->ilist);
list_del(&ini->ilist);
}
}
@@ -464,7 +476,7 @@ int isns_target_deregister(char *name)
if (target)
free_all_acl(target);
if (!use_isns)
if (isns_server == NULL)
return 0;
if (!isns_fd)
@@ -637,7 +649,7 @@ static void qry_rsp_handle(struct isns_hdr *hdr)
list_for_each_entry_safe(mgmt, n, &qry_list, qlist) {
if (mgmt->transaction == transaction) {
remque(&mgmt->qlist);
list_del(&mgmt->qlist);
goto found;
}
}
@@ -690,7 +702,7 @@ found:
goto free_qry_mgmt;
snprintf(ini->name, sizeof(ini->name), "%s",
name);
insque(&ini->ilist, &target->isns_head);
list_add_tail(&ini->ilist, &target->isns_head);
} else
name = NULL;
break;
@@ -707,7 +719,7 @@ free_qry_mgmt:
free(mgmt);
}
int isns_handle(int is_timeout, int *timeout)
int isns_handle(int is_timeout)
{
int err;
struct isns_io *rx = &isns_rx;
@@ -716,6 +728,9 @@ int isns_handle(int is_timeout, int *timeout)
uint16_t function, length, flags, transaction, sequence;
char *name = NULL;
if (isns_server == NULL)
return 0;
if (is_timeout)
return isns_attr_query(NULL);
@@ -855,7 +870,7 @@ int isns_scn_handle(int is_accept)
return 0;
}
static int scn_init(char *addr)
static int scn_init(void)
{
int fd, opt, err;
union {
@@ -868,29 +883,31 @@ static int scn_init(char *addr)
fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno));
return -errno;
err = -errno;
goto out;
}
opt = 1;
if (ss.ss_family == AF_INET6) {
err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
if (err)
if (err) {
log_error("%s %d: %s\n", __func__, __LINE__,
strerror(errno));
goto out;
goto out_close;
}
}
err = listen(fd, 5);
if (err) {
log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno));
goto out;
goto out_close;
}
slen = sizeof(lss);
err = getsockname(fd, (struct sockaddr *) &lss, &slen);
if (err) {
log_error("%s %d: %s\n", __func__, __LINE__, strerror(errno));
goto out;
goto out_close;
}
/* protocol independent way ? */
@@ -899,8 +916,9 @@ static int scn_init(char *addr)
else
scn_listen_port = ntohs(lss.sin.sin_port);
log_error("scn listen port %u %d %d\n", scn_listen_port, fd, err);
out:
log_info("scn listen port %u %d %d\n", scn_listen_port, fd, err);
out_close:
if (err)
close(fd);
else {
@@ -908,10 +926,11 @@ out:
isns_set_fd(isns_fd, scn_listen_fd, scn_fd);
}
out:
return err;
}
int isns_init(char *addr, int isns_ac)
int isns_init(void)
{
int err;
char port[8];
@@ -920,10 +939,11 @@ int isns_init(char *addr, int isns_ac)
snprintf(port, sizeof(port), "%d", ISNS_PORT);
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
err = getaddrinfo(addr, (char *) &port, &hints, &res);
err = getaddrinfo(isns_server, (char *)&port, &hints, &res);
if (err) {
log_error("getaddrinfo error: %s, %s", gai_strerror(err), addr);
return -1;
log_error("getaddrinfo error: %s, %s", gai_strerror(err),
isns_server);
goto out;
}
memcpy(&ss, res->ai_addr, sizeof(ss));
freeaddrinfo(res);
@@ -931,28 +951,40 @@ int isns_init(char *addr, int isns_ac)
rxbuf = calloc(2, BUFSIZE);
if (!rxbuf) {
log_error("oom!");
return -1;
err = -ENOMEM;
goto out;
}
scn_init(addr);
err = scn_init();
if (err != 0)
goto out_free;
isns_rx.buf = rxbuf;
isns_rx.offset = 0;
scn_rx.buf = rxbuf + BUFSIZE;
scn_rx.offset = 0;
use_isns = 1;
use_isns_ac = isns_ac;
isns_timeout = current_timeout * 1000;
return current_timeout * 1000;
err = isns_connect();
out:
return err;
out_free:
free(rxbuf);
goto out;
}
void isns_exit(void)
{
struct target *target;
if (!use_isns)
return;
if (isns_server == NULL)
goto out;
if (!isns_fd)
goto close;
list_for_each_entry(target, &targets_list, tlist)
isns_scn_deregister(target->name);
@@ -961,12 +993,28 @@ void isns_exit(void)
/* we can't receive events any more. */
isns_set_fd(0, 0, 0);
if (isns_fd)
close:
if (isns_fd) {
close(isns_fd);
if (scn_listen_fd)
isns_fd = 0;
}
if (scn_listen_fd) {
close(scn_listen_fd);
if (scn_fd)
scn_listen_fd = 0;
}
if (scn_fd) {
close(scn_fd);
scn_fd = 0;
}
free(rxbuf);
rxbuf = NULL;
free(isns_server);
isns_server = NULL;
isns_timeout = -1;
out:
return;
}
+4 -2
View File
@@ -64,9 +64,11 @@ static void dolog(int prio, const char *func, int line, const char *fmt, va_list
int len = strlen(func) + strlen(fmt);
char f[len+1+1];
if (fmt[len] != '\n')
sprintf(f, "%s:%d: %s\n", func, line, fmt);
sprintf(f, "%s:%d: %s %s\n", func, line,
(prio == LOG_ERR) ? "ERROR:" : "", fmt);
else
sprintf(f, "%s:%d: %s", func, line, fmt);
sprintf(f, "%s:%d: %s %s", func, line,
(prio == LOG_ERR) ? "ERROR: " : "", fmt);
vsyslog(prio, f, ap);
} else {
struct timeval time;
+10 -10
View File
@@ -65,22 +65,22 @@ static void iscsi_adm_request_exec(struct iscsi_adm_req *req, struct iscsi_adm_r
break;
case C_TRGT_UPDATE:
if (req->u.trgt.type & (1 << key_session))
err = config_param_set(req->tid, req->sid,
err = config_params_set(req->tid, req->sid,
key_session,
req->u.trgt.session_partial,
req->u.trgt.session_param);
req->u.trgt.session_params);
if (err < 0)
goto out;
if (req->u.trgt.type & (1 << key_target))
err = config_param_set(req->tid, req->sid, key_target,
err = config_params_set(req->tid, req->sid, key_target,
req->u.trgt.target_partial,
req->u.trgt.target_param);
req->u.trgt.target_params);
break;
case C_TRGT_SHOW:
err = kernel_param_get(req->tid, req->sid, key_target,
req->u.trgt.target_param);
err = config_params_get(req->tid, req->sid, key_target,
req->u.trgt.target_params);
break;
case C_SESS_NEW:
@@ -88,8 +88,8 @@ static void iscsi_adm_request_exec(struct iscsi_adm_req *req, struct iscsi_adm_r
case C_SESS_UPDATE:
break;
case C_SESS_SHOW:
err = kernel_param_get(req->tid, req->sid, key_session,
req->u.trgt.session_param);
err = config_params_get(req->tid, req->sid, key_session,
req->u.trgt.session_params);
break;
case C_CONN_NEW:
@@ -105,11 +105,11 @@ static void iscsi_adm_request_exec(struct iscsi_adm_req *req, struct iscsi_adm_r
case C_ACCT_NEW:
err = config_account_add(req->tid, req->u.acnt.auth_dir,
req->u.acnt.u.user.name,
req->u.acnt.u.user.pass);
req->u.acnt.u.user.pass, NULL, 0);
break;
case C_ACCT_DEL:
err = config_account_del(req->tid, req->u.acnt.auth_dir,
req->u.acnt.u.user.name);
req->u.acnt.u.user.name, 0);
break;
case C_ACCT_LIST:
*rsp_data = malloc(req->u.acnt.u.list.alloc_len);
+115 -52
View File
@@ -18,10 +18,24 @@
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h>
#include "iscsid.h"
int param_index_by_name(char *name, struct iscsi_key *keys)
/* Taken from Linux kernel sources */
size_t strlcpy(char *dest, const char *src, size_t size)
{
size_t ret = strlen(src);
if (size) {
size_t len = (ret >= size) ? size - 1 : ret;
memcpy(dest, src, len);
dest[len] = '\0';
}
return ret;
}
int params_index_by_name(char *name, struct iscsi_key *keys)
{
int i, err = -ENOENT;
@@ -35,19 +49,43 @@ int param_index_by_name(char *name, struct iscsi_key *keys)
return err;
}
void param_set_defaults(struct iscsi_param *params, struct iscsi_key *keys)
int params_index_by_name_numwild(char *name, struct iscsi_key *keys)
{
int i, err = -ENOENT;
for (i = 0; keys[i].name; i++) {
if (!strncasecmp(keys[i].name, name, strlen(keys[i].name))) {
int j;
if (strlen(keys[i].name) > strlen(name))
continue;
for (j = strlen(keys[i].name); j < strlen(name); j++) {
if (!isdigit(name[j]))
goto next;
}
err = i;
break;
next:
continue;
}
}
return err;
}
void params_set_defaults(unsigned int *params, struct iscsi_key *keys)
{
int i;
for (i = 0; keys[i].name; i++)
params[i].val = keys[i].local_def;
params[i] = keys[i].local_def;
return;
}
static int range_val_to_str(unsigned int val, char *str)
static int range_val_to_str(unsigned int val, char *str, int len)
{
sprintf(str, "%u", val);
snprintf(str, len, "%u", val);
return 0;
}
@@ -57,16 +95,16 @@ static int range_str_to_val(char *str, unsigned int *val)
return 0;
}
static int bool_val_to_str(unsigned int val, char *str)
static int bool_val_to_str(unsigned int val, char *str, int len)
{
int err = 0;
switch (val) {
case 0:
strcpy(str, "No");
strlcpy(str, "No", len);
break;
case 1:
strcpy(str, "Yes");
strlcpy(str, "Yes", len);
break;
default:
err = -EINVAL;
@@ -136,18 +174,32 @@ static int maximum_set_val(struct iscsi_param *param, int idx, unsigned int *val
return 0;
}
static int digest_val_to_str(unsigned int val, char *str)
static int digest_val_to_str(unsigned int val, char *str, int len)
{
int err = 0;
int pos = 0;
if (val & DIGEST_CRC32C)
strcpy(str, "CRC32C");
else if (val & DIGEST_NONE)
strcpy(str, "None");
else
err = -EINVAL;
if (val & DIGEST_NONE) {
len -= pos;
pos = snprintf(&str[pos], len, "%s", "None");
}
return err;
if (pos >= len)
goto out;
if (val & DIGEST_CRC32C) {
len -= pos;
pos = snprintf(&str[pos], len, "%s%s",
(pos != 0) ? "," : "", "CRC32C");
}
if (pos >= len)
goto out;
if (pos == 0)
pos = snprintf(&str[0], len, "%s", "Unknown");
out:
return 0;
}
static int digest_str_to_val(char *str, unsigned int *val)
@@ -156,11 +208,13 @@ static int digest_str_to_val(char *str, unsigned int *val)
char *p, *q;
p = str;
*val = DIGEST_NONE;
*val = 0;
do {
if (!strncmp(p, "None", strlen("None")))
while ((*p != '\0') && isspace(*p))
p++;
if (!strncasecmp(p, "None", strlen("None")))
*val |= DIGEST_NONE;
else if (!strncmp(p, "CRC32C", strlen("CRC32C")))
else if (!strncasecmp(p, "CRC32C", strlen("CRC32C")))
*val |= DIGEST_CRC32C;
else {
err = -EINVAL;
@@ -171,6 +225,9 @@ static int digest_str_to_val(char *str, unsigned int *val)
p = q + 1;
} while (q);
if (*val == 0)
*val = DIGEST_NONE;
return err;
}
@@ -186,38 +243,38 @@ static int digest_set_val(struct iscsi_param *param, int idx, unsigned int *val)
return 0;
}
static int marker_val_to_str(unsigned int val, char *str)
static int marker_val_to_str(unsigned int val, char *str, int len)
{
if (val == 0)
strcpy(str, "Irrelevant");
strlcpy(str, "Irrelevant", len);
else
strcpy(str, "Reject");
strlcpy(str, "Reject", len);
return 0;
}
static int marker_set_val(struct iscsi_param *param, int idx, unsigned int *val)
static int marker_set_val(struct iscsi_param *params, int idx, unsigned int *val)
{
if ((idx == key_ofmarkint && param[key_ofmarker].key_state == KEY_STATE_DONE) ||
(idx == key_ifmarkint && param[key_ifmarker].key_state == KEY_STATE_DONE))
if ((idx == key_ofmarkint && params[key_ofmarker].key_state == KEY_STATE_DONE) ||
(idx == key_ifmarkint && params[key_ifmarker].key_state == KEY_STATE_DONE))
*val = 0;
else
*val = 1;
param[idx].val = *val;
params[idx].val = *val;
return 0;
}
int param_val_to_str(struct iscsi_key *keys, int idx, unsigned int val, char *str)
int params_val_to_str(struct iscsi_key *keys, int idx, unsigned int val, char *str, int len)
{
if (keys[idx].ops->val_to_str)
return keys[idx].ops->val_to_str(val, str);
return keys[idx].ops->val_to_str(val, str, len);
else
return 0;
}
int param_str_to_val(struct iscsi_key *keys, int idx, char *str, unsigned int *val)
int params_str_to_val(struct iscsi_key *keys, int idx, char *str, unsigned int *val)
{
if (keys[idx].ops->str_to_val)
return keys[idx].ops->str_to_val(str, val);
@@ -225,7 +282,7 @@ int param_str_to_val(struct iscsi_key *keys, int idx, char *str, unsigned int *v
return 0;
}
int param_check_val(struct iscsi_key *keys, int idx, unsigned int *val)
int params_check_val(struct iscsi_key *keys, int idx, unsigned int *val)
{
if (keys[idx].ops->check_val)
return keys[idx].ops->check_val(&keys[idx], val);
@@ -233,7 +290,7 @@ int param_check_val(struct iscsi_key *keys, int idx, unsigned int *val)
return 0;
}
int param_set_val(struct iscsi_key *keys, struct iscsi_param *param,
int params_set_val(struct iscsi_key *keys, struct iscsi_param *param,
int idx, unsigned int *val)
{
if (keys[idx].ops->set_val)
@@ -284,35 +341,41 @@ static struct iscsi_key_ops marker_ops = {
/*
* List of local target keys with initial values.
* Must match corresponding key_* enum in iscsi_scst.h!!
*
* Updating this array don't forget to update tgt_params_check() in
* the kernel as well!
*/
struct iscsi_key target_keys[] = {
{"QueuedCommands", SET_KEY_VALUES(QUEUED_CMNDS), &minimum_ops},
{"QueuedCommands", SET_KEY_VALUES(QUEUED_CMNDS), 1, &minimum_ops},
{NULL,},
};
/*
* List of iSCSI RFC specified session keys with initial values.
* Must match corresponding key_* enum in iscsi_scst.h!!
*
* Updating this array don't forget to update sess_params_check() in
* the kernel as well!
*/
struct iscsi_key session_keys[] = {
{"InitialR2T", 1, 0, 0, 1, &or_ops},
{"ImmediateData", 1, 1, 0, 1, &and_ops},
{"MaxConnections", 1, 1, 1, 1, &minimum_ops},
{"MaxRecvDataSegmentLength", 8192, -1, 512, -1, &minimum_ops},
{"MaxXmitDataSegmentLength", 8192, -1, 512, -1, &minimum_ops},
{"MaxBurstLength", 262144, -1, 512, -1, &minimum_ops},
{"FirstBurstLength", 65536, -1, 512, -1, &minimum_ops},
{"DefaultTime2Wait", 2, 0, 0, 3600, &maximum_ops},
{"DefaultTime2Retain", 20, 0, 0, 3600, &minimum_ops},
{"MaxOutstandingR2T", 1, 32, 1, 65535, &minimum_ops},
{"DataPDUInOrder", 1, 0, 0, 1, &or_ops},
{"DataSequenceInOrder", 1, 0, 0, 1, &or_ops},
{"ErrorRecoveryLevel", 0, 0, 0, 0, &minimum_ops},
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, &digest_ops},
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, &digest_ops},
{"OFMarker", 0, 0, 0, 0, &and_ops},
{"IFMarker", 0, 0, 0, 0, &and_ops},
{"OFMarkInt", 2048, 2048, 1, 65535, &marker_ops},
{"IFMarkInt", 2048, 2048, 1, 65535, &marker_ops},
{"InitialR2T", 1, 0, 0, 1, 1, &or_ops},
{"ImmediateData", 1, 1, 0, 1, 1, &and_ops},
{"MaxConnections", 1, 1, 1, 1, 0, &minimum_ops},
{"MaxRecvDataSegmentLength", 8192, -1, 512, -1, 1, &minimum_ops},
{"MaxXmitDataSegmentLength", 8192, -1, 512, -1, 1, &minimum_ops},
{"MaxBurstLength", 262144, -1, 512, -1, 1, &minimum_ops},
{"FirstBurstLength", 65536, -1, 512, -1, 1, &minimum_ops},
{"DefaultTime2Wait", 2, 0, 0, 3600, 0, &maximum_ops},
{"DefaultTime2Retain", 20, 0, 0, 3600, 0, &minimum_ops},
{"MaxOutstandingR2T", 1, 32, 1, 65535, 1, &minimum_ops},
{"DataPDUInOrder", 1, 0, 0, 1, 0, &or_ops},
{"DataSequenceInOrder", 1, 0, 0, 1, 0, &or_ops},
{"ErrorRecoveryLevel", 0, 0, 0, 0, 0, &minimum_ops},
{"HeaderDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, 1, &digest_ops},
{"DataDigest", DIGEST_NONE, DIGEST_NONE, DIGEST_NONE, DIGEST_ALL, 1, &digest_ops},
{"OFMarker", 0, 0, 0, 0, 0, &and_ops},
{"IFMarker", 0, 0, 0, 0, 0, &and_ops},
{"OFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops},
{"IFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops},
{NULL,},
};
+17 -7
View File
@@ -16,6 +16,11 @@
#ifndef PARAMS_H
#define PARAMS_H
#define ISCSI_ISNS_SERVER_PARAM_NAME "iSNSServer"
#define ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME "iSNSAccessControl"
#define ISCSI_ENABLED_ATTR_NAME "enabled"
struct iscsi_key;
struct iscsi_param {
@@ -24,7 +29,7 @@ struct iscsi_param {
};
struct iscsi_key_ops {
int (*val_to_str)(unsigned int, char *);
int (*val_to_str)(unsigned int, char *, int);
int (*str_to_val)(char *, unsigned int *);
int (*check_val)(struct iscsi_key *, unsigned int *);
int (*set_val)(struct iscsi_param *, int, unsigned int *);
@@ -36,17 +41,22 @@ struct iscsi_key {
unsigned int local_def;
unsigned int min;
unsigned int max;
int show_in_sysfs;
struct iscsi_key_ops *ops;
};
extern struct iscsi_key session_keys[];
extern struct iscsi_key target_keys[];
extern struct iscsi_key user_keys[];
extern void param_set_defaults(struct iscsi_param *, struct iscsi_key *);
extern int param_index_by_name(char *, struct iscsi_key *);
extern int param_val_to_str(struct iscsi_key *, int, unsigned int, char *);
extern int param_str_to_val(struct iscsi_key *, int, char *, unsigned int *);
extern int param_check_val(struct iscsi_key *, int, unsigned int *);
extern int param_set_val(struct iscsi_key *, struct iscsi_param *, int, unsigned int *);
extern size_t strlcpy(char *dest, const char *src, size_t size);
extern void params_set_defaults(unsigned int *, struct iscsi_key *);
extern int params_index_by_name(char *, struct iscsi_key *);
extern int params_index_by_name_numwild(char *, struct iscsi_key *);
extern int params_val_to_str(struct iscsi_key *, int, unsigned int, char *, int);
extern int params_str_to_val(struct iscsi_key *, int, char *, unsigned int *);
extern int params_check_val(struct iscsi_key *, int, unsigned int *);
extern int params_set_val(struct iscsi_key *, struct iscsi_param *, int, unsigned int *);
#endif
+5 -19
View File
@@ -47,7 +47,7 @@ static int session_alloc(u32 tid, struct session **psess)
session->target = target;
INIT_LIST_HEAD(&session->slist);
insque(&session->slist, &target->sessions_list);
list_add_tail(&session->slist, &target->sessions_list);
*psess = session;
@@ -99,7 +99,6 @@ int session_create(struct connection *conn)
/* We are single threaded, so it doesn't need any protection */
static u16 tsih = 1;
struct session *session;
char *user;
int res = 0;
res = session_alloc(conn->tid, &session);
@@ -112,7 +111,7 @@ int session_create(struct connection *conn)
session->sid.id.tsih = tsih;
INIT_LIST_HEAD(&session->conn_list);
insque(&conn->clist, &session->conn_list);
list_add_tail(&conn->clist, &session->conn_list);
conn->sess = session;
conn->sess->initiator = strdup(conn->initiator);
@@ -136,27 +135,13 @@ int session_create(struct connection *conn)
log_debug(1, "sid %#" PRIx64, session->sid.id64);
if (conn->user != NULL)
user = conn->user;
else
user = "";
res = kernel_session_create(conn->tid, session->sid.id64, conn->exp_cmd_sn,
session->initiator, user);
res = kernel_session_create(conn);
if (res != 0)
goto out_free;
res = kernel_param_set(conn->tid, session->sid.id64, key_session, 0,
conn->session_param);
if (res != 0)
goto out_destroy;
out:
return res;
out_destroy:
kernel_session_destroy(conn->tid, session->sid.id64);
out_free:
session_free(session);
conn->sess = NULL;
@@ -171,10 +156,11 @@ void session_free(struct session *session)
kernel_session_destroy(session->target->tid, session->sid.id64);
if (session->target)
remque(&session->slist);
list_del(&session->slist);
free(session->initiator);
free(session);
return;
}
struct connection *conn_find(struct session *session, u16 cid)
+57 -51
View File
@@ -78,34 +78,25 @@ struct target *target_find_by_id(u32 tid)
return NULL;
}
static void all_accounts_del(u32 tid, int dir)
int target_del(u32 tid, u32 cookie)
{
char name[ISCSI_NAME_LEN], pass[ISCSI_NAME_LEN];
struct target *target;
int err;
memset(name, 0, sizeof(name));
for (; config_account_query(tid, dir, name, pass) != -ENOENT;
memset(name, 0, sizeof(name))) {
config_account_del(tid, dir, name);
}
}
int target_del(u32 tid)
{
struct target *target = target_find_by_id(tid);
int err = kernel_target_destroy(tid);
err = kernel_target_destroy(tid, cookie);
if (err < 0 && err != -ENOENT)
return err;
else if (!err && !target)
target = target_find_by_id(tid);
if (!err && !target)
/* A leftover kernel object was cleaned up - don't complain. */
return 0;
if (!target)
return -ENOENT;
remque(&target->tlist);
list_del(&target->tlist);
if (!list_empty(&target->sessions_list)) {
log_error("%s: target %u still has sessions\n", __FUNCTION__,
@@ -113,62 +104,77 @@ int target_del(u32 tid)
exit(-1);
}
all_accounts_del(tid, AUTH_DIR_INCOMING);
all_accounts_del(tid, AUTH_DIR_OUTGOING);
isns_target_deregister(target->name);
free(target);
target_free(target);
return 0;
}
int target_add(u32 *tid, char *name)
void target_free(struct target *target)
{
accounts_free(&target->target_in_accounts);
accounts_free(&target->target_out_accounts);
free(target);
return;
}
int target_create(const char *name, struct target **out_target)
{
int res = 0;
struct target *target;
int err;
struct iscsi_param tgt_params[target_key_last];
struct iscsi_param sess_params[session_key_last];
if (!name)
return -EINVAL;
if (name == NULL) {
res = EINVAL;
goto out;
}
if (!(target = malloc(sizeof(*target))))
return -ENOMEM;
target = malloc(sizeof(*target));
if (target == NULL) {
res = -ENOMEM;
goto out;
}
memset(target, 0, sizeof(*target));
memcpy(target->name, name, sizeof(target->name) - 1);
if ((err = kernel_target_create(tid, name)) < 0)
goto out_free;
param_set_defaults(tgt_params, target_keys);
err = kernel_param_set(*tid, 0, key_target, 0, tgt_params);
if (err != 0)
goto out_destroy;
param_set_defaults(sess_params, session_keys);
err = kernel_param_set(*tid, 0, key_session, 0, sess_params);
if (err != 0)
goto out_destroy;
params_set_defaults(target->target_params, target_keys);
params_set_defaults(target->session_params, session_keys);
INIT_LIST_HEAD(&target->tlist);
INIT_LIST_HEAD(&target->sessions_list);
INIT_LIST_HEAD(&target->target_in_accounts);
INIT_LIST_HEAD(&target->target_out_accounts);
INIT_LIST_HEAD(&target->isns_head);
target->tid = *tid;
*out_target = target;
out:
return res;
}
int target_add(struct target *target, u32 *tid, u32 cookie)
{
int err;
if (target_find_by_name(target->name)) {
log_error("duplicated target %s", target->name);
err = -EEXIST;
goto out;
}
err = kernel_target_create(target, tid, cookie);
if (err != 0)
goto out;
#ifdef CONFIG_SCST_PROC
target->tgt_enabled = 1;
#endif
insque(&target->tlist, &targets_list);
list_add_tail(&target->tlist, &targets_list);
isns_target_register(name);
isns_target_register(target->name);
out:
return err;
out_destroy:
kernel_target_destroy(*tid);
out_free:
free(target);
goto out;
}
+2 -2
View File
@@ -1,7 +1,7 @@
Target driver for Qlogic 22xx/23xx/24xx/25xx Fibre Channel cards
================================================================
Version 2.0.0, XX XXXXX 2009
Version 2.0.0, XX XXXXX 2010
----------------------------
This driver consists from two parts: the target mode driver itself and
@@ -12,7 +12,7 @@ necessary callbacks, but it's still capable to work as initiator only.
Mode, when a host acts as the initiator and the target simultaneously,
is supported as well.
This version is compatible with SCST core version 1.0.2 and higher and
This version is compatible with SCST core version 2.0.0 and higher and
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.
+2 -2
View File
@@ -158,8 +158,8 @@ static struct scst_tgt_template tgt2x_template = {
.on_free_cmd = q2t_on_free_cmd,
.task_mgmt_fn_done = q2t_task_mgmt_fn_done,
.on_hw_pending_cmd_timeout = q2t_on_hw_pending_cmd_timeout,
.enable_tgt = q2t_enable_tgt,
.is_tgt_enabled = q2t_is_tgt_enabled,
.enable_target = q2t_enable_tgt,
.is_target_enabled = q2t_is_tgt_enabled,
#ifndef CONFIG_SCST_PROC
.tgtt_attrs = q2t_attrs,
.tgt_attrs = q2t_tgt_attrs,
+1 -1
View File
@@ -1,7 +1,7 @@
Generic SCSI target mid-level for Linux (SCST)
==============================================
Version 1.0.2, XX XXXXX 2009
Version 2.0.0, XX XXXXX 2010
----------------------------
SCST is designed to provide unified, consistent interface between SCSI
+100 -2
View File
@@ -605,6 +605,7 @@ struct scst_tgt_template {
* queuecommand. The target should transmit the response
* buffer and the status in the scst_cmd struct.
* The expectation is that this executing this command is NON-BLOCKING.
* If it is blocking, consider to set threads_num to some none 0 number.
*
* After the response is actually transmitted, the target
* should call the scst_tgt_cmd_done() function of the
@@ -631,7 +632,9 @@ struct scst_tgt_template {
* the low-level driver needs to call scst_rx_data() in order to
* continue processing this command.
* Returns one of the SCST_TGT_RES_* constants.
*
* This command is expected to be NON-BLOCKING.
* If it is blocking, consider to set threads_num to some none 0 number.
*
* Pay attention to "atomic" attribute of the cmd, which can be get
* by scst_cmd_atomic(): it is true if the function called in the
@@ -708,6 +711,7 @@ struct scst_tgt_template {
* scst_restart_cmd().
*
* This command is expected to be NON-BLOCKING.
* If it is blocking, consider to set threads_num to some none 0 number.
*
* Pay attention to "atomic" attribute of the cmd, which can be get
* by scst_cmd_atomic(): it is true if the function called in the
@@ -724,6 +728,7 @@ struct scst_tgt_template {
* Returns one of the SCST_PREPROCESS_* constants.
*
* This command is expected to be NON-BLOCKING.
* If it is blocking, consider to set threads_num to some none 0 number.
*
* Pay attention to "atomic" attribute of the cmd, which can be get
* by scst_cmd_atomic(): it is true if the function called in the
@@ -815,7 +820,7 @@ struct scst_tgt_template {
* case the too early connected initiators would see not those devices,
* which they intended to see.
*/
ssize_t (*enable_tgt) (struct scst_tgt *tgt, const char *buffer,
ssize_t (*enable_target) (struct scst_tgt *tgt, const char *buffer,
size_t size);
/*
@@ -823,7 +828,44 @@ struct scst_tgt_template {
*
* SHOULD HAVE, see above why.
*/
bool (*is_tgt_enabled) (struct scst_tgt *tgt);
bool (*is_target_enabled) (struct scst_tgt *tgt);
/*
* This function adds a virtual target.
*
* If both add_target and del_target callbacks defined, then this
* target driver supposed to support virtual targets. In this case
* an "mgmt" entry will be created in the sysfs root for this driver.
* The "mgmt" entry will support 2 commands: "add_target" and
* "del_target", for which the corresponding callbacks will be called.
* Also target driver can define own commands for the "mgmt" entry, see
* mgmt_cmd and mgmt_cmd_help below.
*
* This approach allows uniform targets management to simplify external
* management tools like scstadmin. See README for more details.
*
* Either both add_target and del_target must be defined, or none.
*
* MUST HAVE if virtual targets are supported.
*/
ssize_t (*add_target) (const char *target_name, const char *params);
/*
* This function deletes a virtual target. See comment for add_target
* above.
*
* MUST HAVE if virtual targets are supported.
*/
ssize_t (*del_target) (const char *target_name);
/*
* 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.
*
* OPTIONAL.
*/
ssize_t (*mgmt_cmd) (const char *cmd);
/*
* Name of the template. Must be unique to identify
@@ -862,6 +904,9 @@ struct scst_tgt_template {
const struct attribute **sess_attrs;
#endif
/* Optional help string for mgmt_cmd commands */
const char *mgmt_cmd_help;
/** Private, must be inited to 0 by memset() **/
/* List of targets per template, protected by scst_mutex */
@@ -3544,4 +3589,57 @@ static inline void add_wait_queue_exclusive_head(wait_queue_head_t *q,
spin_unlock_irqrestore(&q->lock, flags);
}
#ifndef CONFIG_SCST_PROC
/**
** Helper functionality to help target drivers and dev handlers support
** sending events to user space and wait for their completion in a safe
** manner. See samples how to use it in iscsi-scst or scst_user.
**/
struct scst_sysfs_user_info {
/* Unique cookie to identify request */
uint32_t info_cookie;
/* Entry in the global list */
struct list_head info_list_entry;
/* Set if reply from the user space is being executed */
unsigned int info_being_executed:1;
/* Set if this info is in the info_list */
unsigned int info_in_list:1;
/* Completion to wait on for the request completion */
struct completion info_completion;
/* Request completion status and optional data */
int info_status;
void *data;
};
/*
* This function creates an info structure and adds it in the info_list.
* Returns 0 and out_info on success, error code otherwise.
*/
int scst_sysfs_user_add_info(struct scst_sysfs_user_info **out_info);
/* This function deletes and frees info */
void scst_sysfs_user_del_info(struct scst_sysfs_user_info *info);
/*
* This function finds the info based on cookie and set for it flag
* info_being_executed. Returns found entry or NULL.
*/
struct scst_sysfs_user_info *scst_sysfs_user_get_info(uint32_t cookie);
/*
* This function waits for the info request been completed by user space.
* Returns status of the request completion.
*/
int scst_wait_info_completion(struct scst_sysfs_user_info *info,
unsigned long timeout);
#endif /* CONFIG_SCST_PROC */
#endif /* __SCST_H */
+3 -1
View File
@@ -351,6 +351,8 @@ enum scst_cdb_flags {
/*************************************************************
** Misc constants
*************************************************************/
#define SCST_SYSFS_BLOCK_SIZE PAGE_SIZE
#define SCST_SYSFS_BLOCK_SIZE PAGE_SIZE
#define SCST_SYSFS_KEY_MARK "[key]"
#endif /* __SCST_CONST_H */
+1 -1
View File
@@ -26,7 +26,7 @@
#define DEV_USER_NAME "scst_user"
#define DEV_USER_PATH "/dev/"
#define DEV_USER_VERSION_NAME "1.0.2"
#define DEV_USER_VERSION_NAME "2.0.0"
#define DEV_USER_VERSION \
DEV_USER_VERSION_NAME "$Revision$" SCST_CONST_VERSION
+3 -8
View File
@@ -2532,16 +2532,12 @@ static int dev_user_attach_tgt(struct scst_tgt_dev *tgt_dev)
ucmd->user_cmd.sess.lun = (uint64_t)tgt_dev->lun;
ucmd->user_cmd.sess.threads_num = tgt_dev->sess->tgt->tgtt->threads_num;
ucmd->user_cmd.sess.rd_only = tgt_dev->acg_dev->rd_only;
strncpy(ucmd->user_cmd.sess.initiator_name,
strlcpy(ucmd->user_cmd.sess.initiator_name,
tgt_dev->sess->initiator_name,
sizeof(ucmd->user_cmd.sess.initiator_name)-1);
ucmd->user_cmd.sess.initiator_name[
sizeof(ucmd->user_cmd.sess.initiator_name)-1] = '\0';
strncpy(ucmd->user_cmd.sess.target_name,
strlcpy(ucmd->user_cmd.sess.target_name,
tgt_dev->sess->tgt->tgt_name,
sizeof(ucmd->user_cmd.sess.target_name)-1);
ucmd->user_cmd.sess.target_name[
sizeof(ucmd->user_cmd.sess.target_name)-1] = '\0';
TRACE_MGMT_DBG("Preparing ATTACH_SESS %p (h %d, sess_h %llx, LUN %llx, "
"threads_num %d, rd_only %d, initiator %s, target %s)",
@@ -2757,8 +2753,7 @@ static int dev_user_register_dev(struct file *file,
for (i = 0; i < (int)ARRAY_SIZE(dev->ucmd_hash); i++)
INIT_LIST_HEAD(&dev->ucmd_hash[i]);
strncpy(dev->name, dev_desc->name, sizeof(dev->name)-1);
dev->name[sizeof(dev->name)-1] = '\0';
strlcpy(dev->name, dev_desc->name, sizeof(dev->name)-1);
scst_init_mem_lim(&dev->udev_mem_lim);
+3 -3
View File
@@ -3277,7 +3277,7 @@ static int vdev_open(struct vdev_type *vdt, char *p, const char *name)
res = -ENOMEM;
goto out_free_vdev;
}
strncpy(virt_dev->file_name, file_name, len);
strlcpy(virt_dev->file_name, file_name, len);
}
if (vdt->vfns.pre_register != NULL) {
@@ -3518,7 +3518,7 @@ static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
goto out_unlock;
}
strncpy(fn, file_name, len);
strlcpy(fn, file_name, len);
virt_dev->file_name = fn;
res = vdisk_get_file_size(virt_dev->file_name,
@@ -4253,7 +4253,7 @@ static int vdisk_proc_mgmt_cmd(const char *buffer, int length,
res = -ENOMEM;
goto out_free_vdev;
}
strncpy(virt_dev->file_name, file_name, len);
strlcpy(virt_dev->file_name, file_name, len);
list_add_tail(&virt_dev->vdisk_dev_list_entry,
&vdisk_dev_list);
+2 -2
View File
@@ -692,7 +692,7 @@ static int scst_set_lun_not_supported_request_sense(struct scst_cmd *cmd,
cmd->sg = scst_alloc(cmd->bufflen, GFP_ATOMIC, &cmd->sg_cnt);
if (cmd->sg == NULL) {
PRINT_CRIT_ERROR("Unable to alloc sg"
PRINT_ERROR("Unable to alloc sg for REQUEST SENSE"
"(sense %x/%x/%x)", key, asc, ascq);
res = 1;
goto out;
@@ -737,7 +737,7 @@ static int scst_set_lun_not_supported_inquiry(struct scst_cmd *cmd)
cmd->sg = scst_alloc(cmd->bufflen, GFP_ATOMIC, &cmd->sg_cnt);
if (cmd->sg == NULL) {
PRINT_CRIT_ERROR("%s", "Unable to alloc sg for INQUIRY "
PRINT_ERROR("%s", "Unable to alloc sg for INQUIRY "
"for not supported LUN");
res = 1;
goto out;
+14 -6
View File
@@ -220,13 +220,21 @@ int __scst_register_target_template(struct scst_tgt_template *vtt,
}
#ifndef CONFIG_SCST_PROC
if (!vtt->enable_tgt || !vtt->is_tgt_enabled) {
PRINT_WARNING("Target driver %s doesn't have enable_tgt() "
"and/or is_tgt_enabled() method(s). This is unsafe "
if (!vtt->enable_target || !vtt->is_target_enabled) {
PRINT_WARNING("Target driver %s doesn't have enable_target() "
"and/or is_target_enabled() method(s). This is unsafe "
"and can lead that initiators connected on the "
"initialization time can see an unexpected set of "
"devices or no devices at all!", vtt->name);
}
if (((vtt->add_target != NULL) && (vtt->del_target == NULL)) ||
((vtt->add_target == NULL) && (vtt->del_target != NULL))) {
PRINT_ERROR("Target driver %s must either define both "
"add_target() and del_target(), or none.", vtt->name);
res = -EINVAL;
goto out_err;
}
#else
if (!vtt->no_proc_entry) {
res = scst_build_proc_target_dir_entries(vtt);
@@ -1298,8 +1306,8 @@ int scst_add_dev_threads(struct scst_device *dev, int num)
PRINT_ERROR("Failed to allocate thr %d", res);
goto out_del;
}
strncpy(nm, dev->handler->name, ARRAY_SIZE(nm)-1);
nm[ARRAY_SIZE(nm)-1] = '\0';
strlcpy(nm, dev->handler->name, ARRAY_SIZE(nm));
thr->cmd_thread = kthread_create(scst_cmd_thread,
&dev->cmd_lists, "%sd%d_%d", nm, dev->dev_num, n++);
if (IS_ERR(thr->cmd_thread)) {
@@ -1801,7 +1809,7 @@ static void __init scst_print_config(void)
j = i;
#ifdef CONFIG_SCST_STRICT_SERIALIZING
i += snprintf(&buf[i], sizeof(buf) - i, "Strict serializing");
i += snprintf(&buf[i], sizeof(buf) - i, "STRICT_SERIALIZING");
#endif
#ifdef CONFIG_SCST_EXTRACHECKS
+1 -2
View File
@@ -1342,8 +1342,7 @@ static int sgv_pool_init(struct sgv_pool *pool, const char *name,
name, sizeof(struct sgv_pool_obj), clustering_type,
single_alloc_pages, pool->max_caches, pool->max_cached_pages);
strncpy(pool->name, name, sizeof(pool->name)-1);
pool->name[sizeof(pool->name)-1] = '\0';
strlcpy(pool->name, name, sizeof(pool->name)-1);
pool->owner_mm = current->mm;
+3 -3
View File
@@ -878,7 +878,7 @@ static int scst_proc_group_add(const char *p)
TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of name failed");
goto out_nomem;
}
strncpy(name, p, len);
strlcpy(name, p, len);
acg = scst_alloc_add_acg(NULL, name);
if (acg == NULL) {
@@ -942,7 +942,7 @@ static int scst_proc_rename_acg(struct scst_acg *acg, const char *new_name)
TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of new name failed");
goto out_nomem;
}
strncpy(name, new_name, len);
strlcpy(name, new_name, len);
res = scst_proc_group_add_tree(acg, new_name);
if (res != 0)
@@ -2225,7 +2225,7 @@ static int scst_version_info_show(struct seq_file *seq, void *v)
seq_printf(seq, "%s\n", SCST_VERSION_STRING);
#ifdef CONFIG_SCST_STRICT_SERIALIZING
seq_printf(seq, "Strict serializing enabled\n");
seq_printf(seq, "STRICT_SERIALIZING\n");
#endif
#ifdef CONFIG_SCST_EXTRACHECKS
+312 -17
View File
@@ -27,8 +27,6 @@
#include "scst_priv.h"
#include "scst_mem.h"
static DEFINE_MUTEX(scst_sysfs_mutex);
static DECLARE_COMPLETION(scst_sysfs_root_release_completion);
static struct kobject scst_sysfs_root_kobj;
@@ -204,6 +202,125 @@ static struct kobj_attribute tgtt_trace_attr =
#endif /* #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */
static ssize_t scst_tgtt_mgmt_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
char *help = "Usage: echo \"add_target target_name [parameters]\" "
">mgmt\n"
" echo \"del_target target_name\" >mgmt\n"
"%s"
"\n"
"where parameters are one or more "
"param_name=value pairs separated by ';'\n";
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 : "");
}
static ssize_t scst_tgtt_mgmt_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
int res;
char *buffer, *p, *target_name;
struct scst_tgt_template *tgtt;
TRACE_ENTRY();
tgtt = container_of(kobj, struct scst_tgt_template, tgtt_kobj);
buffer = kzalloc(count+1, GFP_KERNEL);
if (buffer == NULL) {
res = -ENOMEM;
goto out;
}
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;
while (isspace(*p) && *p != '\0')
p++;
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++;
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')
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)
res = tgtt->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_tgtt_mgmt =
__ATTR(mgmt, S_IRUGO | S_IWUSR, scst_tgtt_mgmt_show,
scst_tgtt_mgmt_store);
int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt)
{
int retval = 0;
@@ -227,6 +344,16 @@ int scst_create_tgtt_sysfs(struct scst_tgt_template *tgtt)
* it will be done by the _put function() called by the caller.
*/
if (tgtt->add_target != NULL) {
retval = sysfs_create_file(&tgtt->tgtt_kobj,
&scst_tgtt_mgmt.attr);
if (retval != 0) {
PRINT_ERROR("Can't add mgmt attr for target driver %s",
tgtt->name);
goto out;
}
}
pattr = tgtt->tgtt_attrs;
if (pattr != NULL) {
while (*pattr != NULL) {
@@ -410,7 +537,7 @@ static ssize_t scst_tgt_enable_show(struct kobject *kobj,
tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
enabled = tgt->tgtt->is_tgt_enabled(tgt);
enabled = tgt->tgtt->is_target_enabled(tgt);
res = sprintf(buf, "%d\n", enabled ? 1 : 0);
@@ -431,7 +558,9 @@ static ssize_t scst_tgt_enable_store(struct kobject *kobj,
tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
res = tgt->tgtt->enable_tgt(tgt, buf, count);
res = tgt->tgtt->enable_target(tgt, buf, count);
if (res == 0)
res = count;
out:
TRACE_EXIT_RES(res);
@@ -470,8 +599,8 @@ int scst_create_tgt_sysfs(struct scst_tgt *tgt)
* it will be done by the _put function() called by the caller.
*/
if ((tgt->tgtt->enable_tgt != NULL) &&
(tgt->tgtt->is_tgt_enabled != NULL)) {
if ((tgt->tgtt->enable_target != NULL) &&
(tgt->tgtt->is_target_enabled != NULL)) {
retval = sysfs_create_file(&tgt->tgt_kobj,
&tgt_enable_attr.attr);
if (retval != 0) {
@@ -1618,7 +1747,7 @@ static ssize_t scst_ini_group_mgmt_store(struct kobject *kobj,
res = -ENOMEM;
goto out_free_up;
}
strncpy(name, p, len);
strlcpy(name, p, len);
acg = scst_alloc_add_acg(tgt, name);
kfree(name);
@@ -1694,7 +1823,7 @@ int scst_create_acn_sysfs(struct scst_acg *acg, struct scst_acn *acn)
retval = -ENOMEM;
goto out_free;
}
strncpy((char *)attr->attr.name, acn->name, len);
strlcpy((char *)attr->attr.name, acn->name, len);
attr->attr.owner = THIS_MODULE;
attr->attr.mode = S_IRUGO;
@@ -1703,7 +1832,7 @@ int scst_create_acn_sysfs(struct scst_acg *acg, struct scst_acn *acn)
retval = sysfs_create_file(acg->initiators_kobj, &attr->attr);
if (retval != 0) {
PRINT_ERROR("Unable to allocate initiator '%s' for group '%s'",
PRINT_ERROR("Unable to create acn '%s' for group '%s'",
acn->name, acg->acg_name);
kfree(attr->attr.name);
goto out_free;
@@ -2131,20 +2260,18 @@ static ssize_t scst_threads_store(struct kobject *kobj,
TRACE_ENTRY();
if (mutex_lock_interruptible(&scst_sysfs_mutex) != 0) {
if (mutex_lock_interruptible(&scst_global_threads_mutex) != 0) {
res = -EINTR;
goto out;
}
mutex_lock(&scst_global_threads_mutex);
oldtn = scst_nr_global_threads;
sscanf(buf, "%du", &newtn);
if (newtn <= 0) {
PRINT_ERROR("Illegal threads num value %d", newtn);
res = -EINVAL;
goto out_up_thr_free;
goto out_up;
}
delta = newtn - oldtn;
if (delta < 0)
@@ -2154,11 +2281,9 @@ static ssize_t scst_threads_store(struct kobject *kobj,
PRINT_INFO("Changed cmd threads num: old %d, new %d", oldtn, newtn);
out_up_thr_free:
out_up:
mutex_unlock(&scst_global_threads_mutex);
mutex_unlock(&scst_sysfs_mutex);
out:
TRACE_EXIT_RES(res);
return res;
@@ -2408,7 +2533,7 @@ static ssize_t scst_version_show(struct kobject *kobj,
sprintf(buf, "%s\n", SCST_VERSION_STRING);
#ifdef CONFIG_SCST_STRICT_SERIALIZING
strcat(buf, "Strict serializing enabled\n");
strcat(buf, "STRICT_SERIALIZING\n");
#endif
#ifdef CONFIG_SCST_EXTRACHECKS
@@ -2686,6 +2811,176 @@ void scst_devt_sysfs_put(struct scst_dev_type *devt)
return;
}
static DEFINE_MUTEX(scst_sysfs_user_info_mutex);
/* All protected by scst_sysfs_user_info_mutex */
static LIST_HEAD(scst_sysfs_user_info_list);
static uint32_t scst_sysfs_info_cur_cookie;
/* scst_sysfs_user_info_mutex supposed to be held */
static struct scst_sysfs_user_info *scst_sysfs_user_find_info(uint32_t cookie)
{
struct scst_sysfs_user_info *info, *res = NULL;
TRACE_ENTRY();
list_for_each_entry(info, &scst_sysfs_user_info_list,
info_list_entry) {
if (info->info_cookie == cookie) {
res = info;
break;
}
}
TRACE_EXIT_HRES(res);
return res;
}
struct scst_sysfs_user_info *scst_sysfs_user_get_info(uint32_t cookie)
{
struct scst_sysfs_user_info *res = NULL;
TRACE_ENTRY();
mutex_lock(&scst_sysfs_user_info_mutex);
res = scst_sysfs_user_find_info(cookie);
if (res != NULL) {
if (!res->info_being_executed)
res->info_being_executed = 1;
}
mutex_unlock(&scst_sysfs_user_info_mutex);
TRACE_EXIT_HRES(res);
return res;
}
EXPORT_SYMBOL(scst_sysfs_user_get_info);
int scst_sysfs_user_add_info(struct scst_sysfs_user_info **out_info)
{
int res = 0;
struct scst_sysfs_user_info *info;
TRACE_ENTRY();
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
PRINT_ERROR("Unable to allocate sysfs user info (size %d)",
sizeof(*info));
res = -ENOMEM;
goto out;
}
mutex_lock(&scst_sysfs_user_info_mutex);
while ((info->info_cookie == 0) ||
(scst_sysfs_user_find_info(info->info_cookie) != NULL))
info->info_cookie = scst_sysfs_info_cur_cookie++;
init_completion(&info->info_completion);
list_add_tail(&info->info_list_entry, &scst_sysfs_user_info_list);
info->info_in_list = 1;
*out_info = info;
mutex_unlock(&scst_sysfs_user_info_mutex);
out:
TRACE_EXIT_RES(res);
return res;
}
EXPORT_SYMBOL(scst_sysfs_user_add_info);
void scst_sysfs_user_del_info(struct scst_sysfs_user_info *info)
{
TRACE_ENTRY();
mutex_lock(&scst_sysfs_user_info_mutex);
if (info->info_in_list)
list_del(&info->info_list_entry);
mutex_unlock(&scst_sysfs_user_info_mutex);
kfree(info);
TRACE_EXIT();
return;
}
EXPORT_SYMBOL(scst_sysfs_user_del_info);
static bool scst_sysfs_user_info_executing(struct scst_sysfs_user_info *info)
{
bool res;
TRACE_ENTRY();
mutex_lock(&scst_sysfs_user_info_mutex);
res = info->info_being_executed;
if (info->info_in_list) {
list_del(&info->info_list_entry);
info->info_in_list = 0;
}
mutex_unlock(&scst_sysfs_user_info_mutex);
TRACE_EXIT_RES(res);
return res;
}
int scst_wait_info_completion(struct scst_sysfs_user_info *info,
unsigned long timeout)
{
int res, rc;
TRACE_ENTRY();
TRACE_DBG("Waiting for info %p completion", info);
while (1) {
rc = wait_for_completion_interruptible_timeout(
&info->info_completion, timeout);
if (rc > 0) {
TRACE_DBG("Waiting for info %p finished with %d",
info, rc);
break;
} else if (rc == 0) {
if (!scst_sysfs_user_info_executing(info)) {
PRINT_ERROR("Timeout waiting for user "
"space event %p", info);
res = -EBUSY;
goto out;
} else {
/* Req is being executed in the kernel */
TRACE_DBG("Keep waiting for info %p completion",
info);
wait_for_completion(&info->info_completion);
break;
}
} else if (rc != -ERESTARTSYS) {
res = rc;
PRINT_ERROR("wait_for_completion() failed: %d",
res);
goto out;
} else {
TRACE_DBG("Waiting for info %p finished with %d, "
"retrying", info, rc);
}
}
TRACE_DBG("info %p, status %d", info, info->info_status);
res = info->info_status;
out:
TRACE_EXIT_RES(res);
return res;
}
EXPORT_SYMBOL(scst_wait_info_completion);
int __init scst_sysfs_init(void)
{
int retval = 0;
+3 -2
View File
@@ -494,6 +494,7 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
goto out_done;
}
#else
PRINT_ERROR("Unknown opcode %x", cmd->cdb[0]);
scst_set_cmd_error(cmd,
SCST_LOAD_SENSE(scst_sense_invalid_opcode));
goto out_done;
@@ -585,8 +586,8 @@ static int scst_parse_cmd(struct scst_cmd *cmd)
if (scst_cmd_is_expected_set(cmd)) {
#ifdef CONFIG_SCST_USE_EXPECTED_VALUES
# ifdef CONFIG_SCST_EXTRACHECKS
if ((cmd->data_direction != cmd->expected_data_direction) ||
(cmd->bufflen != cmd->expected_transfer_len)) {
if (unlikely((cmd->data_direction != cmd->expected_data_direction) ||
(cmd->bufflen != cmd->expected_transfer_len))) {
TRACE(TRACE_MINOR, "Expected values don't match "
"decoded ones: data_direction %d, "
"expected_data_direction %d, "
+1 -1
View File
@@ -1,7 +1,7 @@
User space FILEIO handler
=========================
Version 1.0.2, XX XXXXX 2009
Version 2.0.0, XX XXXXX 2010
----------------------------
User space program fileio_tgt uses interface of SCST's scst_user dev
+1 -1
View File
@@ -22,7 +22,7 @@
/* 8 byte ASCII Vendor */
#define VENDOR "SCST_USR"
/* 4 byte ASCII Product Revision Level - left aligned */
#define FIO_REV " 102"
#define FIO_REV " 200"
#define MAX_USN_LEN (20+1) /* For '\0' */
+1 -1
View File
@@ -66,7 +66,7 @@ unsigned long trace_flag = DEFAULT_LOG_FLAGS;
#endif /* defined(DEBUG) || defined(TRACING) */
#define DEF_BLOCK_SHIFT 9
#define VERSION_STR "1.0.2"
#define VERSION_STR "2.0.0"
#define THREADS 7
#define MAX_VDEVS 10