From 4d96089e50923e5e6ddb597d67c78a149193e046 Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Wed, 17 Feb 2010 19:07:54 +0000 Subject: [PATCH] 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 --- iscsi-scst/ChangeLog-IET | 461 -------------- iscsi-scst/README | 449 ++++++++++++- iscsi-scst/README-IET | 105 ---- iscsi-scst/README_in-tree | 473 ++++++++++++-- iscsi-scst/ToDo | 19 +- iscsi-scst/include/iscsi_scst.h | 133 ++-- iscsi-scst/include/iscsi_scst_ver.h | 2 +- iscsi-scst/kernel/config.c | 936 ++++++++++++++++++++++------ iscsi-scst/kernel/conn.c | 22 +- iscsi-scst/kernel/event.c | 68 +- iscsi-scst/kernel/iscsi.c | 47 +- iscsi-scst/kernel/iscsi.h | 97 +-- iscsi-scst/kernel/nthread.c | 5 +- iscsi-scst/kernel/param.c | 341 +++++----- iscsi-scst/kernel/session.c | 219 +++++-- iscsi-scst/kernel/target.c | 397 ++++++------ iscsi-scst/usr/Makefile | 5 +- iscsi-scst/usr/chap.c | 26 +- iscsi-scst/usr/config.c | 912 ++++++++++++++++++++------- iscsi-scst/usr/conn.c | 10 +- iscsi-scst/usr/ctldev.c | 253 +++++++- iscsi-scst/usr/event.c | 734 ++++++++++++++++++++-- iscsi-scst/usr/iscsi_adm.c | 57 +- iscsi-scst/usr/iscsi_adm.h | 4 +- iscsi-scst/usr/iscsi_scstd.c | 44 +- iscsi-scst/usr/iscsid.c | 105 ++-- iscsi-scst/usr/iscsid.h | 95 ++- iscsi-scst/usr/isns.c | 124 ++-- iscsi-scst/usr/log.c | 6 +- iscsi-scst/usr/message.c | 20 +- iscsi-scst/usr/param.c | 167 +++-- iscsi-scst/usr/param.h | 24 +- iscsi-scst/usr/session.c | 24 +- iscsi-scst/usr/target.c | 108 ++-- qla2x00t/qla2x00-target/README | 4 +- qla2x00t/qla2x00-target/qla2x00t.c | 4 +- scst/README | 2 +- scst/include/scst.h | 102 ++- scst/include/scst_const.h | 4 +- scst/include/scst_user.h | 2 +- scst/src/dev_handlers/scst_user.c | 11 +- scst/src/dev_handlers/scst_vdisk.c | 6 +- scst/src/scst_lib.c | 4 +- scst/src/scst_main.c | 20 +- scst/src/scst_mem.c | 3 +- scst/src/scst_proc.c | 6 +- scst/src/scst_sysfs.c | 329 +++++++++- scst/src/scst_targ.c | 5 +- usr/fileio/README | 2 +- usr/fileio/common.h | 2 +- usr/fileio/fileio.c | 2 +- 51 files changed, 4956 insertions(+), 2044 deletions(-) delete mode 100644 iscsi-scst/ChangeLog-IET delete mode 100644 iscsi-scst/README-IET diff --git a/iscsi-scst/ChangeLog-IET b/iscsi-scst/ChangeLog-IET deleted file mode 100644 index 5433baace..000000000 --- a/iscsi-scst/ChangeLog-IET +++ /dev/null @@ -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 diff --git a/iscsi-scst/README b/iscsi-scst/README index f6fae45cc..7e47ccab6 100644 --- a/iscsi-scst/README +++ b/iscsi-scst/README @@ -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-.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 for fixes * Krzysztof Blaszkowski for many fixes diff --git a/iscsi-scst/README-IET b/iscsi-scst/README-IET deleted file mode 100644 index 83829c981..000000000 --- a/iscsi-scst/README-IET +++ /dev/null @@ -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= - -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= 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 . - - -Developer Notes ----------------- -The central resource for IET development is the - 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. diff --git a/iscsi-scst/README_in-tree b/iscsi-scst/README_in-tree index 2b2020900..e3e4d1c31 100644 --- a/iscsi-scst/README_in-tree +++ b/iscsi-scst/README_in-tree @@ -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 for fixes * Krzysztof Blaszkowski for many fixes diff --git a/iscsi-scst/ToDo b/iscsi-scst/ToDo index 5754d42b4..e9624d941 100644 --- a/iscsi-scst/ToDo +++ b/iscsi-scst/ToDo @@ -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. diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index 6aa9fddd6..be008e401 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -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) { diff --git a/iscsi-scst/include/iscsi_scst_ver.h b/iscsi-scst/include/iscsi_scst_ver.h index fd3f8a19e..795348083 100644 --- a/iscsi-scst/include/iscsi_scst_ver.h +++ b/iscsi-scst/include/iscsi_scst_ver.h @@ -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 diff --git a/iscsi-scst/kernel/config.c b/iscsi-scst/kernel/config.c index c7f7144af..a3ba5c810 100644 --- a/iscsi-scst/kernel/config.c +++ b/iscsi-scst/kernel/config.c @@ -15,6 +15,9 @@ #include "iscsi.h" +/* Protected by target_mgmt_mutex */ +int ctr_open_state; + #ifdef CONFIG_SCST_PROC #include @@ -61,7 +64,8 @@ static ssize_t iscsi_proc_log_entry_write(struct file *file, TRACE_EXIT_RES(res); return res; } -#endif + +#endif /* DEBUG or TRACE */ static int iscsi_version_info_show(struct seq_file *seq, void *v) { @@ -230,6 +234,9 @@ err: #else /* CONFIG_SCST_PROC */ +/* Protected by target_mgmt_mutex */ +static LIST_HEAD(iscsi_attrs_list); + static ssize_t iscsi_version_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -260,93 +267,236 @@ static ssize_t iscsi_version_show(struct kobject *kobj, static struct kobj_attribute iscsi_version_attr = __ATTR(version, S_IRUGO, iscsi_version_show, NULL); +static ssize_t iscsi_open_state_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + switch (ctr_open_state) { + case ISCSI_CTR_OPEN_STATE_CLOSED: + sprintf(buf, "%s\n", "closed"); + break; + case ISCSI_CTR_OPEN_STATE_OPEN: + sprintf(buf, "%s\n", "open"); + break; + case ISCSI_CTR_OPEN_STATE_CLOSING: + sprintf(buf, "%s\n", "closing"); + break; + default: + sprintf(buf, "%s\n", "unknown"); + break; + } + + return strlen(buf); +} + +static struct kobj_attribute iscsi_open_state_attr = + __ATTR(open_state, S_IRUGO, iscsi_open_state_show, NULL); + const struct attribute *iscsi_attrs[] = { &iscsi_version_attr.attr, + &iscsi_open_state_attr.attr, NULL, }; #endif /* CONFIG_SCST_PROC */ -/* target_mutex supposed to be locked */ -static int add_conn(struct iscsi_target *target, void __user *ptr) -{ - int err; - struct iscsi_session *session; - struct iscsi_kern_conn_info info; - - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; - - session = session_lookup(target, info.sid); - if (!session) - return -ENOENT; - - return conn_add(session, &info); -} - -/* target_mutex supposed to be locked */ -static int del_conn(struct iscsi_target *target, void __user *ptr) -{ - int err; - struct iscsi_session *session; - struct iscsi_kern_conn_info info; - - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; - - session = session_lookup(target, info.sid); - if (!session) { - PRINT_ERROR("Session %llx not found", - (long long unsigned int)info.sid); - return -ENOENT; - } - - return conn_del(session, &info); -} - /* target_mgmt_mutex supposed to be locked */ -static int add_session(struct iscsi_target *target, void __user *ptr) +static int add_conn(void __user *ptr) { int err; - struct iscsi_kern_session_info info; + struct iscsi_session *session; + struct iscsi_kern_conn_info info; + struct iscsi_target *target; - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; - - info.initiator_name[ISCSI_NAME_LEN-1] = '\0'; - info.user_name[ISCSI_NAME_LEN-1] = '\0'; - - return session_add(target, &info); -} - -/* target_mutex supposed to be locked */ -static int del_session(struct iscsi_target *target, void __user *ptr) -{ - int err; - struct iscsi_kern_session_info info; - - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; - - return session_del(target, info.sid); -} - -/* target_mutex supposed to be locked */ -static int iscsi_param_config(struct iscsi_target *target, void __user *ptr, - int set) -{ - int err; - struct iscsi_kern_param_info info; + TRACE_ENTRY(); err = copy_from_user(&info, ptr, sizeof(info)); if (err < 0) goto out; - err = iscsi_param_set(target, &info, set); + target = target_lookup_by_id(info.tid); + if (target == NULL) { + PRINT_ERROR("Target %d not found", info.tid); + err = -ENOENT; + goto out; + } + + mutex_lock(&target->target_mutex); + + session = session_lookup(target, info.sid); + if (!session) { + PRINT_ERROR("Session %lld not found", + (long long unsigned int)info.tid); + err = -ENOENT; + goto out_unlock; + } + + err = __add_conn(session, &info); + +out_unlock: + mutex_unlock(&target->target_mutex); + +out: + TRACE_EXIT_RES(err); + return err; +} + +/* target_mgmt_mutex supposed to be locked */ +static int del_conn(void __user *ptr) +{ + int err; + struct iscsi_session *session; + struct iscsi_kern_conn_info info; + struct iscsi_target *target; + + TRACE_ENTRY(); + + err = copy_from_user(&info, ptr, sizeof(info)); + if (err < 0) + goto out; + + target = target_lookup_by_id(info.tid); + if (target == NULL) { + PRINT_ERROR("Target %d not found", info.tid); + err = -ENOENT; + goto out; + } + + mutex_lock(&target->target_mutex); + + session = session_lookup(target, info.sid); + if (!session) { + PRINT_ERROR("Session %llx not found", + (long long unsigned int)info.sid); + err = -ENOENT; + goto out_unlock; + } + + err = __del_conn(session, &info); + +out_unlock: + mutex_unlock(&target->target_mutex); + +out: + TRACE_EXIT_RES(err); + return err; +} + +/* target_mgmt_mutex supposed to be locked */ +static int add_session(void __user *ptr) +{ + int err; + struct iscsi_kern_session_info *info; + struct iscsi_target *target; + + TRACE_ENTRY(); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info)); + err = -ENOMEM; + goto out; + } + + err = copy_from_user(info, ptr, sizeof(*info)); + if (err != 0) { + PRINT_ERROR("copy_from_user() didn't copy %d bytes", err); + err = -EFAULT; + goto out_free; + } + + info->initiator_name[sizeof(info->initiator_name)-1] = '\0'; +#ifdef CONFIG_SCST_PROC + info->user_name[sizeof(info->user_name)-1] = '\0'; +#endif + + target = target_lookup_by_id(info->tid); + if (target == NULL) { + PRINT_ERROR("Target %d not found", info->tid); + err = -ENOENT; + goto out_free; + } + + err = __add_session(target, info); + +out_free: + kfree(info); + +out: + TRACE_EXIT_RES(err); + return err; +} + +/* target_mgmt_mutex supposed to be locked */ +static int del_session(void __user *ptr) +{ + int err; + struct iscsi_kern_session_info *info; + struct iscsi_target *target; + + TRACE_ENTRY(); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info)); + err = -ENOMEM; + goto out; + } + + err = copy_from_user(info, ptr, sizeof(*info)); + if (err != 0) { + PRINT_ERROR("copy_from_user() didn't copy %d bytes", err); + err = -EFAULT; + goto out_free; + } + + info->initiator_name[sizeof(info->initiator_name)-1] = '\0'; +#ifdef CONFIG_SCST_PROC + info->user_name[sizeof(info->user_name)-1] = '\0'; +#endif + + target = target_lookup_by_id(info->tid); + if (target == NULL) { + PRINT_ERROR("Target %d not found", info->tid); + err = -ENOENT; + goto out_free; + } + + mutex_lock(&target->target_mutex); + err = __del_session(target, info->sid); + mutex_unlock(&target->target_mutex); + +out_free: + kfree(info); + +out: + TRACE_EXIT_RES(err); + return err; +} + +/* target_mgmt_mutex supposed to be locked */ +static int iscsi_params_config(void __user *ptr, int set) +{ + int err; + struct iscsi_kern_params_info info; + struct iscsi_target *target; + + TRACE_ENTRY(); + + err = copy_from_user(&info, ptr, sizeof(info)); + if (err < 0) + goto out; + + target = target_lookup_by_id(info.tid); + if (target == NULL) { + PRINT_ERROR("Target %d not found", info.tid); + err = -ENOENT; + goto out; + } + + mutex_lock(&target->target_mutex); + err = iscsi_params_set(target, &info, set); + mutex_unlock(&target->target_mutex); + if (err < 0) goto out; @@ -354,72 +504,473 @@ static int iscsi_param_config(struct iscsi_target *target, void __user *ptr, err = copy_to_user(ptr, &info, sizeof(info)); out: + TRACE_EXIT_RES(err); return err; } +#ifndef CONFIG_SCST_PROC + +/* target_mgmt_mutex supposed to be locked */ +static int mgmt_cmd_callback(void __user *ptr) +{ + int err = 0, rc; + struct iscsi_kern_mgmt_cmd_res_info cinfo; + struct scst_sysfs_user_info *info; + + TRACE_ENTRY(); + + rc = copy_from_user(&cinfo, ptr, sizeof(cinfo)); + if (rc != 0) { + err = -EFAULT; + goto out; + } + + cinfo.value[sizeof(cinfo.value)-1] = '\0'; + + info = scst_sysfs_user_get_info(cinfo.cookie); + TRACE_DBG("cookie %u, info %p, result %d", cinfo.cookie, info, + cinfo.result); + if (info == NULL) { + err = -EINVAL; + goto out; + } + + info->info_status = 0; + + if (cinfo.result != 0) { + info->info_status = cinfo.result; + goto out_complete; + } + + switch (cinfo.req_cmd) { + case E_ENABLE_TARGET: + case E_DISABLE_TARGET: + { + struct iscsi_target *target; + + target = target_lookup_by_id(cinfo.tid); + if (target == NULL) { + PRINT_ERROR("Target %d not found", cinfo.tid); + err = -ENOENT; + goto out_status; + } + + target->tgt_enabled = (cinfo.req_cmd == E_ENABLE_TARGET) ? 1 : 0; + break; + } + + case E_GET_ATTR_VALUE: + info->data = kstrdup(cinfo.value, GFP_KERNEL); + if (info->data == NULL) { + PRINT_ERROR("Can't dublicate value %s", cinfo.value); + info->info_status = -ENOMEM; + goto out_complete; + } + break; + } + +out_complete: + complete(&info->info_completion); + +out: + TRACE_EXIT_RES(err); + return err; + +out_status: + info->info_status = err; + goto out_complete; +} + +static ssize_t iscsi_attr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int pos; + struct iscsi_attr *tgt_attr; + void *value; + + TRACE_ENTRY(); + + tgt_attr = container_of(attr, struct iscsi_attr, attr); + + pos = iscsi_sysfs_send_event( + (tgt_attr->target != NULL) ? tgt_attr->target->tid : 0, + E_GET_ATTR_VALUE, tgt_attr->name, NULL, &value); + + if (pos != 0) + goto out; + + pos = scnprintf(buf, SCST_SYSFS_BLOCK_SIZE, "%s\n", (char *)value); + + kfree(value); + +out: + TRACE_EXIT_RES(pos); + return pos; +} + +static ssize_t iscsi_attr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int res; + char *buffer; + struct iscsi_attr *tgt_attr; + + TRACE_ENTRY(); + + buffer = kzalloc(count+1, GFP_KERNEL); + if (buffer == NULL) { + res = -ENOMEM; + goto out; + } + + memcpy(buffer, buf, count); + buffer[count] = '\0'; + + tgt_attr = container_of(attr, struct iscsi_attr, attr); + + res = iscsi_sysfs_send_event( + (tgt_attr->target != NULL) ? tgt_attr->target->tid : 0, + E_SET_ATTR_VALUE, tgt_attr->name, buffer, NULL); + + kfree(buffer); + + if (res == 0) + res = count; + +out: + TRACE_EXIT_RES(res); + return res; +} + +/* + * target_mgmt_mutex supposed to be locked. If target != 0, target_mutex + * supposed to be locked as well. + */ +int iscsi_add_attr(struct iscsi_target *target, + const struct iscsi_kern_attr *attr_info) +{ + int res = 0; + struct iscsi_attr *tgt_attr; + struct list_head *attrs_list; + const char *name; + + TRACE_ENTRY(); + + if (target != NULL) { + attrs_list = &target->attrs_list; + name = target->name; + } else { + attrs_list = &iscsi_attrs_list; + name = "global"; + } + + list_for_each_entry(tgt_attr, attrs_list, attrs_list_entry) { + if (strncmp(tgt_attr->name, attr_info->name, + sizeof(tgt_attr->name) == 0)) { + PRINT_ERROR("Attribute %s for %s already exist", + attr_info->name, name); + res = -EEXIST; + goto out; + } + } + + TRACE_DBG("Adding %s's attr %s with mode %x", name, + attr_info->name, attr_info->mode); + + tgt_attr = kzalloc(sizeof(*tgt_attr), GFP_KERNEL); + if (tgt_attr == NULL) { + PRINT_ERROR("Unable to allocate user (size %d)", + sizeof(*tgt_attr)); + res = -ENOMEM; + goto out; + } + + tgt_attr->target = target; + + tgt_attr->name = kstrdup(attr_info->name, GFP_KERNEL); + if (tgt_attr->name == NULL) { + PRINT_ERROR("Unable to allocate attr %s name/value (target %s)", + attr_info->name, name); + res = -ENOMEM; + goto out_free; + } + + list_add(&tgt_attr->attrs_list_entry, attrs_list); + + tgt_attr->attr.attr.name = tgt_attr->name; + tgt_attr->attr.attr.owner = THIS_MODULE; + tgt_attr->attr.attr.mode = attr_info->mode & (S_IRUGO | S_IWUGO); + tgt_attr->attr.show = iscsi_attr_show; + tgt_attr->attr.store = iscsi_attr_store; + + res = sysfs_create_file( + (target != NULL) ? scst_sysfs_get_tgt_kobj(target->scst_tgt) : + scst_sysfs_get_tgtt_kobj(&iscsi_template), + &tgt_attr->attr.attr); + if (res != 0) { + PRINT_ERROR("Unable to create file '%s' for target '%s'", + tgt_attr->attr.attr.name, name); + goto out_del; + } + +out: + TRACE_EXIT_RES(res); + return res; + +out_del: + list_del(&tgt_attr->attrs_list_entry); + +out_free: + kfree(tgt_attr->name); + kfree(tgt_attr); + goto out; +} + +void __iscsi_del_attr(struct iscsi_target *target, + struct iscsi_attr *tgt_attr) +{ + TRACE_ENTRY(); + + TRACE_DBG("Deleting %s's attr %s", + (target != NULL) ? target->name : "global", tgt_attr->name); + + list_del(&tgt_attr->attrs_list_entry); + + sysfs_remove_file((target != NULL) ? + scst_sysfs_get_tgt_kobj(target->scst_tgt) : + scst_sysfs_get_tgtt_kobj(&iscsi_template), + &tgt_attr->attr.attr); + + kfree(tgt_attr->name); + kfree(tgt_attr); + + TRACE_EXIT(); + return; +} + +/* + * target_mgmt_mutex supposed to be locked. If target != 0, target_mutex + * supposed to be locked as well. + */ +static int iscsi_del_attr(struct iscsi_target *target, + const char *attr_name) +{ + int res = 0; + struct iscsi_attr *tgt_attr, *a; + struct list_head *attrs_list; + + TRACE_ENTRY(); + + if (target != NULL) + attrs_list = &target->attrs_list; + else + attrs_list = &iscsi_attrs_list; + + tgt_attr = NULL; + list_for_each_entry(a, attrs_list, attrs_list_entry) { + if (strncmp(a->name, attr_name, sizeof(a->name)) == 0) { + tgt_attr = a; + break; + } + } + + if (tgt_attr == NULL) { + PRINT_ERROR("attr %s not found (target %s)", attr_name, + (target != NULL) ? target->name : "global"); + res = -ENOENT; + goto out; + } + + __iscsi_del_attr(target, tgt_attr); + +out: + TRACE_EXIT_RES(res); + return res; +} + /* target_mgmt_mutex supposed to be locked */ -static int enable_target(void __user *ptr) +static int iscsi_attr_cmd(void __user *ptr, unsigned int cmd) { - int err; - struct iscsi_kern_target_info info; + int rc, err = 0; + struct iscsi_kern_attr_info info; + struct iscsi_target *target; + struct scst_sysfs_user_info *i = NULL; - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; + TRACE_ENTRY(); - err = target_enable(&info); + rc = copy_from_user(&info, ptr, sizeof(info)); + if (rc != 0) { + err = -EFAULT; + goto out; + } + info.attr.name[sizeof(info.attr.name)-1] = '\0'; + + if (info.cookie != 0) { + i = scst_sysfs_user_get_info(info.cookie); + TRACE_DBG("cookie %u, uinfo %p", info.cookie, i); + if (i == NULL) { + err = -EINVAL; + goto out; + } + } + + target = target_lookup_by_id(info.tid); + + if (target != NULL) + mutex_lock(&target->target_mutex); + + switch (cmd) { + case ISCSI_ATTR_ADD: + err = iscsi_add_attr(target, &info.attr); + break; + case ISCSI_ATTR_DEL: + err = iscsi_del_attr(target, info.attr.name); + break; + default: + sBUG(); + } + + if (target != NULL) + mutex_unlock(&target->target_mutex); + + if (i != NULL) { + i->info_status = err; + complete(&i->info_completion); + } + +out: + TRACE_EXIT_RES(err); return err; } -/* target_mgmt_mutex supposed to be locked */ -static int disable_target(void __user *ptr) -{ - int err; - struct iscsi_kern_target_info info; - - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; - - err = target_disable(&info); - - return err; -} +#endif /* CONFIG_SCST_PROC */ /* target_mgmt_mutex supposed to be locked */ static int add_target(void __user *ptr) { int err; - struct iscsi_kern_target_info info; + struct iscsi_kern_target_info *info; +#ifndef CONFIG_SCST_PROC + struct scst_sysfs_user_info *uinfo; +#endif - err = copy_from_user(&info, ptr, sizeof(info)); - if (err < 0) - return err; + TRACE_ENTRY(); - err = target_add(&info); - if (!err) - err = copy_to_user(ptr, &info, sizeof(info)); - - return err; -} - -static int iscsi_check_version(void __user *arg) -{ - struct iscsi_kern_register_info reg; - char ver[sizeof(ISCSI_SCST_INTERFACE_VERSION)+1]; - int res; - - res = copy_from_user(®, arg, sizeof(reg)); - if (res < 0) { - PRINT_ERROR("%s", "Unable to get register info"); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + PRINT_ERROR("Can't alloc info (size %d)", sizeof(*info)); + err = -ENOMEM; goto out; } - res = copy_from_user(ver, (void __user *)(unsigned long)reg.version, + err = copy_from_user(info, ptr, sizeof(*info)); + if (err != 0) { + PRINT_ERROR("copy_from_user() didn't copy %d bytes", err); + err = -EFAULT; + goto out_free; + } + + if (target_lookup_by_id(info->tid) != NULL) { + PRINT_ERROR("Target %u already exist!", info->tid); + err = -EEXIST; + goto out_free; + } + + info->name[sizeof(info->name)-1] = '\0'; + +#ifndef CONFIG_SCST_PROC + if (info->cookie != 0) { + uinfo = scst_sysfs_user_get_info(info->cookie); + TRACE_DBG("cookie %u, uinfo %p", info->cookie, uinfo); + if (uinfo == NULL) { + err = -EINVAL; + goto out_free; + } + } else + uinfo = NULL; +#endif + + err = __add_target(info); + +#ifndef CONFIG_SCST_PROC + if (uinfo != NULL) { + uinfo->info_status = err; + complete(&uinfo->info_completion); + } +#endif + +out_free: + kfree(info); + +out: + TRACE_EXIT_RES(err); + return err; +} + +/* target_mgmt_mutex supposed to be locked */ +static int del_target(void __user *ptr) +{ + int err; + struct iscsi_kern_target_info info; +#ifndef CONFIG_SCST_PROC + struct scst_sysfs_user_info *uinfo; +#endif + + TRACE_ENTRY(); + + err = copy_from_user(&info, ptr, sizeof(info)); + if (err < 0) + goto out; + + info.name[sizeof(info.name)-1] = '\0'; + +#ifndef CONFIG_SCST_PROC + if (info.cookie != 0) { + uinfo = scst_sysfs_user_get_info(info.cookie); + TRACE_DBG("cookie %u, uinfo %p", info.cookie, uinfo); + if (uinfo == NULL) { + err = -EINVAL; + goto out; + } + } else + uinfo = NULL; +#endif + + err = __del_target(info.tid); + +#ifndef CONFIG_SCST_PROC + if (uinfo != NULL) { + uinfo->info_status = err; + complete(&uinfo->info_completion); + } +#endif + +out: + TRACE_EXIT_RES(err); + return err; +} + +static int iscsi_register(void __user *arg) +{ + struct iscsi_kern_register_info reg; + char ver[sizeof(ISCSI_SCST_INTERFACE_VERSION)+1]; + int res, rc; + + TRACE_ENTRY(); + + rc = copy_from_user(®, arg, sizeof(reg)); + if (rc != 0) { + PRINT_ERROR("%s", "Unable to get register info"); + res = -EFAULT; + goto out; + } + + rc = copy_from_user(ver, (void __user *)(unsigned long)reg.version, sizeof(ver)); - if (res < 0) { + if (rc < 0) { PRINT_ERROR("%s", "Unable to get version string"); + res = -EFAULT; goto out; } ver[sizeof(ver)-1] = '\0'; @@ -431,125 +982,141 @@ static int iscsi_check_version(void __user *arg) goto out; } - res = ISCSI_CONN_IOV_MAX << PAGE_SHIFT; + memset(®, 0, sizeof(reg)); + reg.max_data_seg_len = ISCSI_CONN_IOV_MAX << PAGE_SHIFT; + + res = 0; + + rc = copy_to_user(arg, ®, sizeof(reg)); + if (rc != 0) { + PRINT_ERROR("copy_to_user() failed to copy %d bytes", res); + res = -EFAULT; + goto out; + } out: + TRACE_EXIT_RES(res); return res; } static long ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct iscsi_target *target = NULL; long err; - u32 id; - switch (cmd) { - case ADD_TARGET: - case DEL_TARGET: - case ENABLE_TARGET: - case DISABLE_TARGET: - case ADD_SESSION: - case DEL_SESSION: - case ISCSI_PARAM_SET: - case ISCSI_PARAM_GET: - case ADD_CONN: - case DEL_CONN: - break; + TRACE_ENTRY(); - case REGISTER_USERD: - err = iscsi_check_version((void __user *) arg); - goto out; - - default: - PRINT_ERROR("Invalid ioctl cmd %x", cmd); - err = -EINVAL; + if (cmd == REGISTER_USERD) { + err = iscsi_register((void __user *)arg); goto out; } - err = get_user(id, (u32 __user *) arg); - if (err != 0) - goto out; - err = mutex_lock_interruptible(&target_mgmt_mutex); if (err < 0) goto out; - if (cmd == DEL_TARGET) { - err = target_del(id); - goto out_unlock; - } - - target = target_lookup_by_id(id); - - if (cmd == ADD_TARGET) - if (target) { - err = -EEXIST; - PRINT_ERROR("Target %u already exist!", id); - goto out_unlock; - } - switch (cmd) { case ADD_TARGET: - err = add_target((void __user *) arg); - goto out_unlock; - case ENABLE_TARGET: - err = enable_target((void __user *) arg); - goto out_unlock; - case DISABLE_TARGET: - err = disable_target((void __user *) arg); - goto out_unlock; + err = add_target((void __user *)arg); + break; + + case DEL_TARGET: + err = del_target((void __user *)arg); + break; + +#ifndef CONFIG_SCST_PROC + case ISCSI_ATTR_ADD: + case ISCSI_ATTR_DEL: + err = iscsi_attr_cmd((void __user *)arg, cmd); + break; + + case MGMT_CMD_CALLBACK: + err = mgmt_cmd_callback((void __user *)arg); + break; +#endif + case ADD_SESSION: - err = add_session(target, (void __user *) arg); - goto out_unlock; - } + err = add_session((void __user *)arg); + break; - if (!target) { - PRINT_ERROR("Can't find the target %u", id); - err = -EINVAL; - goto out_unlock; - } - - mutex_lock(&target->target_mutex); - - switch (cmd) { case DEL_SESSION: - err = del_session(target, (void __user *) arg); + err = del_session((void __user *)arg); break; case ISCSI_PARAM_SET: - err = iscsi_param_config(target, (void __user *) arg, 1); + err = iscsi_params_config((void __user *)arg, 1); break; case ISCSI_PARAM_GET: - err = iscsi_param_config(target, (void __user *) arg, 0); + err = iscsi_params_config((void __user *)arg, 0); break; case ADD_CONN: - err = add_conn(target, (void __user *) arg); + err = add_conn((void __user *)arg); break; case DEL_CONN: - err = del_conn(target, (void __user *) arg); + err = del_conn((void __user *)arg); break; default: - sBUG(); - break; + PRINT_ERROR("Invalid ioctl cmd %x", cmd); + err = -EINVAL; + goto out_unlock; } - mutex_unlock(&target->target_mutex); - out_unlock: mutex_unlock(&target_mgmt_mutex); out: + TRACE_EXIT_RES(err); return err; } +int open(struct inode *inode, struct file *file) +{ + bool already; + + mutex_lock(&target_mgmt_mutex); + already = (ctr_open_state != ISCSI_CTR_OPEN_STATE_CLOSED); + if (!already) + ctr_open_state = ISCSI_CTR_OPEN_STATE_OPEN; + mutex_unlock(&target_mgmt_mutex); + + if (already) { + PRINT_WARNING("%s", "Attempt to second open the control " + "device!"); + return -EBUSY; + } else + return 0; +} + static int release(struct inode *inode, struct file *filp) { +#ifndef CONFIG_SCST_PROC + struct iscsi_attr *attr, *t; +#endif + TRACE(TRACE_MGMT, "%s", "Releasing allocated resources"); + + mutex_lock(&target_mgmt_mutex); + ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSING; + mutex_unlock(&target_mgmt_mutex); + target_del_all(); + + mutex_lock(&target_mgmt_mutex); + +#ifndef CONFIG_SCST_PROC + list_for_each_entry_safe(attr, t, &iscsi_attrs_list, + attrs_list_entry) { + __iscsi_del_attr(NULL, attr); + } +#endif + + ctr_open_state = ISCSI_CTR_OPEN_STATE_CLOSED; + + mutex_unlock(&target_mgmt_mutex); + return 0; } @@ -557,6 +1124,7 @@ const struct file_operations ctr_fops = { .owner = THIS_MODULE, .unlocked_ioctl = ioctl, .compat_ioctl = ioctl, + .open = open, .release = release, }; diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index ca2f0ae11..8a5eefe26 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -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; diff --git a/iscsi-scst/kernel/event.c b/iscsi-scst/kernel/event.c index 4453fde13..d450dea7a 100644 --- a/iscsi-scst/kernel/event.c +++ b/iscsi-scst/kernel/event.c @@ -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; } diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 826c0e311..68ec27801 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -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) diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 89e9d734e..d3d157195 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -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); diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 998ab0ca7..6bb9a384e 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -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); diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c index 782438d7e..ce08437c5 100644 --- a/iscsi-scst/kernel/param.c +++ b/iscsi-scst/kernel/param.c @@ -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; } diff --git a/iscsi-scst/kernel/session.c b/iscsi-scst/kernel/session.c index 49c291cd0..9d26c7c10 100644 --- a/iscsi-scst/kernel/session.c +++ b/iscsi-scst/kernel/session.c @@ -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, }; diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index 7f56b13a6..b8043d808 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -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 */ diff --git a/iscsi-scst/usr/Makefile b/iscsi-scst/usr/Makefile index 451f960a6..1b056190a 100644 --- a/iscsi-scst/usr/Makefile +++ b/iscsi-scst/usr/Makefile @@ -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 diff --git a/iscsi-scst/usr/chap.c b/iscsi-scst/usr/chap.c index 5af7c28ca..a538350b5 100644 --- a/iscsi-scst/usr/chap.c +++ b/iscsi-scst/usr/chap.c @@ -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; diff --git a/iscsi-scst/usr/config.c b/iscsi-scst/usr/config.c index e7ff36bd7..5d27ca555 100644 --- a/iscsi-scst/usr/config.c +++ b/iscsi-scst/usr/config.c @@ -17,9 +17,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -32,39 +34,9 @@ #define BUFSIZE 4096 #define CONFIG_FILE "/etc/iscsi-scstd.conf" -#define ACCT_CONFIG_FILE CONFIG_FILE -/* - * Account configuration code - */ - -struct user { - struct __qelem ulist; - - u32 tid; - char *name; - char *password; -}; - -/* this is the orignal Ardis code. */ -static char *target_sep_string(char **pp) -{ - char *p = *pp; - char *q; - - for (p = *pp; isspace(*p); p++) - ; - for (q = p; *q && !isspace(*q); q++) - ; - if (*q) - *q++ = 0; - else - p = NULL; - *pp = q; - return p; -} - -static struct iscsi_key user_keys[] = { +/* Index must match ISCSI_USER_DIR_*!! */ +struct iscsi_key user_keys[] = { {"IncomingUser",}, {"OutgoingUser",}, {NULL,}, @@ -73,78 +45,87 @@ static struct iscsi_key user_keys[] = { static struct __qelem discovery_users_in = LIST_HEAD_INIT(discovery_users_in); static struct __qelem discovery_users_out = LIST_HEAD_INIT(discovery_users_out); -#define HASH_ORDER 4 -#define acct_hash(x) ((x) & ((1 << HASH_ORDER) - 1)) - -static struct __qelem trgt_acct_in[1 << HASH_ORDER]; -static struct __qelem trgt_acct_out[1 << HASH_ORDER]; - -static struct __qelem *account_list_get(u32 tid, int dir) +static struct __qelem *account_list_get(struct target *target, int dir) { struct __qelem *list = NULL; - if (tid) { - list = (dir == AUTH_DIR_INCOMING) ? - &trgt_acct_in[acct_hash(tid)] : &trgt_acct_out[acct_hash(tid)]; + if (target != NULL) { + list = (dir == ISCSI_USER_DIR_INCOMING) ? + &target->target_in_accounts : &target->target_out_accounts; } else - list = (dir == AUTH_DIR_INCOMING) ? + list = (dir == ISCSI_USER_DIR_INCOMING) ? &discovery_users_in : &discovery_users_out; return list; } -static int config_account_init(char *filename) +char *config_sep_string(char **pp) { - FILE *fp; - char buf[BUFSIZE], *p, *q; - u32 tid; - int idx, res = 0; + char *p = *pp; + char *q; + static char blank = '\0'; - if (!(fp = fopen(filename, "r"))) { - return errno == ENOENT ? 0 : -errno; - } + if ((pp == NULL) || (*pp == NULL)) + return ␣ - tid = 0; - while (fgets(buf, sizeof(buf), fp)) { - q = buf; - p = target_sep_string(&q); - if (!p || *p == '#') - continue; + for (p = *pp; isspace(*p) || (*p == '='); p++) + ; - if (!strcasecmp(p, "Target")) { - tid = 0; - if (!(p = target_sep_string(&q))) - continue; - tid = target_find_id_by_name(p); - } else if (!((idx = param_index_by_name(p, user_keys)) < 0)) { - char *name, *pass; - name = target_sep_string(&q); - pass = target_sep_string(&q); + for (q = p; (*q != '\0') && !isspace(*q) && (*q != '='); q++) + ; - res = config_account_add(tid, idx, name, pass); - if (res < 0) { - log_error("%s %s\n", name, pass); - break; - } - } - } + if (*q != '\0') + *q++ = '\0'; - fclose(fp); - - return res; + *pp = q; + return p; } -/* Return the first account if the length of name is zero */ -static struct user *account_lookup_by_name(u32 tid, int dir, char *name) +static char *config_gets(char *buf, int size, const char *data, int *offset) { - struct __qelem *list = account_list_get(tid, dir); - struct user *user = NULL; + int offs = *offset, i = 0; + + while ((i < size-1) && (data[offs] != '\n') && (data[offs] != ';') && (data[offs] != '\0')) + buf[i++] = data[offs++]; + + if ((i == 0) && (data[offs] == '\0')) + return NULL; + + if (data[offs] != '\0') + offs++; + + *offset = offs; + buf[i] = '\0'; + + return buf; +} + +int accounts_empty(u32 tid, int dir) +{ + struct target *target; + struct __qelem *list; + + if (tid) { + target = target_find_by_id(tid); + if (target == NULL) + return 0; + } else + target = NULL; + + list = account_list_get(target, dir); + + return list_empty(list); +} + +static struct iscsi_user *__account_lookup_by_name(struct target *target, + int dir, const char *name) +{ + struct __qelem *list; + struct iscsi_user *user = NULL; + + list = account_list_get(target, dir); list_for_each_entry(user, list, ulist) { - if (user->tid != tid) - continue; - if (!strlen(name)) - return user; if (!strcmp(user->name, name)) return user; } @@ -152,17 +133,67 @@ static struct user *account_lookup_by_name(u32 tid, int dir, char *name) return NULL; } -int config_account_query(u32 tid, int dir, char *name, char *pass) + +static struct iscsi_user *account_lookup_by_name(u32 tid, int dir, const char *name) { - struct user *user; + struct target *target; + + if (tid) { + target = target_find_by_id(tid); + if (target == NULL) + return NULL; + } else + target = NULL; + + return __account_lookup_by_name(target, dir, name); +} + +struct iscsi_user *account_get_first(u32 tid, int dir) +{ + struct target *target; + struct __qelem *list; + struct iscsi_user *user = NULL; + + if (tid) { + target = target_find_by_id(tid); + if (target == NULL) + return NULL; + } else + target = NULL; + + list = account_list_get(target, dir); + + list_for_each_entry(user, list, ulist) { + return user; + } + + return NULL; +} + +struct iscsi_user *account_lookup_by_sysfs_name(struct target *target, + int dir, const char *sysfs_name) +{ + struct __qelem *list; + struct iscsi_user *user = NULL; + + list = account_list_get(target, dir); + + list_for_each_entry(user, list, ulist) { + if (!strcmp(user->sysfs_name, sysfs_name)) + return user; + } + + return NULL; +} + +int config_account_query(u32 tid, int dir, const char *name, char *pass) +{ + struct iscsi_user *user; if (!(user = account_lookup_by_name(tid, dir, name))) return -ENOENT; - if (!strlen(name)) - strncpy(name, user->name, ISCSI_NAME_LEN); - - strncpy(pass, user->password, ISCSI_NAME_LEN); + strlcpy(pass, user->password, ISCSI_NAME_LEN); return 0; } @@ -170,19 +201,27 @@ int config_account_query(u32 tid, int dir, char *name, char *pass) int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow, char *buf, size_t buf_sz) { - struct __qelem *list = account_list_get(tid, dir); - struct user *user; + struct target *target; + struct __qelem *list; + struct iscsi_user *user; *cnt = *overflow = 0; + if (tid) { + target = target_find_by_id(tid); + if (target == NULL) + return -ENOENT; + } else + target = NULL; + + list = account_list_get(target, dir); + if (!list) return -ENOENT; list_for_each_entry(user, list, ulist) { - if (user->tid != tid) - continue; if (buf_sz >= ISCSI_NAME_LEN) { - strncpy(buf, user->name, ISCSI_NAME_LEN); + strlcpy(buf, user->name, ISCSI_NAME_LEN); buf_sz -= ISCSI_NAME_LEN; buf += ISCSI_NAME_LEN; *cnt += 1; @@ -193,89 +232,255 @@ int config_account_list(u32 tid, int dir, u32 *cnt, u32 *overflow, return 0; } -static void account_destroy(struct user *user) +static void account_destroy(struct iscsi_user *user, int del) { if (!user) return; - remque(&user->ulist); - free(user->name); - free(user->password); + if (del) + list_del(&user->ulist); + free((void *)user->name); + free((void *)user->password); free(user); + return; } -int config_account_del(u32 tid, int dir, char *name) +void accounts_free(struct __qelem *accounts_list) { - struct user *user; + struct iscsi_user *user, *t; - if (!name || !(user = account_lookup_by_name(tid, dir, name))) - return -ENOENT; + list_for_each_entry_safe(user, t, accounts_list, ulist) { + account_destroy(user, 1); + } - account_destroy(user); - - /* update the file here. */ - return 0; + return; } -static struct user *account_create(void) +int config_account_del(u32 tid, int dir, char *name, u32 cookie) { - struct user *user; + struct iscsi_user *user; + int res = 0; + + if (!name) { + log_error("%s", "Name expected"); + res = -EINVAL; + goto out; + } + + user = account_lookup_by_name(tid, dir, name); + if (user == NULL) { + log_error("User %s not found", name); + res = -ENOENT; + goto out; + } + +#ifndef CONFIG_SCST_PROC + res = kernel_user_del(user, cookie); + if (res != 0) + goto out; +#endif + + account_destroy(user, 1); + +out: + return res; +} + +static struct iscsi_user *account_create(struct target *target, int direction, + const char *sysfs_name, const char *name, const char *pass) +{ + struct iscsi_user *user; if (!(user = malloc(sizeof(*user)))) return NULL; memset(user, 0, sizeof(*user)); INIT_LIST_HEAD(&user->ulist); + user->target = target; + user->direction = direction; + if (!(user->name = strdup(name)) || + !(user->password = strdup(pass))) { + log_error("Unable to duplicate name (%s) or password (%s)", + name, pass); + goto out_destroy; + } + + if (direction == ISCSI_USER_DIR_INCOMING) { + int inc_user_num = 0; + + if (sysfs_name != NULL) { + strlcpy(user->sysfs_name, sysfs_name, sizeof(user->sysfs_name)); + if (account_lookup_by_sysfs_name(target, direction, sysfs_name) == NULL) + goto out; + } + + while (1) { + if (inc_user_num == 0) + snprintf(user->sysfs_name, sizeof(user->sysfs_name), + "IncomingUser"); + else + snprintf(user->sysfs_name, sizeof(user->sysfs_name), + "IncomingUser%d", inc_user_num); + if (account_lookup_by_sysfs_name(target, direction, user->sysfs_name) == NULL) + break; + inc_user_num++; + } + } else + snprintf(user->sysfs_name, sizeof(user->sysfs_name), + "OutgoingUser"); + +out: return user; + +out_destroy: + account_destroy(user, 0); + user = NULL; + goto out; } -int config_account_add(u32 tid, int dir, char *name, char *pass) +int account_replace(struct target *target, int direction, + const char *sysfs_name, char *value) { - int err = -ENOMEM; - struct user *user; + int res = 0; + struct iscsi_user *user, *user1; + char *name, *pass, *n; struct __qelem *list; - if (!name || !pass) - return -EINVAL; + name = config_sep_string(&value); + pass = config_sep_string(&value); - if (tid) { - /* check here */ - /* return -ENOENT; */ + n = config_sep_string(&value); + if (*n != '\0') { + log_error("Unexpected parameter value %s\n", n); + res = -EINVAL; + goto out; + } + + user = account_lookup_by_sysfs_name(target, direction, sysfs_name); + if (user == NULL) { + log_error("Unknown parameter %s\n", sysfs_name); + res = -EINVAL; + goto out; + } + + user1 = __account_lookup_by_name(target, direction, name); + if ((user1 != NULL) && (user1 != user)) { + log_error("User %s already exists\n", name); + res = -EEXIST; + goto out; + } + + list = account_list_get(target, direction); + + list_del(&user->ulist); + + user1 = account_create(target, direction, sysfs_name, name, pass); + if (user1 == NULL) { + res = -ENOMEM; + goto out_add; + } + + list_add_tail(user1, list); + + account_destroy(user, 0); + +out: + return res; + +out_add: + list_add_tail(user, list); + goto out; +} + +int __config_account_add(struct target *target, int dir, char *name, + char *pass, char *sysfs_name, int send_to_kern, u32 cookie) +{ + int err = 0; + struct iscsi_user *user; + struct __qelem *list; + int del = 0; + + if (!name || !pass) { + log_error("%s", "Name or password is NULL"); + err = -EINVAL; + goto out; } /* Check for minimum RFC defined value */ if (strlen(pass) < 12) { log_error("Secret for user %s is too short. At least 12 bytes " "are required\n", name); - return -EINVAL; + err = -EINVAL; + goto out; } - if (!(user = account_create()) || - !(user->name = strdup(name)) || - !(user->password = strdup(pass))) + user = account_create(target, dir, sysfs_name, name, pass); + if (user == NULL) { + err = -ENOMEM; goto out; + } - user->tid = tid; - list = account_list_get(tid, dir); - if (dir == AUTH_DIR_OUTGOING) { - struct user *old, *tmp; - list_for_each_entry_safe(old, tmp, list, ulist) { - if (tid != old->tid) - continue; - log_warning("Only one outgoing %s account is supported." - " Replacing the old one.\n", - tid ? "target" : "discovery"); - account_destroy(old); + if (__account_lookup_by_name(target, dir, name) != NULL) { + log_error("User %s already exists for target %s (direction %s)", + name, target ? target->name : "discovery", + (dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming"); + err = -EEXIST; + goto out_destroy; + } + + list = account_list_get(target, dir); + if (dir == ISCSI_USER_DIR_OUTGOING) { + struct iscsi_user *old; + list_for_each_entry(old, list, ulist) { + log_warning("Only one outgoing %s account is " + "supported. Replacing the old one.\n", + target ? "target" : "discovery"); + account_destroy(old, 1); + break; } } - insque(user, list); + log_debug(1, "User %s added to target %s (direction %s)", user->name, + target ? target->name : "discovery", + (dir == ISCSI_USER_DIR_OUTGOING) ? "outgoing" : "incoming"); + + list_add_tail(user, list); + del = 1; + +#ifndef CONFIG_SCST_PROC + if (send_to_kern) { + err = kernel_user_add(user, cookie); + if (err != 0) + goto out_destroy; + } +#endif - /* update the file here. */ - return 0; out: - account_destroy(user); + return err; +out_destroy: + account_destroy(user, del); + goto out; +} + +int config_account_add(u32 tid, int dir, char *name, char *pass, char *sysfs_name, + u32 cookie) +{ + int err = 0; + struct target *target; + + if (tid) { + target = target_find_by_id(tid); + if (target == NULL) { + err = -ENOENT; + goto out; + } + } else + target = NULL; + + err = __config_account_add(target, dir, name, pass, sysfs_name, 1, cookie); + +out: return err; } @@ -467,167 +672,390 @@ int config_initiator_access(u32 tid, int fd) * Main configuration code */ -static int __config_target_create(u32 *tid, char *name, int update) -{ - int err; - - if (target_find_by_name(name)) { - log_error("duplicated target %s", name); - return -EINVAL; - } - if ((err = target_add(tid, name)) < 0) - return err; - - return err; -} - int config_target_create(u32 *tid, char *name) { - return __config_target_create(tid, name, 1); + int err; + struct target *target; + + err = target_create(name, &target); + if (err != 0) + goto out; + + err = target_add(target, tid, 0); + if (err != 0) + goto out_free; + +out: + return err; + +out_free: + target_free(target); + goto out; } int config_target_destroy(u32 tid) { int err; - if ((err = target_del(tid)) < 0) + if ((err = target_del(tid, 0)) < 0) return err; return err; } -int config_param_set(u32 tid, u64 sid, int type, u32 partial, - struct iscsi_param *param) +int config_params_get(u32 tid, u64 sid, int type, struct iscsi_param *params) { - int err; + int err, i; + struct target *target; - err = kernel_param_set(tid, sid, type, partial, param); + if (sid != 0) { + err = kernel_params_get(tid, sid, type, params); + goto out; + } + err = 0; + + target = target_find_by_id(tid); + if (target == NULL) { + log_error("target %d not found", tid); + err = -EINVAL; + goto out; + } + + if (type == key_session) { + for (i = 0; i < session_key_last; i++) + params[i].val = target->session_params[i]; + } else { + for (i = 0; i < target_key_last; i++) + params[i].val = target->target_params[i]; + } + +out: return err; } -static int iscsi_param_partial_set(u32 tid, u64 sid, int type, int key, u32 val) +int config_params_set(u32 tid, u64 sid, int type, u32 partial, + struct iscsi_param *params) { - struct iscsi_param *param; - struct iscsi_param session_param[session_key_last]; - struct iscsi_param target_param[target_key_last]; + int err, i; + struct target *target; - if (type == key_session) - param = session_param; - else - param = target_param; - - param[key].val = val; - - return config_param_set(tid, sid, type, 1 << key, param); -} - -static int config_main_init(char *filename) -{ - FILE *config; - char buf[BUFSIZE]; - char *p, *q; - int idx; - u32 tid, val; - int res = 0; - - if (!(config = fopen(filename, "r"))) { - return errno == ENOENT ? 0 : -errno; + if (sid != 0) { + err = kernel_params_set(tid, sid, type, partial, params); + goto out; } - tid = 0; - while (fgets(buf, BUFSIZE, config)) { + err = 0; + + target = target_find_by_id(tid); + if (target == 0) { + log_error("target %d not found", tid); + err = -EINVAL; + goto out; + } + + if (partial == 0) + partial = (typeof(partial))-1; + + if (type == key_session) { + for (i = 0; i < session_key_last; i++) { + if (partial & (1 << i)) { + err = params_check_val(session_keys, i, ¶ms[i].val); + if (err < 0) { + log_error("Wrong value %u for parameter %s\n", + params[i].val, session_keys[i].name); + goto out; + } + } + } + for (i = 0; i < session_key_last; i++) { + if (partial & (1 << i)) + target->session_params[i] = params[i].val; + } + } else { + for (i = 0; i < target_key_last; i++) { + if (partial & (1 << i)) { + err = params_check_val(target_keys, i, ¶ms[i].val); + if (err < 0) { + log_error("Wrong value %u for parameter %s\n", + params[i].val, target_keys[i].name); + goto out; + } + } + } + for (i = 0; i < target_key_last; i++) { + if (partial & (1 << i)) + target->target_params[i] = params[i].val; + } + } + +out: + return err; +} + +int config_parse_main(const char *data, u32 cookie) +{ + char buf[BUFSIZE]; + char *p, *q, *n; + int idx, offset = 0; + u32 val; + int res = 0; + struct target *target = NULL; + int global_section = 1; /* supposed to be bool and true */ + int parsed_something = 0; /* supposed to be bool and false */ + int stop_on_errors = (cookie != 0); + + while (config_gets(buf, sizeof(buf), data, &offset)) { + parsed_something = 1; + /* + * If stop_on_errors is false, let's always continue parsing + * and only report errors. + */ + if (stop_on_errors && (res != 0)) + goto out_target_free; + q = buf; - p = target_sep_string(&q); - if (!p || *p == '#') + p = config_sep_string(&q); + if ((*p == '#') || (*p == '\0')) continue; + if (!strcasecmp(p, "Target")) { - tid = 0; - if (!(p = target_sep_string(&q))) + global_section = 0; + + if (target != NULL) { + res = target_add(target, NULL, cookie); + if (res != 0) + target_free(target); + } + + target = NULL; + p = config_sep_string(&q); + if (p == '\0') { + log_error("Target name required on %s\n", q); continue; - if (__config_target_create(&tid, p, 0)) - log_debug(1, "creating target %s", p); - } else if (!strcasecmp(p, "Alias") && tid) { + } + + n = config_sep_string(&q); + if (*n != '\0') { + log_error("Unexpected parameter value %s\n", n); + res = -EINVAL; + continue; + } + + log_debug(1, "Creating target %s", p); + res = target_create(p, &target); + if (res != 0) + goto out; + } else if (!strcasecmp(p, "Alias") && target) { ; - } else if (!((idx = param_index_by_name(p, target_keys)) < 0) && tid) { - val = strtol(q, &q, 0); - if (param_check_val(target_keys, idx, &val) < 0) { + } else if (!((idx = params_index_by_name(p, target_keys)) < 0) && (target != NULL)) { + char *str = config_sep_string(&q); + + n = config_sep_string(&q); + if (*n != '\0') { + log_error("Unexpected parameter value %s\n", n); + res = -EINVAL; + continue; + } + + res = params_str_to_val(target_keys, idx, str, &val); + if (res < 0) { + log_error("Wrong value %s for parameter %s\n", + str, target_keys[idx].name); + continue; + } + + 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); - res = -1; - break; + continue; } - iscsi_param_partial_set(tid, 0, key_target, idx, val); - } else if (!((idx = param_index_by_name(p, session_keys)) < 0) && tid) { - char *str = target_sep_string(&q); - if (param_str_to_val(session_keys, idx, str, &val) < 0) { + target->target_params[idx] = val; + } else if (!((idx = params_index_by_name(p, session_keys)) < 0) && (target != NULL)) { + char *str = config_sep_string(&q); + + n = config_sep_string(&q); + if (*n != '\0') { + log_error("Unexpected parameter value %s\n", n); + res = -EINVAL; + continue; + } + + res = params_str_to_val(session_keys, idx, str, &val); + if (res < 0) { log_error("Wrong value %s for parameter %s\n", str, session_keys[idx].name); - res = -1; - break; + continue; } - if (param_check_val(session_keys, idx, &val) < 0) { + + 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); - res = -1; - break; + continue; } - iscsi_param_partial_set(tid, 0, key_session, idx, val); - } else if (param_index_by_name(p, user_keys) < 0) { - log_warning("Unknown iscsi-scstd.conf param: %s\n", p); - res = -1; - break; + target->session_params[idx] = val; + } else if (!((idx = params_index_by_name_numwild(p, user_keys)) < 0) && + ((target != NULL) || global_section)) { + char *name, *pass; + + name = config_sep_string(&q); + pass = config_sep_string(&q); + + n = config_sep_string(&q); + if (*n != '\0') { + log_error("Unexpected parameter value %s\n", n); + res = -EINVAL; + continue; + } + + res = __config_account_add(target, idx, name, pass, p, + (target == 0), 0); + if (res < 0) + continue; + } else if (global_section && + (!strcasecmp(p, ISCSI_ISNS_SERVER_PARAM_NAME) || + !strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME))) + continue; + else { + log_error("Unknown or unexpected param: %s\n", p); + res = -EINVAL; + continue; } } - fclose(config); - return res; -} + if (stop_on_errors && (res != 0)) + goto out_target_free; -int config_load(char *params) -{ - int i, err; - - for (i = 0; i < 1 << HASH_ORDER; i++) { - INIT_LIST_HEAD(&trgt_acct_in[i]); - INIT_LIST_HEAD(&trgt_acct_out[i]); + if (target != NULL) { + res = target_add(target, NULL, cookie); + if (res != 0) + goto out_target_free; } - /* First, we must finish the main configuration. */ - if ((err = config_main_init(params ? params : CONFIG_FILE))) - return err; +out: + if (stop_on_errors) { + if ((res == 0) && !parsed_something) + res = -ENOENT; + } else + res = 0; - if ((err = config_account_init(ACCT_CONFIG_FILE)) < 0) - return err; + return res; - /* TODO: error handling */ - - return err; +out_target_free: + if (target != NULL) + target_free(target); + goto out; } -int config_isns_load(char *params, char **isns, int *isns_ac) +static int config_isns_load(const char *config) { - FILE *config; char buf[BUFSIZE]; + int offset = 0; char *p, *q; - if (!(config = fopen(params ? : CONFIG_FILE, "r"))) - return -errno; - - while (fgets(buf, BUFSIZE, config)) { + while (config_gets(buf, sizeof(buf), config, &offset)) { q = buf; - p = target_sep_string(&q); - if (!p || *p == '#') + p = config_sep_string(&q); + if ((*p == '\0') || (*p == '#')) continue; - if (!strcasecmp(p, "iSNSServer")) { - *isns = strdup(target_sep_string(&q)); - } else if (!strcasecmp(p, "iSNSAccessControl")) { - char *str = target_sep_string(&q); - if (!strcasecmp(str, "Yes")) - *isns_ac = 1; + if (!strcasecmp(p, ISCSI_ISNS_SERVER_PARAM_NAME)) { + isns_server = strdup(config_sep_string(&q)); + } else if (!strcasecmp(p, ISCSI_ISNS_ACCESS_CONTROL_PARAM_NAME)) { + char *str = config_sep_string(&q); + if (!strcasecmp(str, "No")) + isns_access_control = 0; + else + isns_access_control = 1; } } - fclose(config); return 0; } + +int config_load(const char *config_name) +{ + int i, err = 0, rc; + int config; + const char *cname; + int size; + char *buf; + + if (config_name != NULL) + cname = config_name; + else + cname = CONFIG_FILE; + + config = open(cname, O_RDONLY); + if (config == -1) { + if ((errno == ENOENT) && (config_name == NULL)) { +#ifdef CONFIG_SCST_PROC + log_debug(3, "Default config file %s not found", + CONFIG_FILE); +#endif + goto out; + } else { + err = -errno; + log_error("Open config file %s failed: %s", cname, + strerror(err)); + goto out; + } + } + + size = lseek(config, 0, SEEK_END); + if (size < 0) { + err = -errno; + log_error("lseek() failed: %s", strerror(err)); + goto out_close; + } + + buf = malloc(size+1); + if (buf == NULL) { + err = -ENOMEM; + log_error("malloc() failed: %s", strerror(err)); + goto out_close; + } + + rc = lseek(config, 0, SEEK_SET); + if (rc < 0) { + err = -errno; + log_error("lseek() failed: %s", strerror(err)); + goto out_free; + } + + i = 0; + do { + rc = read(config, &buf[i], size - i); + if (rc < 0) { + err = -errno; + log_error("read() failed: %s", strerror(err)); + goto out_free; + } else if (rc == 0) + break; + i += rc; + } while (i < size); + + size = i; + buf[size+1] = '\0'; + + config_parse_main(buf, 0); + + err = config_isns_load(buf); + if ((err == 0) && (isns_server != NULL)) { + int rc = isns_init(); + if (rc != 0) { + log_error("iSNS server %s init failed: %d", isns_server, rc); + isns_exit(); + } + } + +out_free: + free(buf); + +out_close: + close(config); + +out: + return err; +} diff --git a/iscsi-scst/usr/conn.c b/iscsi-scst/usr/conn.c index d89aefde6..f4a6c747d 100644 --- a/iscsi-scst/usr/conn.c +++ b/iscsi-scst/usr/conn.c @@ -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); diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index 3557e25fc..dd34f7238 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -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(®, 0, sizeof(reg)); reg.version = (uintptr_t)ISCSI_SCST_INTERFACE_VERSION; err = ioctl(ctlfd, REGISTER_USERD, ®); - 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; } diff --git a/iscsi-scst/usr/event.c b/iscsi-scst/usr/event.c index 6a23bf323..10dc3874c 100644 --- a/iscsi-scst/usr/event.c +++ b/iscsi-scst/usr/event.c @@ -24,13 +24,18 @@ #include #include #include +#include #include #include #include +#include + #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; } diff --git a/iscsi-scst/usr/iscsi_adm.c b/iscsi-scst/usr/iscsi_adm.c index bcaca806a..1de876472 100644 --- a/iscsi-scst/usr/iscsi_adm.c +++ b/iscsi-scst/usr/iscsi_adm.c @@ -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); } diff --git a/iscsi-scst/usr/iscsi_adm.h b/iscsi-scst/usr/iscsi_adm.h index fe128897f..0b388a25e 100644 --- a/iscsi-scst/usr/iscsi_adm.h +++ b/iscsi-scst/usr/iscsi_adm.h @@ -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 { diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index ce4a80f52..c7c27ab3d 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -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; } diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index a1d9e4bca..f4249f93d 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -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); diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 0105a578d..33a536db6 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -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); diff --git a/iscsi-scst/usr/isns.c b/iscsi-scst/usr/isns.c index 482e17881..86c78411c 100644 --- a/iscsi-scst/usr/isns.c +++ b/iscsi-scst/usr/isns.c @@ -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; } diff --git a/iscsi-scst/usr/log.c b/iscsi-scst/usr/log.c index 74f6ae40b..4c484dcf6 100644 --- a/iscsi-scst/usr/log.c +++ b/iscsi-scst/usr/log.c @@ -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; diff --git a/iscsi-scst/usr/message.c b/iscsi-scst/usr/message.c index 71a7db73d..432ccd0c8 100644 --- a/iscsi-scst/usr/message.c +++ b/iscsi-scst/usr/message.c @@ -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); diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index b1e1d3278..8f663a51d 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -18,10 +18,24 @@ #include #include #include +#include #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,}, }; diff --git a/iscsi-scst/usr/param.h b/iscsi-scst/usr/param.h index bab03771b..d27f3db59 100644 --- a/iscsi-scst/usr/param.h +++ b/iscsi-scst/usr/param.h @@ -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 diff --git a/iscsi-scst/usr/session.c b/iscsi-scst/usr/session.c index 3d0fad614..744da115c 100644 --- a/iscsi-scst/usr/session.c +++ b/iscsi-scst/usr/session.c @@ -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) diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index 6a3bba3ff..0c6604770 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -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; } diff --git a/qla2x00t/qla2x00-target/README b/qla2x00t/qla2x00-target/README index 18b273bbb..471472d7e 100644 --- a/qla2x00t/qla2x00-target/README +++ b/qla2x00t/qla2x00-target/README @@ -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. diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 40afe5519..4fb7bd74f 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -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, diff --git a/scst/README b/scst/README index ed903fa5d..ded18a55d 100644 --- a/scst/README +++ b/scst/README @@ -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 diff --git a/scst/include/scst.h b/scst/include/scst.h index 510021ac3..f7991d7a7 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -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 */ diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index 0fbc49cd2..636afd355 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -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 */ diff --git a/scst/include/scst_user.h b/scst/include/scst_user.h index 2277b6002..2c59372d1 100644 --- a/scst/include/scst_user.h +++ b/scst/include/scst_user.h @@ -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 diff --git a/scst/src/dev_handlers/scst_user.c b/scst/src/dev_handlers/scst_user.c index 3ecedec98..980768e48 100644 --- a/scst/src/dev_handlers/scst_user.c +++ b/scst/src/dev_handlers/scst_user.c @@ -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); diff --git a/scst/src/dev_handlers/scst_vdisk.c b/scst/src/dev_handlers/scst_vdisk.c index 8c2dc71d5..aa4bddf90 100644 --- a/scst/src/dev_handlers/scst_vdisk.c +++ b/scst/src/dev_handlers/scst_vdisk.c @@ -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); diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index 774d26010..e0c055659 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -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; diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index ea0746e33..62aa6e798 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -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 diff --git a/scst/src/scst_mem.c b/scst/src/scst_mem.c index 25baf0925..8cd706737 100644 --- a/scst/src/scst_mem.c +++ b/scst/src/scst_mem.c @@ -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; diff --git a/scst/src/scst_proc.c b/scst/src/scst_proc.c index ff1365b6e..4b1f36acf 100644 --- a/scst/src/scst_proc.c +++ b/scst/src/scst_proc.c @@ -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 diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index c3eeaf2d2..f892dfa49 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -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; diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 7ae1dfa36..0df22450f 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -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, " diff --git a/usr/fileio/README b/usr/fileio/README index 14a6dd9e2..9f8aca65e 100644 --- a/usr/fileio/README +++ b/usr/fileio/README @@ -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 diff --git a/usr/fileio/common.h b/usr/fileio/common.h index 8cc576596..9d6a635aa 100644 --- a/usr/fileio/common.h +++ b/usr/fileio/common.h @@ -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' */ diff --git a/usr/fileio/fileio.c b/usr/fileio/fileio.c index 2a4e75bb5..588be6b40 100644 --- a/usr/fileio/fileio.c +++ b/usr/fileio/fileio.c @@ -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