diff --git a/boot.open-iscsi b/boot.open-iscsi index 70fa3fe..87d1a3a 100644 --- a/boot.open-iscsi +++ b/boot.open-iscsi @@ -4,7 +4,7 @@ # ### BEGIN INIT INFO # Provides: iscsiboot -# Required-Start: +# Required-Start: boot.proc # Should-Start: # Required-Stop: # Should-Stop: @@ -14,6 +14,7 @@ # ### END INIT INFO +ISCSIADM=/sbin/iscsiadm PID_FILE=/var/run/iscsi.pid CONFIG_FILE=/etc/iscsid.conf DAEMON=/sbin/iscsid @@ -29,10 +30,10 @@ ARGS="-c $CONFIG_FILE -p $PID_FILE" # iscsi_mark_root_nodes() { - TARGETS=$($ISCSIADM -m session 2> /dev/null | sed 's@\[[^:]*:\(.*\)\] .*@\1@g') + TARGETS=$($ISCSIADM -m session 2> /dev/null | sed 's@.*\[[^:]*:\(.*\)\] .*@\1@g') for rec in $TARGETS; do STARTUP=`$ISCSIADM -m node -r $rec | grep "node.conn\[0\].startup" | cut -d' ' -f3` - if [ $STARTUP != "onboot" ] ; then + if [ "$STARTUP" != "onboot" ] ; then $ISCSIADM -m node -r $rec -o update -n node.conn[0].startup -v onboot fi done @@ -43,7 +44,7 @@ rc_reset # We only need to start this for root on iSCSI if ! grep -q iscsi_tcp /proc/modules ; then - rc_failed 7 + rc_failed 6 rc_exit fi diff --git a/iscsi-gen-initiatorname.sh b/iscsi-gen-initiatorname.sh new file mode 100644 index 0000000..c978ecd --- /dev/null +++ b/iscsi-gen-initiatorname.sh @@ -0,0 +1,77 @@ +#/bin/bash +# +# /sbin/iscsi-gen-initiatorname +# +# Generate a default iSCSI Initiatorname for SUSE installations. +# +# Copyright (c) 2007 Hannes Reinecke, SUSE Linux Products GmbH. +# All rights reserved. +# + +if [ "$1" ] ; then + if [ "$1" = "-f" ] ; then + FORCE=1 + else + echo "Invalid option $1" + echo "Usage: $0 [-f]" + exit 1 + fi +fi + +if [ -f /etc/iscsi/initiatorname.iscsi -a -z "$FORCE" ] ; then + if [ -x /sbin/fwparam_ibft ] ; then + eval $(/sbin/fwparam_ibft -b 2> /dev/null | grep iSCSI_INITIATOR_NAME) + fi + if [ "$iSCSI_INITIATOR_NAME" ] ; then + eval $(cat /etc/iscsi/initiatorname.iscsi | sed -e '/^#/d') + if [ "$iSCSI_INITIATOR_NAME" != "$InitiatorName" ] ; then + echo "iSCSI Initiatorname from iBFT is different from the current setting." + echo "Please call '/sbin/iscsi-gen-initiatorname -f' to update the iSCSI Initiatorname." + exit 1 + fi + fi + exit 0 +fi + +if [ -x /sbin/fwparam_ibft ] ; then + eval $(/sbin/fwparam_ibft -b 2> /dev/null | sed -ne '/iSCSI_INITIATOR_NAME/p') + + if [ "$iSCSI_INITIATOR_NAME" ] ; then + cat << EOF >> /etc/iscsi/initiatorname.iscsi +## +## /etc/iscsi/iscsi.initiatorname +## +## iSCSI Initiatorname taken from iBFT BIOS tables. +## +## DO NOT EDIT OR REMOVE THIS FILE! +## If you remove this file, the iSCSI daemon will not start. +## Any change here will not be reflected to the iBFT BIOS tables. +## If a different initiatorname is required please change the +## initiatorname in the BIOS setup and call +## /sbin/iscsi-gen-initiatorname -f +## to recreate an updated version of this file. +## +InitiatorName=$iSCSI_INITIATOR_NAME +EOF + fi +fi + +if [ ! -f /etc/iscsi/initiatorname.iscsi ] ; then + cat << EOF >> /etc/iscsi/initiatorname.iscsi +## +## /etc/iscsi/iscsi.initiatorname +## +## Default iSCSI Initiatorname. +## +## DO NOT EDIT OR REMOVE THIS FILE! +## If you remove this file, the iSCSI daemon will not start. +## If you change the InitiatorName, existing access control lists +## may reject this initiator. The InitiatorName must be unique +## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames. +EOF + ISSUEDATE="1996-04" + INAME=$(/sbin/iscsi-iname -p iqn.$ISSUEDATE.de.suse:01) + printf "InitiatorName=$INAME\n" >>/etc/iscsi/initiatorname.iscsi + chmod 0600 /etc/iscsi/initiatorname.iscsi +fi + diff --git a/iscsi-iname.c b/iscsi-iname.c deleted file mode 100644 index c0f3d01..0000000 --- a/iscsi-iname.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * iSCSI InitiatorName creation utility - * Copyright (C) 2001 Cisco Systems, Inc. - * maintained by linux-iscsi-devel@lists.sourceforge.net - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * See the file COPYING included with this distribution for more details. - * - * $Id: iscsi-iname.c,v 1.4 2004/12/03 19:36:13 mikenc Exp $ - * - * iscsi-iname.c - Compute an iSCSI InitiatorName for this host. - * Note that to ensure uniqueness, the system time is - * a factor. This name must be cached and only regenerated - * if there is no cached value. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "md5.h" - -#define TARGET_NAME_MAXLEN 255 -#define RANDOM_NUM_GENERATOR "/dev/urandom" - -int -main(int argc, char *argv[]) -{ - char iname[TARGET_NAME_MAXLEN + 1]; - struct timeval time; - struct utsname system_info; - long hostid; - struct MD5Context context; - unsigned char digest[16]; - unsigned char *bytes = digest; - unsigned char entropy[16]; - int e; - int fd; - char *prefix; - - /* initialize */ - memset(iname, 0, sizeof (iname)); - memset(digest, 0, sizeof (digest)); - memset(&context, 0, sizeof (context)); - MD5Init(&context); - - /* take a prefix if given, otherwise use a default. */ - if (argc > 1 && argv[1]) { - prefix = argv[1]; - if (( strcmp(prefix, "-h") == 0 ) || - ( strcmp(prefix, "--help") == 0 )) { - printf("\nDisplays the iSCSI initiator name\n"); - return 0; - } else if ( strcmp(prefix, "-p") == 0 ) { - prefix = argv[2]; - } else { - printf("\nUsage: iscsi-iname [-h | --help | " - "-p ]\n"); - return 0; - } - } else { - prefix = "iqn.1987-05.com.cisco:01"; - } - - /* try to feed some entropy from the pool to MD5 in order to get - * uniqueness properties - */ - - if ((fd = open(RANDOM_NUM_GENERATOR, O_RDONLY))) { - e = read(fd, &entropy, 16); - if (e >= 1) - MD5Update(&context, (md5byte *)entropy, e); - close(fd); - } - - /* time the name is created is a factor in order to get - * uniqueness properties - */ - if (gettimeofday(&time, NULL) < 0) { - perror("error: gettimeofday failed"); - return 1; - } - MD5Update(&context, (md5byte *) & time.tv_sec, sizeof (time.tv_sec)); - MD5Update(&context, (md5byte *) & time.tv_usec, sizeof (time.tv_usec)); - - /* hostid */ - hostid = gethostid(); - MD5Update(&context, (md5byte *) & hostid, sizeof (hostid)); - - /* get the hostname and system name */ - if (uname(&system_info) < 0) { - perror("error: uname failed"); - return 1; - } - MD5Update(&context, (md5byte *) system_info.sysname, - sizeof (system_info.sysname)); - MD5Update(&context, (md5byte *) system_info.nodename, - sizeof (system_info.nodename)); - MD5Update(&context, (md5byte *) system_info.release, - sizeof (system_info.release)); - MD5Update(&context, (md5byte *) system_info.version, - sizeof (system_info.version)); - MD5Update(&context, (md5byte *) system_info.machine, - sizeof (system_info.machine)); - - /* compute the md5 hash of all the bits we just collected */ - MD5Final(digest, &context); - - /* vary which md5 bytes we pick (though we probably don't need to do - * this, since hopefully MD5 produces results such that each byte is as - * good as any other). - */ - - if ((fd = open(RANDOM_NUM_GENERATOR, O_RDONLY))) { - if (read(fd, entropy, 1) == 1) - bytes = &digest[(entropy[0] % (sizeof(digest) - 6))]; - close(fd); - } - - /* print the prefix followed by 6 bytes of the MD5 hash */ - sprintf(iname, "%s.%x%x%x%x%x%x", prefix, - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]); - - iname[sizeof (iname) - 1] = '\0'; - printf("%s\n", iname); - return 0; -} diff --git a/open-iscsi-2.0-707.tar.gz b/open-iscsi-2.0-707.tar.gz deleted file mode 100644 index ad732cd..0000000 --- a/open-iscsi-2.0-707.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2bd837f02a8f82c9782346b91836e17172f6f62ecc4b2224739eb2e8d5d9ea08 -size 177056 diff --git a/open-iscsi-2.0-754.tar.gz b/open-iscsi-2.0-754.tar.gz new file mode 100644 index 0000000..8890ec6 --- /dev/null +++ b/open-iscsi-2.0-754.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc286e8bc457612af733fafab0608a25280b81f7306c9f218c9361e1fd8e573e +size 223383 diff --git a/open-iscsi-713.diff b/open-iscsi-713.diff deleted file mode 100644 index 930744c..0000000 --- a/open-iscsi-713.diff +++ /dev/null @@ -1,226 +0,0 @@ -Index: kernel/scsi_transport_iscsi.c -=================================================================== ---- kernel/scsi_transport_iscsi.c (revision 707) -+++ kernel/scsi_transport_iscsi.c (revision 713) -@@ -33,7 +33,7 @@ - #define ISCSI_SESSION_ATTRS 11 - #define ISCSI_CONN_ATTRS 11 - #define ISCSI_HOST_ATTRS 0 --#define ISCSI_TRANSPORT_VERSION "2.0-707" -+#define ISCSI_TRANSPORT_VERSION "2.0-711" - - struct iscsi_internal { - int daemon_pid; -Index: doc/iscsid.8 -=================================================================== ---- doc/iscsid.8 (revision 707) -+++ doc/iscsid.8 (revision 713) -@@ -15,11 +15,11 @@ - .TP - .BI [-c|--config=]\fIconfig\-file\fP - Read configuration from \fIconfig\-file\fR rather than the default --\fI/etc/iscsid.conf\fR file. -+\fI/etc/iscsi/iscsid.conf\fR file. - .TP - .BI [-i|--initiatorname=]\fIiname\-file\fP - Read initiator name from \fIiname\-file\fR rather than the default --\fI/etc/initiatorname.iscsi\fR file. -+\fI/etc/iscsi/initiatorname.iscsi\fR file. - .TP - .BI [-f|--foreground] - run -@@ -47,14 +47,14 @@ - - .SH FILES - .TP --/etc/iscsid.conf -+/etc/iscsi/iscsid.conf - The configuration file read by - .B iscsid - and - .B iscsiadm - on startup. - .TP --/etc/initiatorname.iscsi -+/etc/iscsi/initiatorname.iscsi - The file containing the iSCSI initiatorname - and initiatoralias read by - .B iscsid -@@ -62,7 +62,7 @@ - .B iscsiadm - on startup. - .TP --/var/db/iscsi/*.db -+/etc/iscsi/nodes - Open-iSCSI persistent configuration database - - .SH "SEE ALSO" -Index: usr/idbm.c -=================================================================== ---- usr/idbm.c (revision 707) -+++ usr/idbm.c (revision 713) -@@ -961,15 +961,14 @@ - discovery_rec_t *drec; - node_rec_t *nrec; - -- /* sync default configuration */ -- idbm_sync_config(db, 1); -- - /* allocate new discovery record and initialize with defaults */ - drec = malloc(sizeof(discovery_rec_t)); - if (!drec) { - log_error("out of memory on discovery record allocation"); - return NULL; - } -+ drec->type = type; -+ - if (drec->type == DISCOVERY_TYPE_SENDTARGETS) { - memcpy(drec, &db->drec_st, sizeof(discovery_rec_t)); - } else if (drec->type == DISCOVERY_TYPE_SLP) { -@@ -988,7 +987,6 @@ - memcpy(nrec, &db->nrec, sizeof(node_rec_t)); - - /* update discovery record */ -- drec->type = type; - if (drec->type == DISCOVERY_TYPE_SENDTARGETS) { - strncpy(drec->u.sendtargets.address, ip, NI_MAXHOST); - drec->u.sendtargets.port = port; -@@ -1138,6 +1136,9 @@ - void - idbm_sendtargets_defaults(idbm_t *db, struct iscsi_sendtargets_config *cfg) - { -+ /* sync default configuration */ -+ idbm_sync_config(db, 1); -+ - memcpy(cfg, &db->drec_st.u.sendtargets, - sizeof(struct iscsi_sendtargets_config)); - } -Index: usr/initiator.c -=================================================================== ---- usr/initiator.c (revision 707) -+++ usr/initiator.c (revision 713) -@@ -1528,6 +1528,10 @@ - log_debug(6, "looking for session [%s,%s,%d]", - rec->name, rec->conn[0].address, rec->conn[0].port); - -+ if (strlen(rec->name) != strlen(targetname) || -+ strlen(rec->conn[0].address) != strlen(address)) -+ return 0; -+ - if (!strncmp(rec->name, targetname, strlen(rec->name)) && - !strncmp(rec->conn[0].address, address, - strlen(rec->conn[0].address)) && -Index: usr/version.h -=================================================================== ---- usr/version.h (revision 707) -+++ usr/version.h (revision 713) -@@ -6,7 +6,7 @@ - * This may not be the same value as the kernel versions because - * some other maintainer could merge a patch without going through us - */ --#define ISCSI_VERSION_STR "2.0-707" -+#define ISCSI_VERSION_STR "2.0-711" - #define ISCSI_VERSION_FILE "/sys/module/scsi_transport_iscsi/version" - - #endif -Index: etc/initd/initd.suse -=================================================================== ---- etc/initd/initd.suse (revision 707) -+++ etc/initd/initd.suse (revision 713) -@@ -15,7 +15,7 @@ - ### END INIT INFO - - PID_FILE=/var/run/iscsi.pid --CONFIG_FILE=/etc/iscsid.conf -+CONFIG_FILE=/etc/iscsi/iscsid.conf - DAEMON=/sbin/iscsid - ISCSIADM=/sbin/iscsiadm - ARGS="-c $CONFIG_FILE -p $PID_FILE" -@@ -59,7 +59,7 @@ - $ISCSIADM -m session | while read line; do - set ${line} - TARGET=$(echo $line | cut -d" " -f4) -- PORTAL=$(echo $line | cut -d" " -f3 | sed 's/,.//') -+ PORTAL=$(echo $line | cut -d" " -f3 | sed 's/,.*//') - STARTUP=`$ISCSIADM -m node --targetname $TARGET -p $PORTAL | grep "node.conn\[0\].startup" | cut -d' ' -f3` - NODE=`$ISCSIADM -m node --targetname $TARGET -p $PORTAL | grep "node.name" | cut -d' ' -f3` - if [ $STARTUP != "onboot" ] ; then -@@ -85,7 +85,7 @@ - $ISCSIADM -m session | while read line; do - set ${line} - TARGET=$(echo $line | cut -d" " -f4) -- PORTAL=$(echo $line | cut -d" " -f3 | sed 's/,.//') -+ PORTAL=$(echo $line | cut -d" " -f3 | sed 's/,.*//') - NODE=`$ISCSIADM -m node --targetname $TARGET -p $PORTAL | grep "node.name" | cut -d' ' -f3` - echo -e "\t$NODE" - done -Index: etc/initd/initd.debian -=================================================================== ---- etc/initd/initd.debian (revision 707) -+++ etc/initd/initd.debian (revision 713) -@@ -6,7 +6,7 @@ - - PID_FILE=/var/run/iscsid.pid - CONFIG_FILE=/etc/iscsid.conf --DAEMON=/usr/sbin/iscsid -+DAEMON=/sbin/iscsid - - PATH=/sbin:/bin:/usr/sbin:/usr/bin - -Index: README -=================================================================== ---- README (revision 707) -+++ README (revision 713) -@@ -123,7 +123,7 @@ - - Usage: iscsid [OPTION] - -- -c, --config=[path] Execute in the config file (/etc/iscsid.conf). -+ -c, --config=[path] Execute in the config file (/etc/iscsi/iscsid.conf). - -f, --foreground run iscsid in the foreground - -d, --debug debuglevel print debugging information - -u, --uid=uid run as uid, default is current user -@@ -141,10 +141,10 @@ - - The database contains two tables: - --- Discovery table (discovery.db); --- Node table (node.db). -+- Discovery table (/etc/iscsi/send_targets); -+- Node table (/etc/iscsi/nodes). - --The regular place for iSCSI database files: /var/db/iscsi/*.db -+The regular place for iSCSI database files: /etc/iscsi/nodes - - The iscsiadm utility is a command-line tool to manage (update, delete, - insert, query) the persistent database. -@@ -250,7 +250,7 @@ - 6. Configuration - ================ - --The default configuration file is /etc/iscsid.conf. This file contains -+The default configuration file is /etc/iscsi/iscsid.conf. This file contains - only configuration that could be overwritten by iSCSI Discovery, - or manualy updated via iscsiadm utility. Its OK if this file does not - exist in which case compiled-in default configuration will take place -@@ -333,9 +333,9 @@ - Once iscsi is up, you can perform discovery to targets using: - iscsiadm -m discovery -t sendtargets -p 192.168.1.1:3260 - --While discovery targets are kept in the discovery.db, they are -+While discovery targets are kept in the discovery db, they are - usefull only for re-discovery. The discovered targets (a.k.a. nodes) --are stored as records in the xxx.db. -+are stored as records in the node db. - - The discovered targets are not logged into yet. Rather than logging - into the discovered nodes (making LUs from those nodes available as -@@ -350,7 +350,7 @@ - iscsiadm -m node -T targetname -p ip:port --op update -n node.conn[0].startup -v automatic - - Or to set the "node.conn[0].statup" attribute to "startup" as default for --all sessions add the following to the /etc/iscsid.conf: -+all sessions add the following to the /etc/iscsi/iscsid.conf: - - node.conn[0].startup = automatic - diff --git a/open-iscsi-779.diff b/open-iscsi-779.diff new file mode 100644 index 0000000..ae580bf --- /dev/null +++ b/open-iscsi-779.diff @@ -0,0 +1,10546 @@ +Index: kernel/2.6.16-18_compat.patch +=================================================================== +--- kernel/2.6.16-18_compat.patch (revision 0) ++++ kernel/2.6.16-18_compat.patch (revision 779) +@@ -0,0 +1,136 @@ ++diff -Nurp a/iscsi_2.6.20_compat.h b/iscsi_2.6.20_compat.h ++--- a/iscsi_2.6.20_compat.h 1970-01-01 00:00:00.000000000 +0200 +++++ b/iscsi_2.6.20_compat.h 2007-02-22 18:21:22.000000000 +0200 ++@@ -0,0 +1,47 @@ +++ +++#define CRYPTO_ALG_ASYNC 0x00000080 +++struct hash_desc +++{ +++ struct crypto_tfm *tfm; +++ u32 flags; +++}; +++ +++static inline int crypto_hash_init(struct hash_desc *desc) +++{ +++ crypto_digest_init(desc->tfm); +++ return 0; +++} +++ +++static inline int crypto_hash_digest(struct hash_desc *desc, +++ struct scatterlist *sg, +++ unsigned int nbytes, u8 *out) +++{ +++ crypto_digest_digest(desc->tfm, sg, (nbytes+(PAGE_SIZE-1)) / PAGE_SIZE, out); +++ return nbytes; +++} +++ +++static inline int crypto_hash_update(struct hash_desc *desc, +++ struct scatterlist *sg, +++ unsigned int nbytes) +++{ +++ crypto_digest_update(desc->tfm, sg, (nbytes+(PAGE_SIZE-1)) / PAGE_SIZE); +++ return nbytes; +++} +++ +++static inline int crypto_hash_final(struct hash_desc *desc, u8 *out) +++{ +++ crypto_digest_final(desc->tfm, out); +++ return 0; +++} +++ +++static inline struct crypto_tfm *crypto_alloc_hash(const char *alg_name, +++ u32 type, u32 mask) +++{ +++ struct crypto_tfm *ret = crypto_alloc_tfm(alg_name ,type); +++ return ret ? ret : ERR_PTR(-ENOMEM); +++} +++ +++static inline void crypto_free_hash(struct crypto_tfm *tfm) +++{ +++ crypto_free_tfm(tfm); +++} ++diff -Nurp a/iscsi_tcp.h b/iscsi_tcp.h ++--- a/iscsi_tcp.h 2007-01-10 12:38:25.000000000 +0200 +++++ b/iscsi_tcp.h 2007-01-10 12:38:25.000000000 +0200 ++@@ -49,6 +49,7 @@ ++ #define ISCSI_SG_TABLESIZE SG_ALL ++ #define ISCSI_TCP_MAX_CMD_LEN 16 ++ +++#include "iscsi_2.6.20_compat.h" ++ struct crypto_hash; ++ struct socket; ++ ++diff -Nurp a/libiscsi.c b/libiscsi.c ++--- a/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 +++++ b/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 ++@@ -716,10 +716,9 @@ again: ++ return rc; ++ } ++ ++-static void iscsi_xmitworker(struct work_struct *work) +++static void iscsi_xmitworker(void *data) ++ { ++- struct iscsi_conn *conn = ++- container_of(work, struct iscsi_conn, xmitwork); +++ struct iscsi_conn *conn = data; ++ int rc; ++ /* ++ * serialize Xmit worker on a per-connection basis. ++@@ -1509,7 +1508,7 @@ iscsi_conn_setup(struct iscsi_cls_sessio ++ if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) ++ goto mgmtqueue_alloc_fail; ++ ++- INIT_WORK(&conn->xmitwork, iscsi_xmitworker); +++ INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn); ++ ++ /* allocate login_mtask used for the login/text sequences */ ++ spin_lock_bh(&session->lock); ++diff -Nurp a/libiscsi.h b/libiscsi.h ++--- a/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 +++++ b/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 ++@@ -25,8 +25,6 @@ ++ ++ #include ++ #include ++-#include ++-#include ++ #include "iscsi_proto.h" ++ #include "iscsi_if.h" ++ ++diff -Nurp a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c ++--- a/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 +++++ b/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 ++@@ -234,11 +234,9 @@ static int iscsi_user_scan(struct Scsi_H ++ return 0; ++ } ++ ++-static void session_recovery_timedout(struct work_struct *work) +++static void session_recovery_timedout(void *data) ++ { ++- struct iscsi_cls_session *session = ++- container_of(work, struct iscsi_cls_session, ++- recovery_work.work); +++ struct iscsi_cls_session *session = data; ++ ++ dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " ++ "out after %d secs\n", session->recovery_tmo); ++@@ -278,7 +276,7 @@ iscsi_alloc_session(struct Scsi_Host *sh ++ ++ session->transport = transport; ++ session->recovery_tmo = 120; ++- INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); +++ INIT_WORK(&session->recovery_work, session_recovery_timedout, session); ++ INIT_LIST_HEAD(&session->host_list); ++ INIT_LIST_HEAD(&session->sess_list); ++ ++diff -Nurp a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h ++--- a/scsi_transport_iscsi.h 2007-01-10 12:38:26.000000000 +0200 +++++ b/scsi_transport_iscsi.h 2007-01-10 12:38:25.000000000 +0200 ++@@ -176,7 +176,7 @@ struct iscsi_cls_session { ++ ++ /* recovery fields */ ++ int recovery_tmo; ++- struct delayed_work recovery_work; +++ struct work_struct recovery_work; ++ ++ int target_id; +Index: kernel/iscsi_tcp.c +=================================================================== +--- kernel/iscsi_tcp.c (revision 0) ++++ kernel/iscsi_tcp.c (revision 779) +@@ -0,0 +1,2235 @@ ++/* ++ * iSCSI Initiator over TCP/IP Data-Path ++ * ++ * Copyright (C) 2004 Dmitry Yusupov ++ * Copyright (C) 2004 Alex Aizman ++ * Copyright (C) 2005 - 2006 Mike Christie ++ * Copyright (C) 2006 Red Hat, Inc. All rights reserved. ++ * maintained by open-iscsi@googlegroups.com ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * See the file COPYING included with this distribution for more details. ++ * ++ * Credits: ++ * Christoph Hellwig ++ * FUJITA Tomonori ++ * Arne Redlich ++ * Zhenyu Wang ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "scsi_transport_iscsi.h" ++ ++#include "iscsi_tcp.h" ++ ++MODULE_AUTHOR("Dmitry Yusupov , " ++ "Alex Aizman "); ++MODULE_DESCRIPTION("iSCSI/TCP data-path"); ++MODULE_LICENSE("GPL"); ++/* #define DEBUG_TCP */ ++#define DEBUG_ASSERT ++ ++#ifdef DEBUG_TCP ++#define debug_tcp(fmt...) printk(KERN_INFO "tcp: " fmt) ++#else ++#define debug_tcp(fmt...) ++#endif ++ ++#ifndef DEBUG_ASSERT ++#ifdef BUG_ON ++#undef BUG_ON ++#endif ++#define BUG_ON(expr) ++#endif ++ ++static unsigned int iscsi_max_lun = 512; ++module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); ++ ++static inline void ++iscsi_buf_init_iov(struct iscsi_buf *ibuf, char *vbuf, int size) ++{ ++ ibuf->sg.page = virt_to_page(vbuf); ++ ibuf->sg.offset = offset_in_page(vbuf); ++ ibuf->sg.length = size; ++ ibuf->sent = 0; ++ ibuf->use_sendmsg = 1; ++} ++ ++static inline void ++iscsi_buf_init_sg(struct iscsi_buf *ibuf, struct scatterlist *sg) ++{ ++ ibuf->sg.page = sg->page; ++ ibuf->sg.offset = sg->offset; ++ ibuf->sg.length = sg->length; ++ /* ++ * Fastpath: sg element fits into single page ++ */ ++ if (sg->length + sg->offset <= PAGE_SIZE && !PageSlab(sg->page)) ++ ibuf->use_sendmsg = 0; ++ else ++ ibuf->use_sendmsg = 1; ++ ibuf->sent = 0; ++} ++ ++static inline int ++iscsi_buf_left(struct iscsi_buf *ibuf) ++{ ++ int rc; ++ ++ rc = ibuf->sg.length - ibuf->sent; ++ BUG_ON(rc < 0); ++ return rc; ++} ++ ++static inline void ++iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf, ++ u8* crc) ++{ ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ ++ crypto_hash_digest(&tcp_conn->tx_hash, &buf->sg, buf->sg.length, crc); ++ buf->sg.length = tcp_conn->hdr_size; ++} ++ ++static inline int ++iscsi_hdr_extract(struct iscsi_tcp_conn *tcp_conn) ++{ ++ struct sk_buff *skb = tcp_conn->in.skb; ++ ++ tcp_conn->in.zero_copy_hdr = 0; ++ ++ if (tcp_conn->in.copy >= tcp_conn->hdr_size && ++ tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) { ++ /* ++ * Zero-copy PDU Header: using connection context ++ * to store header pointer. ++ */ ++ if (skb_shinfo(skb)->frag_list == NULL && ++ !skb_shinfo(skb)->nr_frags) { ++ tcp_conn->in.hdr = (struct iscsi_hdr *) ++ ((char*)skb->data + tcp_conn->in.offset); ++ tcp_conn->in.zero_copy_hdr = 1; ++ } else { ++ /* ignoring return code since we checked ++ * in.copy before */ ++ skb_copy_bits(skb, tcp_conn->in.offset, ++ &tcp_conn->hdr, tcp_conn->hdr_size); ++ tcp_conn->in.hdr = &tcp_conn->hdr; ++ } ++ tcp_conn->in.offset += tcp_conn->hdr_size; ++ tcp_conn->in.copy -= tcp_conn->hdr_size; ++ } else { ++ int hdr_remains; ++ int copylen; ++ ++ /* ++ * PDU header scattered across SKB's, ++ * copying it... This'll happen quite rarely. ++ */ ++ ++ if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER) ++ tcp_conn->in.hdr_offset = 0; ++ ++ hdr_remains = tcp_conn->hdr_size - tcp_conn->in.hdr_offset; ++ BUG_ON(hdr_remains <= 0); ++ ++ copylen = min(tcp_conn->in.copy, hdr_remains); ++ skb_copy_bits(skb, tcp_conn->in.offset, ++ (char*)&tcp_conn->hdr + tcp_conn->in.hdr_offset, ++ copylen); ++ ++ debug_tcp("PDU gather offset %d bytes %d in.offset %d " ++ "in.copy %d\n", tcp_conn->in.hdr_offset, copylen, ++ tcp_conn->in.offset, tcp_conn->in.copy); ++ ++ tcp_conn->in.offset += copylen; ++ tcp_conn->in.copy -= copylen; ++ if (copylen < hdr_remains) { ++ tcp_conn->in_progress = IN_PROGRESS_HEADER_GATHER; ++ tcp_conn->in.hdr_offset += copylen; ++ return -EAGAIN; ++ } ++ tcp_conn->in.hdr = &tcp_conn->hdr; ++ tcp_conn->discontiguous_hdr_cnt++; ++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; ++ } ++ ++ return 0; ++} ++ ++/* ++ * must be called with session lock ++ */ ++static void ++iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct iscsi_r2t_info *r2t; ++ struct scsi_cmnd *sc; ++ ++ /* flush ctask's r2t queues */ ++ while (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*))) { ++ __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, ++ sizeof(void*)); ++ debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n"); ++ } ++ ++ sc = ctask->sc; ++ if (unlikely(!sc)) ++ return; ++ ++ tcp_ctask->xmstate = XMSTATE_IDLE; ++ tcp_ctask->r2t = NULL; ++} ++ ++/** ++ * iscsi_data_rsp - SCSI Data-In Response processing ++ * @conn: iscsi connection ++ * @ctask: scsi command task ++ **/ ++static int ++iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ++{ ++ int rc; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; ++ struct iscsi_session *session = conn->session; ++ int datasn = be32_to_cpu(rhdr->datasn); ++ ++ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr); ++ if (rc) ++ return rc; ++ /* ++ * setup Data-In byte counter (gets decremented..) ++ */ ++ ctask->data_count = tcp_conn->in.datalen; ++ ++ if (tcp_conn->in.datalen == 0) ++ return 0; ++ ++ if (ctask->datasn != datasn) ++ return ISCSI_ERR_DATASN; ++ ++ ctask->datasn++; ++ ++ tcp_ctask->data_offset = be32_to_cpu(rhdr->offset); ++ if (tcp_ctask->data_offset + tcp_conn->in.datalen > ctask->total_length) ++ return ISCSI_ERR_DATA_OFFSET; ++ ++ if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) { ++ struct scsi_cmnd *sc = ctask->sc; ++ ++ conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; ++ if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) { ++ int res_count = be32_to_cpu(rhdr->residual_count); ++ ++ if (res_count > 0 && ++ res_count <= sc->request_bufflen) { ++ sc->resid = res_count; ++ sc->result = (DID_OK << 16) | rhdr->cmd_status; ++ } else ++ sc->result = (DID_BAD_TARGET << 16) | ++ rhdr->cmd_status; ++ } else if (rhdr->flags & ISCSI_FLAG_DATA_OVERFLOW) { ++ sc->resid = be32_to_cpu(rhdr->residual_count); ++ sc->result = (DID_OK << 16) | rhdr->cmd_status; ++ } else ++ sc->result = (DID_OK << 16) | rhdr->cmd_status; ++ } ++ ++ conn->datain_pdus_cnt++; ++ return 0; ++} ++ ++/** ++ * iscsi_solicit_data_init - initialize first Data-Out ++ * @conn: iscsi connection ++ * @ctask: scsi command task ++ * @r2t: R2T info ++ * ++ * Notes: ++ * Initialize first Data-Out within this R2T sequence and finds ++ * proper data_offset within this SCSI command. ++ * ++ * This function is called with connection lock taken. ++ **/ ++static void ++iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, ++ struct iscsi_r2t_info *r2t) ++{ ++ struct iscsi_data *hdr; ++ struct scsi_cmnd *sc = ctask->sc; ++ ++ hdr = &r2t->dtask.hdr; ++ memset(hdr, 0, sizeof(struct iscsi_data)); ++ hdr->ttt = r2t->ttt; ++ hdr->datasn = cpu_to_be32(r2t->solicit_datasn); ++ r2t->solicit_datasn++; ++ hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; ++ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); ++ hdr->itt = ctask->hdr->itt; ++ hdr->exp_statsn = r2t->exp_statsn; ++ hdr->offset = cpu_to_be32(r2t->data_offset); ++ if (r2t->data_length > conn->max_xmit_dlength) { ++ hton24(hdr->dlength, conn->max_xmit_dlength); ++ r2t->data_count = conn->max_xmit_dlength; ++ hdr->flags = 0; ++ } else { ++ hton24(hdr->dlength, r2t->data_length); ++ r2t->data_count = r2t->data_length; ++ hdr->flags = ISCSI_FLAG_CMD_FINAL; ++ } ++ conn->dataout_pdus_cnt++; ++ ++ r2t->sent = 0; ++ ++ iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr, ++ sizeof(struct iscsi_hdr)); ++ ++ if (sc->use_sg) { ++ int i, sg_count = 0; ++ struct scatterlist *sg = sc->request_buffer; ++ ++ r2t->sg = NULL; ++ for (i = 0; i < sc->use_sg; i++, sg += 1) { ++ /* FIXME: prefetch ? */ ++ if (sg_count + sg->length > r2t->data_offset) { ++ int page_offset; ++ ++ /* sg page found! */ ++ ++ /* offset within this page */ ++ page_offset = r2t->data_offset - sg_count; ++ ++ /* fill in this buffer */ ++ iscsi_buf_init_sg(&r2t->sendbuf, sg); ++ r2t->sendbuf.sg.offset += page_offset; ++ r2t->sendbuf.sg.length -= page_offset; ++ ++ /* xmit logic will continue with next one */ ++ r2t->sg = sg + 1; ++ break; ++ } ++ sg_count += sg->length; ++ } ++ BUG_ON(r2t->sg == NULL); ++ } else { ++ iscsi_buf_init_iov(&r2t->sendbuf, ++ (char*)sc->request_buffer + r2t->data_offset, ++ r2t->data_count); ++ r2t->sg = NULL; ++ } ++} ++ ++/** ++ * iscsi_r2t_rsp - iSCSI R2T Response processing ++ * @conn: iscsi connection ++ * @ctask: scsi command task ++ **/ ++static int ++iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_r2t_info *r2t; ++ struct iscsi_session *session = conn->session; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr; ++ int r2tsn = be32_to_cpu(rhdr->r2tsn); ++ int rc; ++ ++ if (tcp_conn->in.datalen) { ++ printk(KERN_ERR "iscsi_tcp: invalid R2t with datalen %d\n", ++ tcp_conn->in.datalen); ++ return ISCSI_ERR_DATALEN; ++ } ++ ++ if (tcp_ctask->exp_r2tsn && tcp_ctask->exp_r2tsn != r2tsn) ++ return ISCSI_ERR_R2TSN; ++ ++ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr); ++ if (rc) ++ return rc; ++ ++ /* FIXME: use R2TSN to detect missing R2T */ ++ ++ /* fill-in new R2T associated with the task */ ++ spin_lock(&session->lock); ++ if (!ctask->sc || ctask->mtask || ++ session->state != ISCSI_STATE_LOGGED_IN) { ++ printk(KERN_INFO "iscsi_tcp: dropping R2T itt %d in " ++ "recovery...\n", ctask->itt); ++ spin_unlock(&session->lock); ++ return 0; ++ } ++ ++ rc = __kfifo_get(tcp_ctask->r2tpool.queue, (void*)&r2t, sizeof(void*)); ++ BUG_ON(!rc); ++ ++ r2t->exp_statsn = rhdr->statsn; ++ r2t->data_length = be32_to_cpu(rhdr->data_length); ++ if (r2t->data_length == 0) { ++ printk(KERN_ERR "iscsi_tcp: invalid R2T with zero data len\n"); ++ spin_unlock(&session->lock); ++ return ISCSI_ERR_DATALEN; ++ } ++ ++ if (r2t->data_length > session->max_burst) ++ debug_scsi("invalid R2T with data len %u and max burst %u." ++ "Attempting to execute request.\n", ++ r2t->data_length, session->max_burst); ++ ++ r2t->data_offset = be32_to_cpu(rhdr->data_offset); ++ if (r2t->data_offset + r2t->data_length > ctask->total_length) { ++ spin_unlock(&session->lock); ++ printk(KERN_ERR "iscsi_tcp: invalid R2T with data len %u at " ++ "offset %u and total length %d\n", r2t->data_length, ++ r2t->data_offset, ctask->total_length); ++ return ISCSI_ERR_DATALEN; ++ } ++ ++ r2t->ttt = rhdr->ttt; /* no flip */ ++ r2t->solicit_datasn = 0; ++ ++ iscsi_solicit_data_init(conn, ctask, r2t); ++ ++ tcp_ctask->exp_r2tsn = r2tsn + 1; ++ __kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*)); ++ tcp_ctask->xmstate |= XMSTATE_SOL_HDR; ++ list_move_tail(&ctask->running, &conn->xmitqueue); ++ ++ scsi_queue_work(session->host, &conn->xmitwork); ++ conn->r2t_pdus_cnt++; ++ spin_unlock(&session->lock); ++ ++ return 0; ++} ++ ++static int ++iscsi_tcp_hdr_recv(struct iscsi_conn *conn) ++{ ++ int rc = 0, opcode, ahslen; ++ struct iscsi_hdr *hdr; ++ struct iscsi_session *session = conn->session; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ uint32_t cdgst, rdgst = 0, itt; ++ ++ hdr = tcp_conn->in.hdr; ++ ++ /* verify PDU length */ ++ tcp_conn->in.datalen = ntoh24(hdr->dlength); ++ if (tcp_conn->in.datalen > conn->max_recv_dlength) { ++ printk(KERN_ERR "iscsi_tcp: datalen %d > %d\n", ++ tcp_conn->in.datalen, conn->max_recv_dlength); ++ return ISCSI_ERR_DATALEN; ++ } ++ tcp_conn->data_copied = 0; ++ ++ /* read AHS */ ++ ahslen = hdr->hlength << 2; ++ tcp_conn->in.offset += ahslen; ++ tcp_conn->in.copy -= ahslen; ++ if (tcp_conn->in.copy < 0) { ++ printk(KERN_ERR "iscsi_tcp: can't handle AHS with length " ++ "%d bytes\n", ahslen); ++ return ISCSI_ERR_AHSLEN; ++ } ++ ++ /* calculate read padding */ ++ tcp_conn->in.padding = tcp_conn->in.datalen & (ISCSI_PAD_LEN-1); ++ if (tcp_conn->in.padding) { ++ tcp_conn->in.padding = ISCSI_PAD_LEN - tcp_conn->in.padding; ++ debug_scsi("read padding %d bytes\n", tcp_conn->in.padding); ++ } ++ ++ if (conn->hdrdgst_en) { ++ struct scatterlist sg; ++ ++ sg_init_one(&sg, (u8 *)hdr, ++ sizeof(struct iscsi_hdr) + ahslen); ++ crypto_hash_digest(&tcp_conn->rx_hash, &sg, sg.length, ++ (u8 *)&cdgst); ++ rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) + ++ ahslen); ++ if (cdgst != rdgst) { ++ printk(KERN_ERR "iscsi_tcp: hdrdgst error " ++ "recv 0x%x calc 0x%x\n", rdgst, cdgst); ++ return ISCSI_ERR_HDR_DGST; ++ } ++ } ++ ++ opcode = hdr->opcode & ISCSI_OPCODE_MASK; ++ /* verify itt (itt encoding: age+cid+itt) */ ++ rc = iscsi_verify_itt(conn, hdr, &itt); ++ if (rc == ISCSI_ERR_NO_SCSI_CMD) { ++ tcp_conn->in.datalen = 0; /* force drop */ ++ return 0; ++ } else if (rc) ++ return rc; ++ ++ debug_tcp("opcode 0x%x offset %d copy %d ahslen %d datalen %d\n", ++ opcode, tcp_conn->in.offset, tcp_conn->in.copy, ++ ahslen, tcp_conn->in.datalen); ++ ++ switch(opcode) { ++ case ISCSI_OP_SCSI_DATA_IN: ++ tcp_conn->in.ctask = session->cmds[itt]; ++ rc = iscsi_data_rsp(conn, tcp_conn->in.ctask); ++ if (rc) ++ return rc; ++ /* fall through */ ++ case ISCSI_OP_SCSI_CMD_RSP: ++ tcp_conn->in.ctask = session->cmds[itt]; ++ if (tcp_conn->in.datalen) ++ goto copy_hdr; ++ ++ spin_lock(&session->lock); ++ rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); ++ spin_unlock(&session->lock); ++ break; ++ case ISCSI_OP_R2T: ++ tcp_conn->in.ctask = session->cmds[itt]; ++ if (ahslen) ++ rc = ISCSI_ERR_AHSLEN; ++ else if (tcp_conn->in.ctask->sc->sc_data_direction == ++ DMA_TO_DEVICE) ++ rc = iscsi_r2t_rsp(conn, tcp_conn->in.ctask); ++ else ++ rc = ISCSI_ERR_PROTO; ++ break; ++ case ISCSI_OP_LOGIN_RSP: ++ case ISCSI_OP_TEXT_RSP: ++ case ISCSI_OP_REJECT: ++ case ISCSI_OP_ASYNC_EVENT: ++ /* ++ * It is possible that we could get a PDU with a buffer larger ++ * than 8K, but there are no targets that currently do this. ++ * For now we fail until we find a vendor that needs it ++ */ ++ if (ISCSI_DEF_MAX_RECV_SEG_LEN < ++ tcp_conn->in.datalen) { ++ printk(KERN_ERR "iscsi_tcp: received buffer of len %u " ++ "but conn buffer is only %u (opcode %0x)\n", ++ tcp_conn->in.datalen, ++ ISCSI_DEF_MAX_RECV_SEG_LEN, opcode); ++ rc = ISCSI_ERR_PROTO; ++ break; ++ } ++ ++ if (tcp_conn->in.datalen) ++ goto copy_hdr; ++ /* fall through */ ++ case ISCSI_OP_LOGOUT_RSP: ++ case ISCSI_OP_NOOP_IN: ++ case ISCSI_OP_SCSI_TMFUNC_RSP: ++ rc = iscsi_complete_pdu(conn, hdr, NULL, 0); ++ break; ++ default: ++ rc = ISCSI_ERR_BAD_OPCODE; ++ break; ++ } ++ ++ return rc; ++ ++copy_hdr: ++ /* ++ * if we did zero copy for the header but we will need multiple ++ * skbs to complete the command then we have to copy the header ++ * for later use ++ */ ++ if (tcp_conn->in.zero_copy_hdr && tcp_conn->in.copy <= ++ (tcp_conn->in.datalen + tcp_conn->in.padding + ++ (conn->datadgst_en ? 4 : 0))) { ++ debug_tcp("Copying header for later use. in.copy %d in.datalen" ++ " %d\n", tcp_conn->in.copy, tcp_conn->in.datalen); ++ memcpy(&tcp_conn->hdr, tcp_conn->in.hdr, ++ sizeof(struct iscsi_hdr)); ++ tcp_conn->in.hdr = &tcp_conn->hdr; ++ tcp_conn->in.zero_copy_hdr = 0; ++ } ++ return 0; ++} ++ ++/** ++ * iscsi_ctask_copy - copy skb bits to the destanation cmd task ++ * @conn: iscsi tcp connection ++ * @ctask: scsi command task ++ * @buf: buffer to copy to ++ * @buf_size: size of buffer ++ * @offset: offset within the buffer ++ * ++ * Notes: ++ * The function calls skb_copy_bits() and updates per-connection and ++ * per-cmd byte counters. ++ * ++ * Read counters (in bytes): ++ * ++ * conn->in.offset offset within in progress SKB ++ * conn->in.copy left to copy from in progress SKB ++ * including padding ++ * conn->in.copied copied already from in progress SKB ++ * conn->data_copied copied already from in progress buffer ++ * ctask->sent total bytes sent up to the MidLayer ++ * ctask->data_count left to copy from in progress Data-In ++ * buf_left left to copy from in progress buffer ++ **/ ++static inline int ++iscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct iscsi_cmd_task *ctask, ++ void *buf, int buf_size, int offset) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ int buf_left = buf_size - (tcp_conn->data_copied + offset); ++ int size = min(tcp_conn->in.copy, buf_left); ++ int rc; ++ ++ size = min(size, ctask->data_count); ++ ++ debug_tcp("ctask_copy %d bytes at offset %d copied %d\n", ++ size, tcp_conn->in.offset, tcp_conn->in.copied); ++ ++ BUG_ON(size <= 0); ++ BUG_ON(tcp_ctask->sent + size > ctask->total_length); ++ ++ rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset, ++ (char*)buf + (offset + tcp_conn->data_copied), size); ++ /* must fit into skb->len */ ++ BUG_ON(rc); ++ ++ tcp_conn->in.offset += size; ++ tcp_conn->in.copy -= size; ++ tcp_conn->in.copied += size; ++ tcp_conn->data_copied += size; ++ tcp_ctask->sent += size; ++ ctask->data_count -= size; ++ ++ BUG_ON(tcp_conn->in.copy < 0); ++ BUG_ON(ctask->data_count < 0); ++ ++ if (buf_size != (tcp_conn->data_copied + offset)) { ++ if (!ctask->data_count) { ++ BUG_ON(buf_size - tcp_conn->data_copied < 0); ++ /* done with this PDU */ ++ return buf_size - tcp_conn->data_copied; ++ } ++ return -EAGAIN; ++ } ++ ++ /* done with this buffer or with both - PDU and buffer */ ++ tcp_conn->data_copied = 0; ++ return 0; ++} ++ ++/** ++ * iscsi_tcp_copy - copy skb bits to the destanation buffer ++ * @conn: iscsi tcp connection ++ * ++ * Notes: ++ * The function calls skb_copy_bits() and updates per-connection ++ * byte counters. ++ **/ ++static inline int ++iscsi_tcp_copy(struct iscsi_conn *conn, int buf_size) ++{ ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ int buf_left = buf_size - tcp_conn->data_copied; ++ int size = min(tcp_conn->in.copy, buf_left); ++ int rc; ++ ++ debug_tcp("tcp_copy %d bytes at offset %d copied %d\n", ++ size, tcp_conn->in.offset, tcp_conn->data_copied); ++ BUG_ON(size <= 0); ++ ++ rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset, ++ (char*)conn->data + tcp_conn->data_copied, size); ++ BUG_ON(rc); ++ ++ tcp_conn->in.offset += size; ++ tcp_conn->in.copy -= size; ++ tcp_conn->in.copied += size; ++ tcp_conn->data_copied += size; ++ ++ if (buf_size != tcp_conn->data_copied) ++ return -EAGAIN; ++ ++ return 0; ++} ++ ++static inline void ++partial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg, ++ int offset, int length) ++{ ++ struct scatterlist temp; ++ ++ memcpy(&temp, sg, sizeof(struct scatterlist)); ++ temp.offset = offset; ++ temp.length = length; ++ crypto_hash_update(desc, &temp, length); ++} ++ ++static void ++iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len) ++{ ++ struct scatterlist tmp; ++ ++ sg_init_one(&tmp, buf, len); ++ crypto_hash_update(&tcp_conn->rx_hash, &tmp, len); ++} ++ ++static int iscsi_scsi_data_in(struct iscsi_conn *conn) ++{ ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ struct iscsi_cmd_task *ctask = tcp_conn->in.ctask; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct scsi_cmnd *sc = ctask->sc; ++ struct scatterlist *sg; ++ int i, offset, rc = 0; ++ ++ BUG_ON((void*)ctask != sc->SCp.ptr); ++ ++ /* ++ * copying Data-In into the Scsi_Cmnd ++ */ ++ if (!sc->use_sg) { ++ i = ctask->data_count; ++ rc = iscsi_ctask_copy(tcp_conn, ctask, sc->request_buffer, ++ sc->request_bufflen, ++ tcp_ctask->data_offset); ++ if (rc == -EAGAIN) ++ return rc; ++ if (conn->datadgst_en) ++ iscsi_recv_digest_update(tcp_conn, sc->request_buffer, ++ i); ++ rc = 0; ++ goto done; ++ } ++ ++ offset = tcp_ctask->data_offset; ++ sg = sc->request_buffer; ++ ++ if (tcp_ctask->data_offset) ++ for (i = 0; i < tcp_ctask->sg_count; i++) ++ offset -= sg[i].length; ++ /* we've passed through partial sg*/ ++ if (offset < 0) ++ offset = 0; ++ ++ for (i = tcp_ctask->sg_count; i < sc->use_sg; i++) { ++ char *dest; ++ ++ dest = kmap_atomic(sg[i].page, KM_SOFTIRQ0); ++ rc = iscsi_ctask_copy(tcp_conn, ctask, dest + sg[i].offset, ++ sg[i].length, offset); ++ kunmap_atomic(dest, KM_SOFTIRQ0); ++ if (rc == -EAGAIN) ++ /* continue with the next SKB/PDU */ ++ return rc; ++ if (!rc) { ++ if (conn->datadgst_en) { ++ if (!offset) ++ crypto_hash_update( ++ &tcp_conn->rx_hash, ++ &sg[i], sg[i].length); ++ else ++ partial_sg_digest_update( ++ &tcp_conn->rx_hash, ++ &sg[i], ++ sg[i].offset + offset, ++ sg[i].length - offset); ++ } ++ offset = 0; ++ tcp_ctask->sg_count++; ++ } ++ ++ if (!ctask->data_count) { ++ if (rc && conn->datadgst_en) ++ /* ++ * data-in is complete, but buffer not... ++ */ ++ partial_sg_digest_update(&tcp_conn->rx_hash, ++ &sg[i], ++ sg[i].offset, ++ sg[i].length-rc); ++ rc = 0; ++ break; ++ } ++ ++ if (!tcp_conn->in.copy) ++ return -EAGAIN; ++ } ++ BUG_ON(ctask->data_count); ++ ++done: ++ /* check for non-exceptional status */ ++ if (tcp_conn->in.hdr->flags & ISCSI_FLAG_DATA_STATUS) { ++ debug_scsi("done [sc %lx res %d itt 0x%x flags 0x%x]\n", ++ (long)sc, sc->result, ctask->itt, ++ tcp_conn->in.hdr->flags); ++ spin_lock(&conn->session->lock); ++ __iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0); ++ spin_unlock(&conn->session->lock); ++ } ++ ++ return rc; ++} ++ ++static int ++iscsi_data_recv(struct iscsi_conn *conn) ++{ ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ int rc = 0, opcode; ++ ++ opcode = tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK; ++ switch (opcode) { ++ case ISCSI_OP_SCSI_DATA_IN: ++ rc = iscsi_scsi_data_in(conn); ++ break; ++ case ISCSI_OP_SCSI_CMD_RSP: ++ case ISCSI_OP_TEXT_RSP: ++ case ISCSI_OP_LOGIN_RSP: ++ case ISCSI_OP_ASYNC_EVENT: ++ case ISCSI_OP_REJECT: ++ /* ++ * Collect data segment to the connection's data ++ * placeholder ++ */ ++ if (iscsi_tcp_copy(conn, tcp_conn->in.datalen)) { ++ rc = -EAGAIN; ++ goto exit; ++ } ++ ++ rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, conn->data, ++ tcp_conn->in.datalen); ++ if (!rc && conn->datadgst_en && opcode != ISCSI_OP_LOGIN_RSP) ++ iscsi_recv_digest_update(tcp_conn, conn->data, ++ tcp_conn->in.datalen); ++ break; ++ default: ++ BUG_ON(1); ++ } ++exit: ++ return rc; ++} ++ ++/** ++ * iscsi_tcp_data_recv - TCP receive in sendfile fashion ++ * @rd_desc: read descriptor ++ * @skb: socket buffer ++ * @offset: offset in skb ++ * @len: skb->len - offset ++ **/ ++static int ++iscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, ++ unsigned int offset, size_t len) ++{ ++ int rc; ++ struct iscsi_conn *conn = rd_desc->arg.data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ int processed; ++ char pad[ISCSI_PAD_LEN]; ++ struct scatterlist sg; ++ ++ /* ++ * Save current SKB and its offset in the corresponding ++ * connection context. ++ */ ++ tcp_conn->in.copy = skb->len - offset; ++ tcp_conn->in.offset = offset; ++ tcp_conn->in.skb = skb; ++ tcp_conn->in.len = tcp_conn->in.copy; ++ BUG_ON(tcp_conn->in.copy <= 0); ++ debug_tcp("in %d bytes\n", tcp_conn->in.copy); ++ ++more: ++ tcp_conn->in.copied = 0; ++ rc = 0; ++ ++ if (unlikely(conn->suspend_rx)) { ++ debug_tcp("conn %d Rx suspended!\n", conn->id); ++ return 0; ++ } ++ ++ if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER || ++ tcp_conn->in_progress == IN_PROGRESS_HEADER_GATHER) { ++ rc = iscsi_hdr_extract(tcp_conn); ++ if (rc) { ++ if (rc == -EAGAIN) ++ goto nomore; ++ else { ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ return 0; ++ } ++ } ++ ++ /* ++ * Verify and process incoming PDU header. ++ */ ++ rc = iscsi_tcp_hdr_recv(conn); ++ if (!rc && tcp_conn->in.datalen) { ++ if (conn->datadgst_en) ++ crypto_hash_init(&tcp_conn->rx_hash); ++ tcp_conn->in_progress = IN_PROGRESS_DATA_RECV; ++ } else if (rc) { ++ iscsi_conn_failure(conn, rc); ++ return 0; ++ } ++ } ++ ++ if (tcp_conn->in_progress == IN_PROGRESS_DDIGEST_RECV) { ++ uint32_t recv_digest; ++ ++ debug_tcp("extra data_recv offset %d copy %d\n", ++ tcp_conn->in.offset, tcp_conn->in.copy); ++ rc = iscsi_tcp_copy(conn, sizeof(uint32_t)); ++ if (rc) { ++ if (rc == -EAGAIN) ++ goto again; ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ return 0; ++ } ++ ++ memcpy(&recv_digest, conn->data, sizeof(uint32_t)); ++ if (recv_digest != tcp_conn->in.datadgst) { ++ debug_tcp("iscsi_tcp: data digest error!" ++ "0x%x != 0x%x\n", recv_digest, ++ tcp_conn->in.datadgst); ++ iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST); ++ return 0; ++ } else { ++ debug_tcp("iscsi_tcp: data digest match!" ++ "0x%x == 0x%x\n", recv_digest, ++ tcp_conn->in.datadgst); ++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; ++ } ++ } ++ ++ if (tcp_conn->in_progress == IN_PROGRESS_DATA_RECV && ++ tcp_conn->in.copy) { ++ ++ debug_tcp("data_recv offset %d copy %d\n", ++ tcp_conn->in.offset, tcp_conn->in.copy); ++ ++ rc = iscsi_data_recv(conn); ++ if (rc) { ++ if (rc == -EAGAIN) ++ goto again; ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ return 0; ++ } ++ tcp_conn->in.copy -= tcp_conn->in.padding; ++ tcp_conn->in.offset += tcp_conn->in.padding; ++ if (conn->datadgst_en) { ++ if (tcp_conn->in.padding) { ++ debug_tcp("padding -> %d\n", ++ tcp_conn->in.padding); ++ memset(pad, 0, tcp_conn->in.padding); ++ sg_init_one(&sg, pad, tcp_conn->in.padding); ++ crypto_hash_update(&tcp_conn->rx_hash, ++ &sg, sg.length); ++ } ++ crypto_hash_final(&tcp_conn->rx_hash, ++ (u8 *) &tcp_conn->in.datadgst); ++ debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst); ++ tcp_conn->in_progress = IN_PROGRESS_DDIGEST_RECV; ++ tcp_conn->data_copied = 0; ++ } else ++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; ++ } ++ ++ debug_tcp("f, processed %d from out of %d padding %d\n", ++ tcp_conn->in.offset - offset, (int)len, tcp_conn->in.padding); ++ BUG_ON(tcp_conn->in.offset - offset > len); ++ ++ if (tcp_conn->in.offset - offset != len) { ++ debug_tcp("continue to process %d bytes\n", ++ (int)len - (tcp_conn->in.offset - offset)); ++ goto more; ++ } ++ ++nomore: ++ processed = tcp_conn->in.offset - offset; ++ BUG_ON(processed == 0); ++ return processed; ++ ++again: ++ processed = tcp_conn->in.offset - offset; ++ debug_tcp("c, processed %d from out of %d rd_desc_cnt %d\n", ++ processed, (int)len, (int)rd_desc->count); ++ BUG_ON(processed == 0); ++ BUG_ON(processed > len); ++ ++ conn->rxdata_octets += processed; ++ return processed; ++} ++ ++static void ++iscsi_tcp_data_ready(struct sock *sk, int flag) ++{ ++ struct iscsi_conn *conn = sk->sk_user_data; ++ read_descriptor_t rd_desc; ++ ++ read_lock(&sk->sk_callback_lock); ++ ++ /* ++ * Use rd_desc to pass 'conn' to iscsi_tcp_data_recv. ++ * We set count to 1 because we want the network layer to ++ * hand us all the skbs that are available. iscsi_tcp_data_recv ++ * handled pdus that cross buffers or pdus that still need data. ++ */ ++ rd_desc.arg.data = conn; ++ rd_desc.count = 1; ++ tcp_read_sock(sk, &rd_desc, iscsi_tcp_data_recv); ++ ++ read_unlock(&sk->sk_callback_lock); ++} ++ ++static void ++iscsi_tcp_state_change(struct sock *sk) ++{ ++ struct iscsi_tcp_conn *tcp_conn; ++ struct iscsi_conn *conn; ++ struct iscsi_session *session; ++ void (*old_state_change)(struct sock *); ++ ++ read_lock(&sk->sk_callback_lock); ++ ++ conn = (struct iscsi_conn*)sk->sk_user_data; ++ session = conn->session; ++ ++ if ((sk->sk_state == TCP_CLOSE_WAIT || ++ sk->sk_state == TCP_CLOSE) && ++ !atomic_read(&sk->sk_rmem_alloc)) { ++ debug_tcp("iscsi_tcp_state_change: TCP_CLOSE|TCP_CLOSE_WAIT\n"); ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ } ++ ++ tcp_conn = conn->dd_data; ++ old_state_change = tcp_conn->old_state_change; ++ ++ read_unlock(&sk->sk_callback_lock); ++ ++ old_state_change(sk); ++} ++ ++/** ++ * iscsi_write_space - Called when more output buffer space is available ++ * @sk: socket space is available for ++ **/ ++static void ++iscsi_write_space(struct sock *sk) ++{ ++ struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ ++ tcp_conn->old_write_space(sk); ++ debug_tcp("iscsi_write_space: cid %d\n", conn->id); ++ scsi_queue_work(conn->session->host, &conn->xmitwork); ++} ++ ++static void ++iscsi_conn_set_callbacks(struct iscsi_conn *conn) ++{ ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ struct sock *sk = tcp_conn->sock->sk; ++ ++ /* assign new callbacks */ ++ write_lock_bh(&sk->sk_callback_lock); ++ sk->sk_user_data = conn; ++ tcp_conn->old_data_ready = sk->sk_data_ready; ++ tcp_conn->old_state_change = sk->sk_state_change; ++ tcp_conn->old_write_space = sk->sk_write_space; ++ sk->sk_data_ready = iscsi_tcp_data_ready; ++ sk->sk_state_change = iscsi_tcp_state_change; ++ sk->sk_write_space = iscsi_write_space; ++ write_unlock_bh(&sk->sk_callback_lock); ++} ++ ++static void ++iscsi_conn_restore_callbacks(struct iscsi_tcp_conn *tcp_conn) ++{ ++ struct sock *sk = tcp_conn->sock->sk; ++ ++ /* restore socket callbacks, see also: iscsi_conn_set_callbacks() */ ++ write_lock_bh(&sk->sk_callback_lock); ++ sk->sk_user_data = NULL; ++ sk->sk_data_ready = tcp_conn->old_data_ready; ++ sk->sk_state_change = tcp_conn->old_state_change; ++ sk->sk_write_space = tcp_conn->old_write_space; ++ sk->sk_no_check = 0; ++ write_unlock_bh(&sk->sk_callback_lock); ++} ++ ++/** ++ * iscsi_send - generic send routine ++ * @sk: kernel's socket ++ * @buf: buffer to write from ++ * @size: actual size to write ++ * @flags: socket's flags ++ */ ++static inline int ++iscsi_send(struct iscsi_conn *conn, struct iscsi_buf *buf, int size, int flags) ++{ ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ struct socket *sk = tcp_conn->sock; ++ int offset = buf->sg.offset + buf->sent, res; ++ ++ /* ++ * if we got use_sg=0 or are sending something we kmallocd ++ * then we did not have to do kmap (kmap returns page_address) ++ * ++ * if we got use_sg > 0, but had to drop down, we do not ++ * set clustering so this should only happen for that ++ * slab case. ++ */ ++ if (buf->use_sendmsg) ++ res = sock_no_sendpage(sk, buf->sg.page, offset, size, flags); ++ else ++ res = tcp_conn->sendpage(sk, buf->sg.page, offset, size, flags); ++ ++ if (res >= 0) { ++ conn->txdata_octets += res; ++ buf->sent += res; ++ return res; ++ } ++ ++ tcp_conn->sendpage_failures_cnt++; ++ if (res == -EAGAIN) ++ res = -ENOBUFS; ++ else ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ return res; ++} ++ ++/** ++ * iscsi_sendhdr - send PDU Header via tcp_sendpage() ++ * @conn: iscsi connection ++ * @buf: buffer to write from ++ * @datalen: lenght of data to be sent after the header ++ * ++ * Notes: ++ * (Tx, Fast Path) ++ **/ ++static inline int ++iscsi_sendhdr(struct iscsi_conn *conn, struct iscsi_buf *buf, int datalen) ++{ ++ int flags = 0; /* MSG_DONTWAIT; */ ++ int res, size; ++ ++ size = buf->sg.length - buf->sent; ++ BUG_ON(buf->sent + size > buf->sg.length); ++ if (buf->sent + size != buf->sg.length || datalen) ++ flags |= MSG_MORE; ++ ++ res = iscsi_send(conn, buf, size, flags); ++ debug_tcp("sendhdr %d bytes, sent %d res %d\n", size, buf->sent, res); ++ if (res >= 0) { ++ if (size != res) ++ return -EAGAIN; ++ return 0; ++ } ++ ++ return res; ++} ++ ++/** ++ * iscsi_sendpage - send one page of iSCSI Data-Out. ++ * @conn: iscsi connection ++ * @buf: buffer to write from ++ * @count: remaining data ++ * @sent: number of bytes sent ++ * ++ * Notes: ++ * (Tx, Fast Path) ++ **/ ++static inline int ++iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf, ++ int *count, int *sent) ++{ ++ int flags = 0; /* MSG_DONTWAIT; */ ++ int res, size; ++ ++ size = buf->sg.length - buf->sent; ++ BUG_ON(buf->sent + size > buf->sg.length); ++ if (size > *count) ++ size = *count; ++ if (buf->sent + size != buf->sg.length || *count != size) ++ flags |= MSG_MORE; ++ ++ res = iscsi_send(conn, buf, size, flags); ++ debug_tcp("sendpage: %d bytes, sent %d left %d sent %d res %d\n", ++ size, buf->sent, *count, *sent, res); ++ if (res >= 0) { ++ *count -= res; ++ *sent += res; ++ if (size != res) ++ return -EAGAIN; ++ return 0; ++ } ++ ++ return res; ++} ++ ++static inline void ++iscsi_data_digest_init(struct iscsi_tcp_conn *tcp_conn, ++ struct iscsi_tcp_cmd_task *tcp_ctask) ++{ ++ crypto_hash_init(&tcp_conn->tx_hash); ++ tcp_ctask->digest_count = 4; ++} ++ ++/** ++ * iscsi_solicit_data_cont - initialize next Data-Out ++ * @conn: iscsi connection ++ * @ctask: scsi command task ++ * @r2t: R2T info ++ * @left: bytes left to transfer ++ * ++ * Notes: ++ * Initialize next Data-Out within this R2T sequence and continue ++ * to process next Scatter-Gather element(if any) of this SCSI command. ++ * ++ * Called under connection lock. ++ **/ ++static void ++iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, ++ struct iscsi_r2t_info *r2t, int left) ++{ ++ struct iscsi_data *hdr; ++ struct scsi_cmnd *sc = ctask->sc; ++ int new_offset; ++ ++ hdr = &r2t->dtask.hdr; ++ memset(hdr, 0, sizeof(struct iscsi_data)); ++ hdr->ttt = r2t->ttt; ++ hdr->datasn = cpu_to_be32(r2t->solicit_datasn); ++ r2t->solicit_datasn++; ++ hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; ++ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); ++ hdr->itt = ctask->hdr->itt; ++ hdr->exp_statsn = r2t->exp_statsn; ++ new_offset = r2t->data_offset + r2t->sent; ++ hdr->offset = cpu_to_be32(new_offset); ++ if (left > conn->max_xmit_dlength) { ++ hton24(hdr->dlength, conn->max_xmit_dlength); ++ r2t->data_count = conn->max_xmit_dlength; ++ } else { ++ hton24(hdr->dlength, left); ++ r2t->data_count = left; ++ hdr->flags = ISCSI_FLAG_CMD_FINAL; ++ } ++ conn->dataout_pdus_cnt++; ++ ++ iscsi_buf_init_iov(&r2t->headbuf, (char*)hdr, ++ sizeof(struct iscsi_hdr)); ++ ++ if (iscsi_buf_left(&r2t->sendbuf)) ++ return; ++ ++ if (sc->use_sg) { ++ iscsi_buf_init_sg(&r2t->sendbuf, r2t->sg); ++ r2t->sg += 1; ++ } else { ++ iscsi_buf_init_iov(&r2t->sendbuf, ++ (char*)sc->request_buffer + new_offset, ++ r2t->data_count); ++ r2t->sg = NULL; ++ } ++} ++ ++static void iscsi_set_padding(struct iscsi_tcp_cmd_task *tcp_ctask, ++ unsigned long len) ++{ ++ tcp_ctask->pad_count = len & (ISCSI_PAD_LEN - 1); ++ if (!tcp_ctask->pad_count) ++ return; ++ ++ tcp_ctask->pad_count = ISCSI_PAD_LEN - tcp_ctask->pad_count; ++ debug_scsi("write padding %d bytes\n", tcp_ctask->pad_count); ++ tcp_ctask->xmstate |= XMSTATE_W_PAD; ++} ++ ++/** ++ * iscsi_tcp_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands ++ * @conn: iscsi connection ++ * @ctask: scsi command task ++ * @sc: scsi command ++ **/ ++static void ++iscsi_tcp_cmd_init(struct iscsi_cmd_task *ctask) ++{ ++ struct scsi_cmnd *sc = ctask->sc; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ ++ BUG_ON(__kfifo_len(tcp_ctask->r2tqueue)); ++ ++ tcp_ctask->sent = 0; ++ tcp_ctask->sg_count = 0; ++ ++ if (sc->sc_data_direction == DMA_TO_DEVICE) { ++ tcp_ctask->xmstate = XMSTATE_W_HDR; ++ tcp_ctask->exp_r2tsn = 0; ++ BUG_ON(ctask->total_length == 0); ++ ++ if (sc->use_sg) { ++ struct scatterlist *sg = sc->request_buffer; ++ ++ iscsi_buf_init_sg(&tcp_ctask->sendbuf, sg); ++ tcp_ctask->sg = sg + 1; ++ tcp_ctask->bad_sg = sg + sc->use_sg; ++ } else { ++ iscsi_buf_init_iov(&tcp_ctask->sendbuf, ++ sc->request_buffer, ++ sc->request_bufflen); ++ tcp_ctask->sg = NULL; ++ tcp_ctask->bad_sg = NULL; ++ } ++ debug_scsi("cmd [itt 0x%x total %d imm_data %d " ++ "unsol count %d, unsol offset %d]\n", ++ ctask->itt, ctask->total_length, ctask->imm_count, ++ ctask->unsol_count, ctask->unsol_offset); ++ } else ++ tcp_ctask->xmstate = XMSTATE_R_HDR; ++ ++ iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)ctask->hdr, ++ sizeof(struct iscsi_hdr)); ++} ++ ++/** ++ * iscsi_tcp_mtask_xmit - xmit management(immediate) task ++ * @conn: iscsi connection ++ * @mtask: task management task ++ * ++ * Notes: ++ * The function can return -EAGAIN in which case caller must ++ * call it again later, or recover. '0' return code means successful ++ * xmit. ++ * ++ * Management xmit state machine consists of two states: ++ * IN_PROGRESS_IMM_HEAD - PDU Header xmit in progress ++ * IN_PROGRESS_IMM_DATA - PDU Data xmit in progress ++ **/ ++static int ++iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask) ++{ ++ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; ++ int rc; ++ ++ debug_scsi("mtask deq [cid %d state %x itt 0x%x]\n", ++ conn->id, tcp_mtask->xmstate, mtask->itt); ++ ++ if (tcp_mtask->xmstate & XMSTATE_IMM_HDR) { ++ tcp_mtask->xmstate &= ~XMSTATE_IMM_HDR; ++ if (mtask->data_count) ++ tcp_mtask->xmstate |= XMSTATE_IMM_DATA; ++ if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE && ++ conn->stop_stage != STOP_CONN_RECOVER && ++ conn->hdrdgst_en) ++ iscsi_hdr_digest(conn, &tcp_mtask->headbuf, ++ (u8*)tcp_mtask->hdrext); ++ rc = iscsi_sendhdr(conn, &tcp_mtask->headbuf, ++ mtask->data_count); ++ if (rc) { ++ tcp_mtask->xmstate |= XMSTATE_IMM_HDR; ++ if (mtask->data_count) ++ tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA; ++ return rc; ++ } ++ } ++ ++ if (tcp_mtask->xmstate & XMSTATE_IMM_DATA) { ++ BUG_ON(!mtask->data_count); ++ tcp_mtask->xmstate &= ~XMSTATE_IMM_DATA; ++ /* FIXME: implement. ++ * Virtual buffer could be spreaded across multiple pages... ++ */ ++ do { ++ int rc; ++ ++ rc = iscsi_sendpage(conn, &tcp_mtask->sendbuf, ++ &mtask->data_count, &tcp_mtask->sent); ++ if (rc) { ++ tcp_mtask->xmstate |= XMSTATE_IMM_DATA; ++ return rc; ++ } ++ } while (mtask->data_count); ++ } ++ ++ BUG_ON(tcp_mtask->xmstate != XMSTATE_IDLE); ++ if (mtask->hdr->itt == RESERVED_ITT) { ++ struct iscsi_session *session = conn->session; ++ ++ spin_lock_bh(&session->lock); ++ list_del(&conn->mtask->running); ++ __kfifo_put(session->mgmtpool.queue, (void*)&conn->mtask, ++ sizeof(void*)); ++ spin_unlock_bh(&session->lock); ++ } ++ return 0; ++} ++ ++static inline int ++iscsi_send_read_hdr(struct iscsi_conn *conn, ++ struct iscsi_tcp_cmd_task *tcp_ctask) ++{ ++ int rc; ++ ++ tcp_ctask->xmstate &= ~XMSTATE_R_HDR; ++ if (conn->hdrdgst_en) ++ iscsi_hdr_digest(conn, &tcp_ctask->headbuf, ++ (u8*)tcp_ctask->hdrext); ++ rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, 0); ++ if (!rc) { ++ BUG_ON(tcp_ctask->xmstate != XMSTATE_IDLE); ++ return 0; /* wait for Data-In */ ++ } ++ tcp_ctask->xmstate |= XMSTATE_R_HDR; ++ return rc; ++} ++ ++static inline int ++iscsi_send_write_hdr(struct iscsi_conn *conn, ++ struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ int rc; ++ ++ tcp_ctask->xmstate &= ~XMSTATE_W_HDR; ++ if (conn->hdrdgst_en) ++ iscsi_hdr_digest(conn, &tcp_ctask->headbuf, ++ (u8*)tcp_ctask->hdrext); ++ rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->imm_count); ++ if (rc) { ++ tcp_ctask->xmstate |= XMSTATE_W_HDR; ++ return rc; ++ } ++ ++ if (ctask->imm_count) { ++ tcp_ctask->xmstate |= XMSTATE_IMM_DATA; ++ iscsi_set_padding(tcp_ctask, ctask->imm_count); ++ ++ if (ctask->conn->datadgst_en) { ++ iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask); ++ tcp_ctask->immdigest = 0; ++ } ++ } ++ ++ if (ctask->unsol_count) ++ tcp_ctask->xmstate |= XMSTATE_UNS_HDR | XMSTATE_UNS_INIT; ++ return 0; ++} ++ ++static int ++iscsi_send_padding(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ int sent = 0, rc; ++ ++ if (tcp_ctask->xmstate & XMSTATE_W_PAD) { ++ iscsi_buf_init_iov(&tcp_ctask->sendbuf, (char*)&tcp_ctask->pad, ++ tcp_ctask->pad_count); ++ if (conn->datadgst_en) ++ crypto_hash_update(&tcp_conn->tx_hash, ++ &tcp_ctask->sendbuf.sg, ++ tcp_ctask->sendbuf.sg.length); ++ } else if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_PAD)) ++ return 0; ++ ++ tcp_ctask->xmstate &= ~XMSTATE_W_PAD; ++ tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_PAD; ++ debug_scsi("sending %d pad bytes for itt 0x%x\n", ++ tcp_ctask->pad_count, ctask->itt); ++ rc = iscsi_sendpage(conn, &tcp_ctask->sendbuf, &tcp_ctask->pad_count, ++ &sent); ++ if (rc) { ++ debug_scsi("padding send failed %d\n", rc); ++ tcp_ctask->xmstate |= XMSTATE_W_RESEND_PAD; ++ } ++ return rc; ++} ++ ++static int ++iscsi_send_digest(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, ++ struct iscsi_buf *buf, uint32_t *digest) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask; ++ struct iscsi_tcp_conn *tcp_conn; ++ int rc, sent = 0; ++ ++ if (!conn->datadgst_en) ++ return 0; ++ ++ tcp_ctask = ctask->dd_data; ++ tcp_conn = conn->dd_data; ++ ++ if (!(tcp_ctask->xmstate & XMSTATE_W_RESEND_DATA_DIGEST)) { ++ crypto_hash_final(&tcp_conn->tx_hash, (u8*)digest); ++ iscsi_buf_init_iov(buf, (char*)digest, 4); ++ } ++ tcp_ctask->xmstate &= ~XMSTATE_W_RESEND_DATA_DIGEST; ++ ++ rc = iscsi_sendpage(conn, buf, &tcp_ctask->digest_count, &sent); ++ if (!rc) ++ debug_scsi("sent digest 0x%x for itt 0x%x\n", *digest, ++ ctask->itt); ++ else { ++ debug_scsi("sending digest 0x%x failed for itt 0x%x!\n", ++ *digest, ctask->itt); ++ tcp_ctask->xmstate |= XMSTATE_W_RESEND_DATA_DIGEST; ++ } ++ return rc; ++} ++ ++static int ++iscsi_send_data(struct iscsi_cmd_task *ctask, struct iscsi_buf *sendbuf, ++ struct scatterlist **sg, int *sent, int *count, ++ struct iscsi_buf *digestbuf, uint32_t *digest) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct iscsi_conn *conn = ctask->conn; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ int rc, buf_sent, offset; ++ ++ while (*count) { ++ buf_sent = 0; ++ offset = sendbuf->sent; ++ ++ rc = iscsi_sendpage(conn, sendbuf, count, &buf_sent); ++ *sent = *sent + buf_sent; ++ if (buf_sent && conn->datadgst_en) ++ partial_sg_digest_update(&tcp_conn->tx_hash, ++ &sendbuf->sg, sendbuf->sg.offset + offset, ++ buf_sent); ++ if (!iscsi_buf_left(sendbuf) && *sg != tcp_ctask->bad_sg) { ++ iscsi_buf_init_sg(sendbuf, *sg); ++ *sg = *sg + 1; ++ } ++ ++ if (rc) ++ return rc; ++ } ++ ++ rc = iscsi_send_padding(conn, ctask); ++ if (rc) ++ return rc; ++ ++ return iscsi_send_digest(conn, ctask, digestbuf, digest); ++} ++ ++static int ++iscsi_send_unsol_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct iscsi_data_task *dtask; ++ int rc; ++ ++ tcp_ctask->xmstate |= XMSTATE_UNS_DATA; ++ if (tcp_ctask->xmstate & XMSTATE_UNS_INIT) { ++ dtask = &tcp_ctask->unsol_dtask; ++ ++ iscsi_prep_unsolicit_data_pdu(ctask, &dtask->hdr); ++ iscsi_buf_init_iov(&tcp_ctask->headbuf, (char*)&dtask->hdr, ++ sizeof(struct iscsi_hdr)); ++ if (conn->hdrdgst_en) ++ iscsi_hdr_digest(conn, &tcp_ctask->headbuf, ++ (u8*)dtask->hdrext); ++ ++ tcp_ctask->xmstate &= ~XMSTATE_UNS_INIT; ++ iscsi_set_padding(tcp_ctask, ctask->data_count); ++ } ++ ++ rc = iscsi_sendhdr(conn, &tcp_ctask->headbuf, ctask->data_count); ++ if (rc) { ++ tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; ++ tcp_ctask->xmstate |= XMSTATE_UNS_HDR; ++ return rc; ++ } ++ ++ if (conn->datadgst_en) { ++ dtask = &tcp_ctask->unsol_dtask; ++ iscsi_data_digest_init(ctask->conn->dd_data, tcp_ctask); ++ dtask->digest = 0; ++ } ++ ++ debug_scsi("uns dout [itt 0x%x dlen %d sent %d]\n", ++ ctask->itt, ctask->unsol_count, tcp_ctask->sent); ++ return 0; ++} ++ ++static int ++iscsi_send_unsol_pdu(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ int rc; ++ ++ if (tcp_ctask->xmstate & XMSTATE_UNS_HDR) { ++ BUG_ON(!ctask->unsol_count); ++ tcp_ctask->xmstate &= ~XMSTATE_UNS_HDR; ++send_hdr: ++ rc = iscsi_send_unsol_hdr(conn, ctask); ++ if (rc) ++ return rc; ++ } ++ ++ if (tcp_ctask->xmstate & XMSTATE_UNS_DATA) { ++ struct iscsi_data_task *dtask = &tcp_ctask->unsol_dtask; ++ int start = tcp_ctask->sent; ++ ++ rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg, ++ &tcp_ctask->sent, &ctask->data_count, ++ &dtask->digestbuf, &dtask->digest); ++ ctask->unsol_count -= tcp_ctask->sent - start; ++ if (rc) ++ return rc; ++ tcp_ctask->xmstate &= ~XMSTATE_UNS_DATA; ++ /* ++ * Done with the Data-Out. Next, check if we need ++ * to send another unsolicited Data-Out. ++ */ ++ if (ctask->unsol_count) { ++ debug_scsi("sending more uns\n"); ++ tcp_ctask->xmstate |= XMSTATE_UNS_INIT; ++ goto send_hdr; ++ } ++ } ++ return 0; ++} ++ ++static int iscsi_send_sol_pdu(struct iscsi_conn *conn, ++ struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ struct iscsi_session *session = conn->session; ++ struct iscsi_r2t_info *r2t; ++ struct iscsi_data_task *dtask; ++ int left, rc; ++ ++ if (tcp_ctask->xmstate & XMSTATE_SOL_HDR) { ++ tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; ++ tcp_ctask->xmstate |= XMSTATE_SOL_DATA; ++ if (!tcp_ctask->r2t) { ++ spin_lock_bh(&session->lock); ++ __kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t, ++ sizeof(void*)); ++ spin_unlock_bh(&session->lock); ++ } ++send_hdr: ++ r2t = tcp_ctask->r2t; ++ dtask = &r2t->dtask; ++ ++ if (conn->hdrdgst_en) ++ iscsi_hdr_digest(conn, &r2t->headbuf, ++ (u8*)dtask->hdrext); ++ rc = iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count); ++ if (rc) { ++ tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; ++ tcp_ctask->xmstate |= XMSTATE_SOL_HDR; ++ return rc; ++ } ++ ++ if (conn->datadgst_en) { ++ iscsi_data_digest_init(conn->dd_data, tcp_ctask); ++ dtask->digest = 0; ++ } ++ ++ iscsi_set_padding(tcp_ctask, r2t->data_count); ++ debug_scsi("sol dout [dsn %d itt 0x%x dlen %d sent %d]\n", ++ r2t->solicit_datasn - 1, ctask->itt, r2t->data_count, ++ r2t->sent); ++ } ++ ++ if (tcp_ctask->xmstate & XMSTATE_SOL_DATA) { ++ r2t = tcp_ctask->r2t; ++ dtask = &r2t->dtask; ++ ++ rc = iscsi_send_data(ctask, &r2t->sendbuf, &r2t->sg, ++ &r2t->sent, &r2t->data_count, ++ &dtask->digestbuf, &dtask->digest); ++ if (rc) ++ return rc; ++ tcp_ctask->xmstate &= ~XMSTATE_SOL_DATA; ++ ++ /* ++ * Done with this Data-Out. Next, check if we have ++ * to send another Data-Out for this R2T. ++ */ ++ BUG_ON(r2t->data_length - r2t->sent < 0); ++ left = r2t->data_length - r2t->sent; ++ if (left) { ++ iscsi_solicit_data_cont(conn, ctask, r2t, left); ++ tcp_ctask->xmstate |= XMSTATE_SOL_DATA; ++ tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; ++ goto send_hdr; ++ } ++ ++ /* ++ * Done with this R2T. Check if there are more ++ * outstanding R2Ts ready to be processed. ++ */ ++ spin_lock_bh(&session->lock); ++ tcp_ctask->r2t = NULL; ++ __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t, ++ sizeof(void*)); ++ if (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, ++ sizeof(void*))) { ++ tcp_ctask->r2t = r2t; ++ tcp_ctask->xmstate |= XMSTATE_SOL_DATA; ++ tcp_ctask->xmstate &= ~XMSTATE_SOL_HDR; ++ spin_unlock_bh(&session->lock); ++ goto send_hdr; ++ } ++ spin_unlock_bh(&session->lock); ++ } ++ return 0; ++} ++ ++static int ++iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ int rc = 0; ++ ++ debug_scsi("ctask deq [cid %d xmstate %x itt 0x%x]\n", ++ conn->id, tcp_ctask->xmstate, ctask->itt); ++ ++ /* ++ * serialize with TMF AbortTask ++ */ ++ if (ctask->mtask) ++ return rc; ++ ++ if (tcp_ctask->xmstate & XMSTATE_R_HDR) ++ return iscsi_send_read_hdr(conn, tcp_ctask); ++ ++ if (tcp_ctask->xmstate & XMSTATE_W_HDR) { ++ rc = iscsi_send_write_hdr(conn, ctask); ++ if (rc) ++ return rc; ++ } ++ ++ if (tcp_ctask->xmstate & XMSTATE_IMM_DATA) { ++ rc = iscsi_send_data(ctask, &tcp_ctask->sendbuf, &tcp_ctask->sg, ++ &tcp_ctask->sent, &ctask->imm_count, ++ &tcp_ctask->immbuf, &tcp_ctask->immdigest); ++ if (rc) ++ return rc; ++ tcp_ctask->xmstate &= ~XMSTATE_IMM_DATA; ++ } ++ ++ rc = iscsi_send_unsol_pdu(conn, ctask); ++ if (rc) ++ return rc; ++ ++ rc = iscsi_send_sol_pdu(conn, ctask); ++ if (rc) ++ return rc; ++ ++ return rc; ++} ++ ++static struct iscsi_cls_conn * ++iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx) ++{ ++ struct iscsi_conn *conn; ++ struct iscsi_cls_conn *cls_conn; ++ struct iscsi_tcp_conn *tcp_conn; ++ ++ cls_conn = iscsi_conn_setup(cls_session, conn_idx); ++ if (!cls_conn) ++ return NULL; ++ conn = cls_conn->dd_data; ++ /* ++ * due to strange issues with iser these are not set ++ * in iscsi_conn_setup ++ */ ++ conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; ++ ++ tcp_conn = kzalloc(sizeof(*tcp_conn), GFP_KERNEL); ++ if (!tcp_conn) ++ goto tcp_conn_alloc_fail; ++ ++ conn->dd_data = tcp_conn; ++ tcp_conn->iscsi_conn = conn; ++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; ++ /* initial operational parameters */ ++ tcp_conn->hdr_size = sizeof(struct iscsi_hdr); ++ ++ tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0, ++ CRYPTO_ALG_ASYNC); ++ tcp_conn->tx_hash.flags = 0; ++ if (IS_ERR(tcp_conn->tx_hash.tfm)) { ++ printk(KERN_ERR "Could not create connection due to crc32c " ++ "loading error %ld. Make sure the crc32c module is " ++ "built as a module or into the kernel\n", ++ PTR_ERR(tcp_conn->tx_hash.tfm)); ++ goto free_tcp_conn; ++ } ++ ++ tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0, ++ CRYPTO_ALG_ASYNC); ++ tcp_conn->rx_hash.flags = 0; ++ if (IS_ERR(tcp_conn->rx_hash.tfm)) { ++ printk(KERN_ERR "Could not create connection due to crc32c " ++ "loading error %ld. Make sure the crc32c module is " ++ "built as a module or into the kernel\n", ++ PTR_ERR(tcp_conn->rx_hash.tfm)); ++ goto free_tx_tfm; ++ } ++ ++ return cls_conn; ++ ++free_tx_tfm: ++ crypto_free_hash(tcp_conn->tx_hash.tfm); ++free_tcp_conn: ++ kfree(tcp_conn); ++tcp_conn_alloc_fail: ++ iscsi_conn_teardown(cls_conn); ++ return NULL; ++} ++ ++static void ++iscsi_tcp_release_conn(struct iscsi_conn *conn) ++{ ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ ++ if (!tcp_conn->sock) ++ return; ++ ++ sock_hold(tcp_conn->sock->sk); ++ iscsi_conn_restore_callbacks(tcp_conn); ++ sock_put(tcp_conn->sock->sk); ++ ++ sock_release(tcp_conn->sock); ++ tcp_conn->sock = NULL; ++ conn->recv_lock = NULL; ++} ++ ++static void ++iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ ++ iscsi_tcp_release_conn(conn); ++ iscsi_conn_teardown(cls_conn); ++ ++ if (tcp_conn->tx_hash.tfm) ++ crypto_free_hash(tcp_conn->tx_hash.tfm); ++ if (tcp_conn->rx_hash.tfm) ++ crypto_free_hash(tcp_conn->rx_hash.tfm); ++ ++ kfree(tcp_conn); ++} ++ ++static void ++iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ ++ iscsi_conn_stop(cls_conn, flag); ++ iscsi_tcp_release_conn(conn); ++ tcp_conn->hdr_size = sizeof(struct iscsi_hdr); ++} ++ ++static int ++iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session, ++ struct iscsi_cls_conn *cls_conn, uint64_t transport_eph, ++ int is_leading) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ struct sock *sk; ++ struct socket *sock; ++ int err; ++ ++ /* lookup for existing socket */ ++ sock = sockfd_lookup((int)transport_eph, &err); ++ if (!sock) { ++ printk(KERN_ERR "iscsi_tcp: sockfd_lookup failed %d\n", err); ++ return -EEXIST; ++ } ++ ++ err = iscsi_conn_bind(cls_session, cls_conn, is_leading); ++ if (err) ++ return err; ++ ++ /* bind iSCSI connection and socket */ ++ tcp_conn->sock = sock; ++ ++ /* setup Socket parameters */ ++ sk = sock->sk; ++ sk->sk_reuse = 1; ++ sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */ ++ sk->sk_allocation = GFP_ATOMIC; ++ ++ /* FIXME: disable Nagle's algorithm */ ++ ++ /* ++ * Intercept TCP callbacks for sendfile like receive ++ * processing. ++ */ ++ conn->recv_lock = &sk->sk_callback_lock; ++ iscsi_conn_set_callbacks(conn); ++ tcp_conn->sendpage = tcp_conn->sock->ops->sendpage; ++ /* ++ * set receive state machine into initial state ++ */ ++ tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER; ++ ++ return 0; ++} ++ ++/* called with host lock */ ++static void ++iscsi_tcp_mgmt_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask, ++ char *data, uint32_t data_size) ++{ ++ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; ++ ++ iscsi_buf_init_iov(&tcp_mtask->headbuf, (char*)mtask->hdr, ++ sizeof(struct iscsi_hdr)); ++ tcp_mtask->xmstate = XMSTATE_IMM_HDR; ++ tcp_mtask->sent = 0; ++ ++ if (mtask->data_count) ++ iscsi_buf_init_iov(&tcp_mtask->sendbuf, (char*)mtask->data, ++ mtask->data_count); ++} ++ ++static int ++iscsi_r2tpool_alloc(struct iscsi_session *session) ++{ ++ int i; ++ int cmd_i; ++ ++ /* ++ * initialize per-task: R2T pool and xmit queue ++ */ ++ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { ++ struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ ++ /* ++ * pre-allocated x4 as much r2ts to handle race when ++ * target acks DataOut faster than we data_xmit() queues ++ * could replenish r2tqueue. ++ */ ++ ++ /* R2T pool */ ++ if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, ++ (void***)&tcp_ctask->r2ts, ++ sizeof(struct iscsi_r2t_info))) { ++ goto r2t_alloc_fail; ++ } ++ ++ /* R2T xmit queue */ ++ tcp_ctask->r2tqueue = kfifo_alloc( ++ session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL); ++ if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) { ++ iscsi_pool_free(&tcp_ctask->r2tpool, ++ (void**)tcp_ctask->r2ts); ++ goto r2t_alloc_fail; ++ } ++ } ++ ++ return 0; ++ ++r2t_alloc_fail: ++ for (i = 0; i < cmd_i; i++) { ++ struct iscsi_cmd_task *ctask = session->cmds[i]; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ ++ kfifo_free(tcp_ctask->r2tqueue); ++ iscsi_pool_free(&tcp_ctask->r2tpool, ++ (void**)tcp_ctask->r2ts); ++ } ++ return -ENOMEM; ++} ++ ++static void ++iscsi_r2tpool_free(struct iscsi_session *session) ++{ ++ int i; ++ ++ for (i = 0; i < session->cmds_max; i++) { ++ struct iscsi_cmd_task *ctask = session->cmds[i]; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ ++ kfifo_free(tcp_ctask->r2tqueue); ++ iscsi_pool_free(&tcp_ctask->r2tpool, ++ (void**)tcp_ctask->r2ts); ++ } ++} ++ ++static int ++iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, ++ char *buf, int buflen) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_session *session = conn->session; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ int value; ++ ++ switch(param) { ++ case ISCSI_PARAM_HDRDGST_EN: ++ iscsi_set_param(cls_conn, param, buf, buflen); ++ tcp_conn->hdr_size = sizeof(struct iscsi_hdr); ++ if (conn->hdrdgst_en) ++ tcp_conn->hdr_size += sizeof(__u32); ++ break; ++ case ISCSI_PARAM_DATADGST_EN: ++ iscsi_set_param(cls_conn, param, buf, buflen); ++ tcp_conn->sendpage = conn->datadgst_en ? ++ sock_no_sendpage : tcp_conn->sock->ops->sendpage; ++ break; ++ case ISCSI_PARAM_MAX_R2T: ++ sscanf(buf, "%d", &value); ++ if (session->max_r2t == roundup_pow_of_two(value)) ++ break; ++ iscsi_r2tpool_free(session); ++ iscsi_set_param(cls_conn, param, buf, buflen); ++ if (session->max_r2t & (session->max_r2t - 1)) ++ session->max_r2t = roundup_pow_of_two(session->max_r2t); ++ if (iscsi_r2tpool_alloc(session)) ++ return -ENOMEM; ++ break; ++ default: ++ return iscsi_set_param(cls_conn, param, buf, buflen); ++ } ++ ++ return 0; ++} ++ ++static int ++iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn, ++ enum iscsi_param param, char *buf) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ struct inet_sock *inet; ++ struct ipv6_pinfo *np; ++ struct sock *sk; ++ int len; ++ ++ switch(param) { ++ case ISCSI_PARAM_CONN_PORT: ++ mutex_lock(&conn->xmitmutex); ++ if (!tcp_conn->sock) { ++ mutex_unlock(&conn->xmitmutex); ++ return -EINVAL; ++ } ++ ++ inet = inet_sk(tcp_conn->sock->sk); ++ len = sprintf(buf, "%hu\n", be16_to_cpu(inet->dport)); ++ mutex_unlock(&conn->xmitmutex); ++ break; ++ case ISCSI_PARAM_CONN_ADDRESS: ++ mutex_lock(&conn->xmitmutex); ++ if (!tcp_conn->sock) { ++ mutex_unlock(&conn->xmitmutex); ++ return -EINVAL; ++ } ++ ++ sk = tcp_conn->sock->sk; ++ if (sk->sk_family == PF_INET) { ++ inet = inet_sk(sk); ++ len = sprintf(buf, NIPQUAD_FMT "\n", ++ NIPQUAD(inet->daddr)); ++ } else { ++ np = inet6_sk(sk); ++ len = sprintf(buf, NIP6_FMT "\n", NIP6(np->daddr)); ++ } ++ mutex_unlock(&conn->xmitmutex); ++ break; ++ default: ++ return iscsi_conn_get_param(cls_conn, param, buf); ++ } ++ ++ return len; ++} ++ ++static void ++iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_tcp_conn *tcp_conn = conn->dd_data; ++ ++ stats->txdata_octets = conn->txdata_octets; ++ stats->rxdata_octets = conn->rxdata_octets; ++ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt; ++ stats->dataout_pdus = conn->dataout_pdus_cnt; ++ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt; ++ stats->datain_pdus = conn->datain_pdus_cnt; ++ stats->r2t_pdus = conn->r2t_pdus_cnt; ++ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt; ++ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt; ++ stats->custom_length = 3; ++ strcpy(stats->custom[0].desc, "tx_sendpage_failures"); ++ stats->custom[0].value = tcp_conn->sendpage_failures_cnt; ++ strcpy(stats->custom[1].desc, "rx_discontiguous_hdr"); ++ stats->custom[1].value = tcp_conn->discontiguous_hdr_cnt; ++ strcpy(stats->custom[2].desc, "eh_abort_cnt"); ++ stats->custom[2].value = conn->eh_abort_cnt; ++} ++ ++static struct iscsi_cls_session * ++iscsi_tcp_session_create(struct iscsi_transport *iscsit, ++ struct scsi_transport_template *scsit, ++ uint32_t initial_cmdsn, uint32_t *hostno) ++{ ++ struct iscsi_cls_session *cls_session; ++ struct iscsi_session *session; ++ uint32_t hn; ++ int cmd_i; ++ ++ cls_session = iscsi_session_setup(iscsit, scsit, ++ sizeof(struct iscsi_tcp_cmd_task), ++ sizeof(struct iscsi_tcp_mgmt_task), ++ initial_cmdsn, &hn); ++ if (!cls_session) ++ return NULL; ++ *hostno = hn; ++ ++ session = class_to_transport_session(cls_session); ++ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { ++ struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; ++ struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data; ++ ++ ctask->hdr = &tcp_ctask->hdr; ++ } ++ ++ for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { ++ struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i]; ++ struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data; ++ ++ mtask->hdr = &tcp_mtask->hdr; ++ } ++ ++ if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session))) ++ goto r2tpool_alloc_fail; ++ ++ return cls_session; ++ ++r2tpool_alloc_fail: ++ iscsi_session_teardown(cls_session); ++ return NULL; ++} ++ ++static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session) ++{ ++ iscsi_r2tpool_free(class_to_transport_session(cls_session)); ++ iscsi_session_teardown(cls_session); ++} ++ ++static struct scsi_host_template iscsi_sht = { ++ .name = "iSCSI Initiator over TCP/IP", ++ .queuecommand = iscsi_queuecommand, ++ .change_queue_depth = iscsi_change_queue_depth, ++ .can_queue = ISCSI_XMIT_CMDS_MAX - 1, ++ .sg_tablesize = ISCSI_SG_TABLESIZE, ++ .max_sectors = 0xFFFF, ++ .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, ++ .eh_abort_handler = iscsi_eh_abort, ++ .eh_host_reset_handler = iscsi_eh_host_reset, ++ .use_clustering = DISABLE_CLUSTERING, ++ .proc_name = "iscsi_tcp", ++ .this_id = -1, ++}; ++ ++static struct iscsi_transport iscsi_tcp_transport = { ++ .owner = THIS_MODULE, ++ .name = "tcp", ++ .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T | CAP_HDRDGST ++ | CAP_DATADGST, ++ .param_mask = ISCSI_MAX_RECV_DLENGTH | ++ ISCSI_MAX_XMIT_DLENGTH | ++ ISCSI_HDRDGST_EN | ++ ISCSI_DATADGST_EN | ++ ISCSI_INITIAL_R2T_EN | ++ ISCSI_MAX_R2T | ++ ISCSI_IMM_DATA_EN | ++ ISCSI_FIRST_BURST | ++ ISCSI_MAX_BURST | ++ ISCSI_PDU_INORDER_EN | ++ ISCSI_DATASEQ_INORDER_EN | ++ ISCSI_ERL | ++ ISCSI_CONN_PORT | ++ ISCSI_CONN_ADDRESS | ++ ISCSI_EXP_STATSN | ++ ISCSI_PERSISTENT_PORT | ++ ISCSI_PERSISTENT_ADDRESS | ++ ISCSI_TARGET_NAME | ++ ISCSI_TPGT, ++ .host_template = &iscsi_sht, ++ .conndata_size = sizeof(struct iscsi_conn), ++ .max_conn = 1, ++ .max_cmd_len = ISCSI_TCP_MAX_CMD_LEN, ++ /* session management */ ++ .create_session = iscsi_tcp_session_create, ++ .destroy_session = iscsi_tcp_session_destroy, ++ /* connection management */ ++ .create_conn = iscsi_tcp_conn_create, ++ .bind_conn = iscsi_tcp_conn_bind, ++ .destroy_conn = iscsi_tcp_conn_destroy, ++ .set_param = iscsi_conn_set_param, ++ .get_conn_param = iscsi_tcp_conn_get_param, ++ .get_session_param = iscsi_session_get_param, ++ .start_conn = iscsi_conn_start, ++ .stop_conn = iscsi_tcp_conn_stop, ++ /* IO */ ++ .send_pdu = iscsi_conn_send_pdu, ++ .get_stats = iscsi_conn_get_stats, ++ .init_cmd_task = iscsi_tcp_cmd_init, ++ .init_mgmt_task = iscsi_tcp_mgmt_init, ++ .xmit_cmd_task = iscsi_tcp_ctask_xmit, ++ .xmit_mgmt_task = iscsi_tcp_mtask_xmit, ++ .cleanup_cmd_task = iscsi_tcp_cleanup_ctask, ++ /* recovery */ ++ .session_recovery_timedout = iscsi_session_recovery_timedout, ++}; ++ ++static int __init ++iscsi_tcp_init(void) ++{ ++ if (iscsi_max_lun < 1) { ++ printk(KERN_ERR "iscsi_tcp: Invalid max_lun value of %u\n", ++ iscsi_max_lun); ++ return -EINVAL; ++ } ++ iscsi_tcp_transport.max_lun = iscsi_max_lun; ++ ++ if (!iscsi_register_transport(&iscsi_tcp_transport)) ++ return -ENODEV; ++ ++ return 0; ++} ++ ++static void __exit ++iscsi_tcp_exit(void) ++{ ++ iscsi_unregister_transport(&iscsi_tcp_transport); ++} ++ ++module_init(iscsi_tcp_init); ++module_exit(iscsi_tcp_exit); +Index: kernel/libiscsi.c +=================================================================== +--- kernel/libiscsi.c (revision 0) ++++ kernel/libiscsi.c (revision 779) +@@ -0,0 +1,1995 @@ ++/* ++ * iSCSI lib functions ++ * ++ * Copyright (C) 2006 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004 - 2006 Mike Christie ++ * Copyright (C) 2004 - 2005 Dmitry Yusupov ++ * Copyright (C) 2004 - 2005 Alex Aizman ++ * maintained by open-iscsi@googlegroups.com ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "iscsi_proto.h" ++#include "scsi_transport_iscsi.h" ++#include "libiscsi.h" ++ ++struct iscsi_session * ++class_to_transport_session(struct iscsi_cls_session *cls_session) ++{ ++ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); ++ return iscsi_hostdata(shost->hostdata); ++} ++EXPORT_SYMBOL_GPL(class_to_transport_session); ++ ++#define INVALID_SN_DELTA 0xffff ++ ++int ++iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) ++{ ++ uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn); ++ uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn); ++ ++ if (max_cmdsn < exp_cmdsn -1 && ++ max_cmdsn > exp_cmdsn - INVALID_SN_DELTA) ++ return ISCSI_ERR_MAX_CMDSN; ++ if (max_cmdsn > session->max_cmdsn || ++ max_cmdsn < session->max_cmdsn - INVALID_SN_DELTA) ++ session->max_cmdsn = max_cmdsn; ++ if (exp_cmdsn > session->exp_cmdsn || ++ exp_cmdsn < session->exp_cmdsn - INVALID_SN_DELTA) ++ session->exp_cmdsn = exp_cmdsn; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn); ++ ++void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask, ++ struct iscsi_data *hdr) ++{ ++ struct iscsi_conn *conn = ctask->conn; ++ ++ memset(hdr, 0, sizeof(struct iscsi_data)); ++ hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); ++ hdr->datasn = cpu_to_be32(ctask->unsol_datasn); ++ ctask->unsol_datasn++; ++ hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; ++ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); ++ ++ hdr->itt = ctask->hdr->itt; ++ hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); ++ hdr->offset = cpu_to_be32(ctask->unsol_offset); ++ ++ if (ctask->unsol_count > conn->max_xmit_dlength) { ++ hton24(hdr->dlength, conn->max_xmit_dlength); ++ ctask->data_count = conn->max_xmit_dlength; ++ ctask->unsol_offset += ctask->data_count; ++ hdr->flags = 0; ++ } else { ++ hton24(hdr->dlength, ctask->unsol_count); ++ ctask->data_count = ctask->unsol_count; ++ hdr->flags = ISCSI_FLAG_CMD_FINAL; ++ } ++} ++EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu); ++ ++/** ++ * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu ++ * @ctask: iscsi cmd task ++ * ++ * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set ++ * fields like dlength or final based on how much data it sends ++ */ ++static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_conn *conn = ctask->conn; ++ struct iscsi_session *session = conn->session; ++ struct iscsi_cmd *hdr = ctask->hdr; ++ struct scsi_cmnd *sc = ctask->sc; ++ ++ hdr->opcode = ISCSI_OP_SCSI_CMD; ++ hdr->flags = ISCSI_ATTR_SIMPLE; ++ int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); ++ hdr->itt = build_itt(ctask->itt, conn->id, session->age); ++ hdr->data_length = cpu_to_be32(sc->request_bufflen); ++ hdr->cmdsn = cpu_to_be32(session->cmdsn); ++ session->cmdsn++; ++ hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); ++ memcpy(hdr->cdb, sc->cmnd, sc->cmd_len); ++ memset(&hdr->cdb[sc->cmd_len], 0, MAX_COMMAND_SIZE - sc->cmd_len); ++ ++ ctask->data_count = 0; ++ if (sc->sc_data_direction == DMA_TO_DEVICE) { ++ hdr->flags |= ISCSI_FLAG_CMD_WRITE; ++ /* ++ * Write counters: ++ * ++ * imm_count bytes to be sent right after ++ * SCSI PDU Header ++ * ++ * unsol_count bytes(as Data-Out) to be sent ++ * without R2T ack right after ++ * immediate data ++ * ++ * r2t_data_count bytes to be sent via R2T ack's ++ * ++ * pad_count bytes to be sent as zero-padding ++ */ ++ ctask->imm_count = 0; ++ ctask->unsol_count = 0; ++ ctask->unsol_offset = 0; ++ ctask->unsol_datasn = 0; ++ ++ if (session->imm_data_en) { ++ if (ctask->total_length >= session->first_burst) ++ ctask->imm_count = min(session->first_burst, ++ conn->max_xmit_dlength); ++ else ++ ctask->imm_count = min(ctask->total_length, ++ conn->max_xmit_dlength); ++ hton24(ctask->hdr->dlength, ctask->imm_count); ++ } else ++ zero_data(ctask->hdr->dlength); ++ ++ if (!session->initial_r2t_en) { ++ ctask->unsol_count = min(session->first_burst, ++ ctask->total_length) - ctask->imm_count; ++ ctask->unsol_offset = ctask->imm_count; ++ } ++ ++ if (!ctask->unsol_count) ++ /* No unsolicit Data-Out's */ ++ ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL; ++ } else { ++ ctask->datasn = 0; ++ hdr->flags |= ISCSI_FLAG_CMD_FINAL; ++ zero_data(hdr->dlength); ++ ++ if (sc->sc_data_direction == DMA_FROM_DEVICE) ++ hdr->flags |= ISCSI_FLAG_CMD_READ; ++ } ++ ++ conn->scsicmd_pdus_cnt++; ++} ++EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu); ++ ++/** ++ * iscsi_complete_command - return command back to scsi-ml ++ * @ctask: iscsi cmd task ++ * ++ * Must be called with session lock. ++ * This function returns the scsi command to scsi-ml and returns ++ * the cmd task to the pool of available cmd tasks. ++ */ ++static void iscsi_complete_command(struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_session *session = ctask->conn->session; ++ struct scsi_cmnd *sc = ctask->sc; ++ ++ ctask->state = ISCSI_TASK_COMPLETED; ++ ctask->sc = NULL; ++ /* SCSI eh reuses commands to verify us */ ++ sc->SCp.ptr = NULL; ++ list_del_init(&ctask->running); ++ __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*)); ++ sc->scsi_done(sc); ++} ++ ++static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask) ++{ ++ atomic_inc(&ctask->refcount); ++} ++ ++static void iscsi_get_ctask(struct iscsi_cmd_task *ctask) ++{ ++ spin_lock_bh(&ctask->conn->session->lock); ++ __iscsi_get_ctask(ctask); ++ spin_unlock_bh(&ctask->conn->session->lock); ++} ++ ++static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask) ++{ ++ if (atomic_dec_and_test(&ctask->refcount)) ++ iscsi_complete_command(ctask); ++} ++ ++static void iscsi_put_ctask(struct iscsi_cmd_task *ctask) ++{ ++ spin_lock_bh(&ctask->conn->session->lock); ++ __iscsi_put_ctask(ctask); ++ spin_unlock_bh(&ctask->conn->session->lock); ++} ++ ++/** ++ * iscsi_cmd_rsp - SCSI Command Response processing ++ * @conn: iscsi connection ++ * @hdr: iscsi header ++ * @ctask: scsi command task ++ * @data: cmd data buffer ++ * @datalen: len of buffer ++ * ++ * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and ++ * then completes the command and task. ++ **/ ++static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ++ struct iscsi_cmd_task *ctask, char *data, ++ int datalen) ++{ ++ int rc; ++ struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr; ++ struct iscsi_session *session = conn->session; ++ struct scsi_cmnd *sc = ctask->sc; ++ ++ rc = iscsi_check_assign_cmdsn(session, (struct iscsi_nopin*)rhdr); ++ if (rc) { ++ sc->result = DID_ERROR << 16; ++ goto out; ++ } ++ ++ conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; ++ ++ sc->result = (DID_OK << 16) | rhdr->cmd_status; ++ ++ if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) { ++ sc->result = DID_ERROR << 16; ++ goto out; ++ } ++ ++ if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) { ++ uint16_t senselen; ++ ++ if (datalen < 2) { ++invalid_datalen: ++ printk(KERN_ERR "iscsi: Got CHECK_CONDITION but " ++ "invalid data buffer size of %d\n", datalen); ++ sc->result = DID_BAD_TARGET << 16; ++ goto out; ++ } ++ ++ senselen = be16_to_cpu(get_unaligned((__be16 *) data)); ++ if (datalen < senselen) ++ goto invalid_datalen; ++ ++ memcpy(sc->sense_buffer, data + 2, ++ min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); ++ debug_scsi("copied %d bytes of sense\n", ++ min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); ++ } ++ ++ if (sc->sc_data_direction == DMA_TO_DEVICE) ++ goto out; ++ ++ if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) { ++ int res_count = be32_to_cpu(rhdr->residual_count); ++ ++ if (res_count > 0 && res_count <= sc->request_bufflen) ++ sc->resid = res_count; ++ else ++ sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; ++ } else if (rhdr->flags & ISCSI_FLAG_CMD_BIDI_UNDERFLOW) ++ sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; ++ else if (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW) ++ sc->resid = be32_to_cpu(rhdr->residual_count); ++ ++out: ++ debug_scsi("done [sc %lx res %d itt 0x%x]\n", ++ (long)sc, sc->result, ctask->itt); ++ conn->scsirsp_pdus_cnt++; ++ ++ __iscsi_put_ctask(ctask); ++ return rc; ++} ++ ++static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) ++{ ++ struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; ++ ++ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; ++ conn->tmfrsp_pdus_cnt++; ++ ++ if (conn->tmabort_state != TMABORT_INITIAL) ++ return; ++ ++ if (tmf->response == ISCSI_TMF_RSP_COMPLETE) ++ conn->tmabort_state = TMABORT_SUCCESS; ++ else if (tmf->response == ISCSI_TMF_RSP_NO_TASK) ++ conn->tmabort_state = TMABORT_NOT_FOUND; ++ else ++ conn->tmabort_state = TMABORT_FAILED; ++ wake_up(&conn->ehwait); ++} ++ ++static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ++ char *data, int datalen) ++{ ++ struct iscsi_reject *reject = (struct iscsi_reject *)hdr; ++ struct iscsi_hdr rejected_pdu; ++ uint32_t itt; ++ ++ conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; ++ ++ if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) { ++ if (ntoh24(reject->dlength) > datalen) ++ return ISCSI_ERR_PROTO; ++ ++ if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { ++ memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); ++ itt = get_itt(rejected_pdu.itt); ++ printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected " ++ "due to DataDigest error.\n", itt, ++ rejected_pdu.opcode); ++ } ++ } ++ return 0; ++} ++ ++/** ++ * __iscsi_complete_pdu - complete pdu ++ * @conn: iscsi conn ++ * @hdr: iscsi header ++ * @data: data buffer ++ * @datalen: len of data buffer ++ * ++ * Completes pdu processing by freeing any resources allocated at ++ * queuecommand or send generic. session lock must be held and verify ++ * itt must have been called. ++ */ ++int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ++ char *data, int datalen) ++{ ++ struct iscsi_session *session = conn->session; ++ int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; ++ struct iscsi_cmd_task *ctask; ++ struct iscsi_mgmt_task *mtask; ++ uint32_t itt; ++ ++ if (hdr->itt != RESERVED_ITT) ++ itt = get_itt(hdr->itt); ++ else ++ itt = ~0U; ++ ++ if (itt < session->cmds_max) { ++ ctask = session->cmds[itt]; ++ ++ debug_scsi("cmdrsp [op 0x%x cid %d itt 0x%x len %d]\n", ++ opcode, conn->id, ctask->itt, datalen); ++ ++ switch(opcode) { ++ case ISCSI_OP_SCSI_CMD_RSP: ++ BUG_ON((void*)ctask != ctask->sc->SCp.ptr); ++ rc = iscsi_scsi_cmd_rsp(conn, hdr, ctask, data, ++ datalen); ++ break; ++ case ISCSI_OP_SCSI_DATA_IN: ++ BUG_ON((void*)ctask != ctask->sc->SCp.ptr); ++ if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { ++ conn->scsirsp_pdus_cnt++; ++ __iscsi_put_ctask(ctask); ++ } ++ break; ++ case ISCSI_OP_R2T: ++ /* LLD handles this for now */ ++ break; ++ default: ++ rc = ISCSI_ERR_BAD_OPCODE; ++ break; ++ } ++ } else if (itt >= ISCSI_MGMT_ITT_OFFSET && ++ itt < ISCSI_MGMT_ITT_OFFSET + session->mgmtpool_max) { ++ mtask = session->mgmt_cmds[itt - ISCSI_MGMT_ITT_OFFSET]; ++ ++ debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n", ++ opcode, conn->id, mtask->itt, datalen); ++ ++ rc = iscsi_check_assign_cmdsn(session, ++ (struct iscsi_nopin*)hdr); ++ if (rc) ++ goto done; ++ ++ switch(opcode) { ++ case ISCSI_OP_LOGOUT_RSP: ++ if (datalen) { ++ rc = ISCSI_ERR_PROTO; ++ break; ++ } ++ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; ++ /* fall through */ ++ case ISCSI_OP_LOGIN_RSP: ++ case ISCSI_OP_TEXT_RSP: ++ /* ++ * login related PDU's exp_statsn is handled in ++ * userspace ++ */ ++ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) ++ rc = ISCSI_ERR_CONN_FAILED; ++ list_del(&mtask->running); ++ if (conn->login_mtask != mtask) ++ __kfifo_put(session->mgmtpool.queue, ++ (void*)&mtask, sizeof(void*)); ++ break; ++ case ISCSI_OP_SCSI_TMFUNC_RSP: ++ if (datalen) { ++ rc = ISCSI_ERR_PROTO; ++ break; ++ } ++ ++ iscsi_tmf_rsp(conn, hdr); ++ break; ++ case ISCSI_OP_NOOP_IN: ++ if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) { ++ rc = ISCSI_ERR_PROTO; ++ break; ++ } ++ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; ++ ++ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) ++ rc = ISCSI_ERR_CONN_FAILED; ++ list_del(&mtask->running); ++ if (conn->login_mtask != mtask) ++ __kfifo_put(session->mgmtpool.queue, ++ (void*)&mtask, sizeof(void*)); ++ break; ++ default: ++ rc = ISCSI_ERR_BAD_OPCODE; ++ break; ++ } ++ } else if (itt == ~0U) { ++ rc = iscsi_check_assign_cmdsn(session, ++ (struct iscsi_nopin*)hdr); ++ if (rc) ++ goto done; ++ ++ switch(opcode) { ++ case ISCSI_OP_NOOP_IN: ++ if (datalen) { ++ rc = ISCSI_ERR_PROTO; ++ break; ++ } ++ ++ if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) ++ break; ++ ++ if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0)) ++ rc = ISCSI_ERR_CONN_FAILED; ++ break; ++ case ISCSI_OP_REJECT: ++ rc = iscsi_handle_reject(conn, hdr, data, datalen); ++ break; ++ case ISCSI_OP_ASYNC_EVENT: ++ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; ++ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) ++ rc = ISCSI_ERR_CONN_FAILED; ++ break; ++ default: ++ rc = ISCSI_ERR_BAD_OPCODE; ++ break; ++ } ++ } else ++ rc = ISCSI_ERR_BAD_ITT; ++ ++done: ++ return rc; ++} ++EXPORT_SYMBOL_GPL(__iscsi_complete_pdu); ++ ++int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ++ char *data, int datalen) ++{ ++ int rc; ++ ++ spin_lock(&conn->session->lock); ++ rc = __iscsi_complete_pdu(conn, hdr, data, datalen); ++ spin_unlock(&conn->session->lock); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(iscsi_complete_pdu); ++ ++/* verify itt (itt encoding: age+cid+itt) */ ++int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ++ uint32_t *ret_itt) ++{ ++ struct iscsi_session *session = conn->session; ++ struct iscsi_cmd_task *ctask; ++ uint32_t itt; ++ ++ if (hdr->itt != RESERVED_ITT) { ++ if (((__force u32)hdr->itt & ISCSI_AGE_MASK) != ++ (session->age << ISCSI_AGE_SHIFT)) { ++ printk(KERN_ERR "iscsi: received itt %x expected " ++ "session age (%x)\n", (__force u32)hdr->itt, ++ session->age & ISCSI_AGE_MASK); ++ return ISCSI_ERR_BAD_ITT; ++ } ++ ++ if (((__force u32)hdr->itt & ISCSI_CID_MASK) != ++ (conn->id << ISCSI_CID_SHIFT)) { ++ printk(KERN_ERR "iscsi: received itt %x, expected " ++ "CID (%x)\n", (__force u32)hdr->itt, conn->id); ++ return ISCSI_ERR_BAD_ITT; ++ } ++ itt = get_itt(hdr->itt); ++ } else ++ itt = ~0U; ++ ++ if (itt < session->cmds_max) { ++ ctask = session->cmds[itt]; ++ ++ if (!ctask->sc) { ++ printk(KERN_INFO "iscsi: dropping ctask with " ++ "itt 0x%x\n", ctask->itt); ++ /* force drop */ ++ return ISCSI_ERR_NO_SCSI_CMD; ++ } ++ ++ if (ctask->sc->SCp.phase != session->age) { ++ printk(KERN_ERR "iscsi: ctask's session age %d, " ++ "expected %d\n", ctask->sc->SCp.phase, ++ session->age); ++ return ISCSI_ERR_SESSION_FAILED; ++ } ++ } ++ ++ *ret_itt = itt; ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_verify_itt); ++ ++void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) ++{ ++ struct iscsi_session *session = conn->session; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&session->lock, flags); ++ if (session->state == ISCSI_STATE_FAILED) { ++ spin_unlock_irqrestore(&session->lock, flags); ++ return; ++ } ++ ++ if (conn->stop_stage == 0) ++ session->state = ISCSI_STATE_FAILED; ++ spin_unlock_irqrestore(&session->lock, flags); ++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); ++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); ++ iscsi_conn_error(conn->cls_conn, err); ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_failure); ++ ++static int iscsi_xmit_mtask(struct iscsi_conn *conn) ++{ ++ struct iscsi_hdr *hdr = conn->mtask->hdr; ++ int rc, was_logout = 0; ++ ++ if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) { ++ conn->session->state = ISCSI_STATE_IN_RECOVERY; ++ iscsi_block_session(session_to_cls(conn->session)); ++ was_logout = 1; ++ } ++ rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask); ++ if (rc) ++ return rc; ++ ++ /* done with this in-progress mtask */ ++ conn->mtask = NULL; ++ ++ if (was_logout) { ++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); ++ return -ENODATA; ++ } ++ return 0; ++} ++ ++/** ++ * iscsi_data_xmit - xmit any command into the scheduled connection ++ * @conn: iscsi connection ++ * ++ * Notes: ++ * The function can return -EAGAIN in which case the caller must ++ * re-schedule it again later or recover. '0' return code means ++ * successful xmit. ++ **/ ++static int iscsi_data_xmit(struct iscsi_conn *conn) ++{ ++ struct iscsi_transport *tt; ++ int rc = 0; ++ ++ if (unlikely(conn->suspend_tx)) { ++ debug_scsi("conn %d Tx suspended!\n", conn->id); ++ return -ENODATA; ++ } ++ tt = conn->session->tt; ++ ++ /* ++ * Transmit in the following order: ++ * ++ * 1) un-finished xmit (ctask or mtask) ++ * 2) immediate control PDUs ++ * 3) write data ++ * 4) SCSI commands ++ * 5) non-immediate control PDUs ++ * ++ * No need to lock around __kfifo_get as long as ++ * there's one producer and one consumer. ++ */ ++ ++ BUG_ON(conn->ctask && conn->mtask); ++ ++ if (conn->ctask) { ++ iscsi_get_ctask(conn->ctask); ++ rc = tt->xmit_cmd_task(conn, conn->ctask); ++ iscsi_put_ctask(conn->ctask); ++ if (rc) ++ goto again; ++ /* done with this in-progress ctask */ ++ conn->ctask = NULL; ++ } ++ if (conn->mtask) { ++ rc = iscsi_xmit_mtask(conn); ++ if (rc) ++ goto again; ++ } ++ ++ /* process immediate first */ ++ if (unlikely(__kfifo_len(conn->immqueue))) { ++ while (__kfifo_get(conn->immqueue, (void*)&conn->mtask, ++ sizeof(void*))) { ++ spin_lock_bh(&conn->session->lock); ++ list_add_tail(&conn->mtask->running, ++ &conn->mgmt_run_list); ++ spin_unlock_bh(&conn->session->lock); ++ rc = iscsi_xmit_mtask(conn); ++ if (rc) ++ goto again; ++ } ++ } ++ ++ /* process command queue */ ++ spin_lock_bh(&conn->session->lock); ++ while (!list_empty(&conn->xmitqueue)) { ++ /* ++ * iscsi tcp may readd the task to the xmitqueue to send ++ * write data ++ */ ++ conn->ctask = list_entry(conn->xmitqueue.next, ++ struct iscsi_cmd_task, running); ++ conn->ctask->state = ISCSI_TASK_RUNNING; ++ list_move_tail(conn->xmitqueue.next, &conn->run_list); ++ __iscsi_get_ctask(conn->ctask); ++ spin_unlock_bh(&conn->session->lock); ++ ++ rc = tt->xmit_cmd_task(conn, conn->ctask); ++ ++ spin_lock_bh(&conn->session->lock); ++ __iscsi_put_ctask(conn->ctask); ++ if (rc) { ++ spin_unlock_bh(&conn->session->lock); ++ goto again; ++ } ++ } ++ spin_unlock_bh(&conn->session->lock); ++ /* done with this ctask */ ++ conn->ctask = NULL; ++ ++ /* process the rest control plane PDUs, if any */ ++ if (unlikely(__kfifo_len(conn->mgmtqueue))) { ++ while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask, ++ sizeof(void*))) { ++ spin_lock_bh(&conn->session->lock); ++ list_add_tail(&conn->mtask->running, ++ &conn->mgmt_run_list); ++ spin_unlock_bh(&conn->session->lock); ++ rc = iscsi_xmit_mtask(conn); ++ if (rc) ++ goto again; ++ } ++ } ++ ++ return -ENODATA; ++ ++again: ++ if (unlikely(conn->suspend_tx)) ++ return -ENODATA; ++ ++ return rc; ++} ++ ++static void iscsi_xmitworker(struct work_struct *work) ++{ ++ struct iscsi_conn *conn = ++ container_of(work, struct iscsi_conn, xmitwork); ++ int rc; ++ /* ++ * serialize Xmit worker on a per-connection basis. ++ */ ++ mutex_lock(&conn->xmitmutex); ++ do { ++ rc = iscsi_data_xmit(conn); ++ } while (rc >= 0 || rc == -EAGAIN); ++ mutex_unlock(&conn->xmitmutex); ++} ++ ++enum { ++ FAILURE_BAD_HOST = 1, ++ FAILURE_SESSION_FAILED, ++ FAILURE_SESSION_FREED, ++ FAILURE_WINDOW_CLOSED, ++ FAILURE_OOM, ++ FAILURE_SESSION_TERMINATE, ++ FAILURE_SESSION_IN_RECOVERY, ++ FAILURE_SESSION_RECOVERY_TIMEOUT, ++}; ++ ++int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) ++{ ++ struct Scsi_Host *host; ++ int reason = 0; ++ struct iscsi_session *session; ++ struct iscsi_conn *conn; ++ struct iscsi_cmd_task *ctask = NULL; ++ ++ sc->scsi_done = done; ++ sc->result = 0; ++ sc->SCp.ptr = NULL; ++ ++ host = sc->device->host; ++ session = iscsi_hostdata(host->hostdata); ++ ++ spin_lock(&session->lock); ++ ++ /* ++ * ISCSI_STATE_FAILED is a temp. state. The recovery ++ * code will decide what is best to do with command queued ++ * during this time ++ */ ++ if (session->state != ISCSI_STATE_LOGGED_IN && ++ session->state != ISCSI_STATE_FAILED) { ++ /* ++ * to handle the race between when we set the recovery state ++ * and block the session we requeue here (commands could ++ * be entering our queuecommand while a block is starting ++ * up because the block code is not locked) ++ */ ++ if (session->state == ISCSI_STATE_IN_RECOVERY) { ++ reason = FAILURE_SESSION_IN_RECOVERY; ++ goto reject; ++ } ++ ++ if (session->state == ISCSI_STATE_RECOVERY_FAILED) ++ reason = FAILURE_SESSION_RECOVERY_TIMEOUT; ++ else if (session->state == ISCSI_STATE_TERMINATE) ++ reason = FAILURE_SESSION_TERMINATE; ++ else ++ reason = FAILURE_SESSION_FREED; ++ goto fault; ++ } ++ ++ /* ++ * Check for iSCSI window and take care of CmdSN wrap-around ++ */ ++ if ((int)(session->max_cmdsn - session->cmdsn) < 0) { ++ reason = FAILURE_WINDOW_CLOSED; ++ goto reject; ++ } ++ ++ conn = session->leadconn; ++ if (!conn) { ++ reason = FAILURE_SESSION_FREED; ++ goto fault; ++ } ++ ++ if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask, ++ sizeof(void*))) { ++ reason = FAILURE_OOM; ++ goto reject; ++ } ++ sc->SCp.phase = session->age; ++ sc->SCp.ptr = (char *)ctask; ++ ++ atomic_set(&ctask->refcount, 1); ++ ctask->state = ISCSI_TASK_PENDING; ++ ctask->mtask = NULL; ++ ctask->conn = conn; ++ ctask->sc = sc; ++ INIT_LIST_HEAD(&ctask->running); ++ ctask->total_length = sc->request_bufflen; ++ iscsi_prep_scsi_cmd_pdu(ctask); ++ ++ session->tt->init_cmd_task(ctask); ++ ++ list_add_tail(&ctask->running, &conn->xmitqueue); ++ debug_scsi( ++ "ctask enq [%s cid %d sc %p cdb 0x%x itt 0x%x len %d cmdsn %d " ++ "win %d]\n", ++ sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", ++ conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen, ++ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); ++ spin_unlock(&session->lock); ++ ++ scsi_queue_work(host, &conn->xmitwork); ++ return 0; ++ ++reject: ++ spin_unlock(&session->lock); ++ debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); ++ return SCSI_MLQUEUE_HOST_BUSY; ++ ++fault: ++ spin_unlock(&session->lock); ++ printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n", ++ sc->cmnd[0], reason); ++ sc->result = (DID_NO_CONNECT << 16); ++ sc->resid = sc->request_bufflen; ++ sc->scsi_done(sc); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_queuecommand); ++ ++int iscsi_change_queue_depth(struct scsi_device *sdev, int depth) ++{ ++ if (depth > ISCSI_MAX_CMD_PER_LUN) ++ depth = ISCSI_MAX_CMD_PER_LUN; ++ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); ++ return sdev->queue_depth; ++} ++EXPORT_SYMBOL_GPL(iscsi_change_queue_depth); ++ ++static int ++iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr, ++ char *data, uint32_t data_size) ++{ ++ struct iscsi_session *session = conn->session; ++ struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; ++ struct iscsi_mgmt_task *mtask; ++ ++ spin_lock_bh(&session->lock); ++ if (session->state == ISCSI_STATE_TERMINATE) { ++ spin_unlock_bh(&session->lock); ++ return -EPERM; ++ } ++ if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || ++ hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) ++ /* ++ * Login and Text are sent serially, in ++ * request-followed-by-response sequence. ++ * Same mtask can be used. Same ITT must be used. ++ * Note that login_mtask is preallocated at conn_create(). ++ */ ++ mtask = conn->login_mtask; ++ else { ++ BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); ++ BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); ++ ++ nop->exp_statsn = cpu_to_be32(conn->exp_statsn); ++ if (!__kfifo_get(session->mgmtpool.queue, ++ (void*)&mtask, sizeof(void*))) { ++ spin_unlock_bh(&session->lock); ++ return -ENOSPC; ++ } ++ } ++ ++ /* ++ * pre-format CmdSN for outgoing PDU. ++ */ ++ if (hdr->itt != RESERVED_ITT) { ++ hdr->itt = build_itt(mtask->itt, conn->id, session->age); ++ nop->cmdsn = cpu_to_be32(session->cmdsn); ++ if (conn->c_stage == ISCSI_CONN_STARTED && ++ !(hdr->opcode & ISCSI_OP_IMMEDIATE)) ++ session->cmdsn++; ++ } else ++ /* do not advance CmdSN */ ++ nop->cmdsn = cpu_to_be32(session->cmdsn); ++ ++ if (data_size) { ++ memcpy(mtask->data, data, data_size); ++ mtask->data_count = data_size; ++ } else ++ mtask->data_count = 0; ++ ++ INIT_LIST_HEAD(&mtask->running); ++ memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr)); ++ if (session->tt->init_mgmt_task) ++ session->tt->init_mgmt_task(conn, mtask, data, data_size); ++ spin_unlock_bh(&session->lock); ++ ++ debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n", ++ hdr->opcode, hdr->itt, data_size); ++ ++ /* ++ * since send_pdu() could be called at least from two contexts, ++ * we need to serialize __kfifo_put, so we don't have to take ++ * additional lock on fast data-path ++ */ ++ if (hdr->opcode & ISCSI_OP_IMMEDIATE) ++ __kfifo_put(conn->immqueue, (void*)&mtask, sizeof(void*)); ++ else ++ __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*)); ++ ++ scsi_queue_work(session->host, &conn->xmitwork); ++ return 0; ++} ++ ++int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, ++ char *data, uint32_t data_size) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ int rc; ++ ++ mutex_lock(&conn->xmitmutex); ++ rc = iscsi_conn_send_generic(conn, hdr, data, data_size); ++ mutex_unlock(&conn->xmitmutex); ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); ++ ++void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) ++{ ++ struct iscsi_session *session = class_to_transport_session(cls_session); ++ struct iscsi_conn *conn = session->leadconn; ++ ++ spin_lock_bh(&session->lock); ++ if (session->state != ISCSI_STATE_LOGGED_IN) { ++ session->state = ISCSI_STATE_RECOVERY_FAILED; ++ if (conn) ++ wake_up(&conn->ehwait); ++ } ++ spin_unlock_bh(&session->lock); ++} ++EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); ++ ++int iscsi_eh_host_reset(struct scsi_cmnd *sc) ++{ ++ struct Scsi_Host *host = sc->device->host; ++ struct iscsi_session *session = iscsi_hostdata(host->hostdata); ++ struct iscsi_conn *conn = session->leadconn; ++ int fail_session = 0; ++ ++ spin_lock_bh(&session->lock); ++ if (session->state == ISCSI_STATE_TERMINATE) { ++failed: ++ debug_scsi("failing host reset: session terminated " ++ "[CID %d age %d]\n", conn->id, session->age); ++ spin_unlock_bh(&session->lock); ++ return FAILED; ++ } ++ ++ if (sc->SCp.phase == session->age) { ++ debug_scsi("failing connection CID %d due to SCSI host reset\n", ++ conn->id); ++ fail_session = 1; ++ } ++ spin_unlock_bh(&session->lock); ++ ++ /* ++ * we drop the lock here but the leadconn cannot be destoyed while ++ * we are in the scsi eh ++ */ ++ if (fail_session) ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ ++ debug_scsi("iscsi_eh_host_reset wait for relogin\n"); ++ wait_event_interruptible(conn->ehwait, ++ session->state == ISCSI_STATE_TERMINATE || ++ session->state == ISCSI_STATE_LOGGED_IN || ++ session->state == ISCSI_STATE_RECOVERY_FAILED); ++ if (signal_pending(current)) ++ flush_signals(current); ++ ++ spin_lock_bh(&session->lock); ++ if (session->state == ISCSI_STATE_LOGGED_IN) ++ printk(KERN_INFO "iscsi: host reset succeeded\n"); ++ else ++ goto failed; ++ spin_unlock_bh(&session->lock); ++ ++ return SUCCESS; ++} ++EXPORT_SYMBOL_GPL(iscsi_eh_host_reset); ++ ++static void iscsi_tmabort_timedout(unsigned long data) ++{ ++ struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)data; ++ struct iscsi_conn *conn = ctask->conn; ++ struct iscsi_session *session = conn->session; ++ ++ spin_lock(&session->lock); ++ if (conn->tmabort_state == TMABORT_INITIAL) { ++ conn->tmabort_state = TMABORT_TIMEDOUT; ++ debug_scsi("tmabort timedout [sc %p itt 0x%x]\n", ++ ctask->sc, ctask->itt); ++ /* unblock eh_abort() */ ++ wake_up(&conn->ehwait); ++ } ++ spin_unlock(&session->lock); ++} ++ ++/* must be called with the mutex lock */ ++static int iscsi_exec_abort_task(struct scsi_cmnd *sc, ++ struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_conn *conn = ctask->conn; ++ struct iscsi_session *session = conn->session; ++ struct iscsi_tm *hdr = &conn->tmhdr; ++ int rc; ++ ++ /* ++ * ctask timed out but session is OK requests must be serialized. ++ */ ++ memset(hdr, 0, sizeof(struct iscsi_tm)); ++ hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; ++ hdr->flags = ISCSI_TM_FUNC_ABORT_TASK; ++ hdr->flags |= ISCSI_FLAG_CMD_FINAL; ++ memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); ++ hdr->rtt = ctask->hdr->itt; ++ hdr->refcmdsn = ctask->hdr->cmdsn; ++ ++ rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr, ++ NULL, 0); ++ if (rc) { ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt, ++ rc); ++ return rc; ++ } ++ ++ debug_scsi("abort sent [itt 0x%x]\n", ctask->itt); ++ ++ spin_lock_bh(&session->lock); ++ ctask->mtask = (struct iscsi_mgmt_task *) ++ session->mgmt_cmds[get_itt(hdr->itt) - ++ ISCSI_MGMT_ITT_OFFSET]; ++ ++ if (conn->tmabort_state == TMABORT_INITIAL) { ++ conn->tmfcmd_pdus_cnt++; ++ conn->tmabort_timer.expires = 10*HZ + jiffies; ++ conn->tmabort_timer.function = iscsi_tmabort_timedout; ++ conn->tmabort_timer.data = (unsigned long)ctask; ++ add_timer(&conn->tmabort_timer); ++ debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt); ++ } ++ spin_unlock_bh(&session->lock); ++ mutex_unlock(&conn->xmitmutex); ++ ++ /* ++ * block eh thread until: ++ * ++ * 1) abort response ++ * 2) abort timeout ++ * 3) session is terminated or restarted or userspace has ++ * given up on recovery ++ */ ++ wait_event_interruptible(conn->ehwait, ++ sc->SCp.phase != session->age || ++ session->state != ISCSI_STATE_LOGGED_IN || ++ conn->tmabort_state != TMABORT_INITIAL); ++ if (signal_pending(current)) ++ flush_signals(current); ++ del_timer_sync(&conn->tmabort_timer); ++ ++ mutex_lock(&conn->xmitmutex); ++ return 0; ++} ++ ++/* ++ * xmit mutex and session lock must be held ++ */ ++static struct iscsi_mgmt_task * ++iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt) ++{ ++ int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*); ++ struct iscsi_mgmt_task *task; ++ ++ debug_scsi("searching %d tasks\n", nr_tasks); ++ ++ for (i = 0; i < nr_tasks; i++) { ++ __kfifo_get(fifo, (void*)&task, sizeof(void*)); ++ debug_scsi("check task %u\n", task->itt); ++ ++ if (task->itt == itt) { ++ debug_scsi("matched task\n"); ++ return task; ++ } ++ ++ __kfifo_put(fifo, (void*)&task, sizeof(void*)); ++ } ++ return NULL; ++} ++ ++static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask) ++{ ++ struct iscsi_conn *conn = ctask->conn; ++ struct iscsi_session *session = conn->session; ++ ++ if (!ctask->mtask) ++ return -EINVAL; ++ ++ if (!iscsi_remove_mgmt_task(conn->immqueue, ctask->mtask->itt)) ++ list_del(&ctask->mtask->running); ++ __kfifo_put(session->mgmtpool.queue, (void*)&ctask->mtask, ++ sizeof(void*)); ++ ctask->mtask = NULL; ++ return 0; ++} ++ ++/* ++ * session lock and xmitmutex must be held ++ */ ++static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, ++ int err) ++{ ++ struct scsi_cmnd *sc; ++ ++ sc = ctask->sc; ++ if (!sc) ++ return; ++ ++ conn->session->tt->cleanup_cmd_task(conn, ctask); ++ iscsi_ctask_mtask_cleanup(ctask); ++ ++ sc->result = err; ++ sc->resid = sc->request_bufflen; ++ /* release ref from queuecommand */ ++ __iscsi_put_ctask(ctask); ++} ++ ++int iscsi_eh_abort(struct scsi_cmnd *sc) ++{ ++ struct iscsi_cmd_task *ctask; ++ struct iscsi_conn *conn; ++ struct iscsi_session *session; ++ int rc; ++ ++ /* ++ * if session was ISCSI_STATE_IN_RECOVERY then we may not have ++ * got the command. ++ */ ++ if (!sc->SCp.ptr) { ++ debug_scsi("sc never reached iscsi layer or it completed.\n"); ++ return SUCCESS; ++ } ++ ++ ctask = (struct iscsi_cmd_task *)sc->SCp.ptr; ++ conn = ctask->conn; ++ session = conn->session; ++ ++ conn->eh_abort_cnt++; ++ debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt); ++ ++ mutex_lock(&conn->xmitmutex); ++ spin_lock_bh(&session->lock); ++ ++ /* ++ * If we are not logged in or we have started a new session ++ * then let the host reset code handle this ++ */ ++ if (session->state != ISCSI_STATE_LOGGED_IN || ++ sc->SCp.phase != session->age) ++ goto failed; ++ ++ /* ctask completed before time out */ ++ if (!ctask->sc) { ++ spin_unlock_bh(&session->lock); ++ debug_scsi("sc completed while abort in progress\n"); ++ goto success_rel_mutex; ++ } ++ ++ /* what should we do here ? */ ++ if (conn->ctask == ctask) { ++ printk(KERN_INFO "iscsi: sc %p itt 0x%x partially sent. " ++ "Failing abort\n", sc, ctask->itt); ++ goto failed; ++ } ++ ++ if (ctask->state == ISCSI_TASK_PENDING) ++ goto success_cleanup; ++ ++ conn->tmabort_state = TMABORT_INITIAL; ++ ++ spin_unlock_bh(&session->lock); ++ rc = iscsi_exec_abort_task(sc, ctask); ++ spin_lock_bh(&session->lock); ++ ++ if (rc || sc->SCp.phase != session->age || ++ session->state != ISCSI_STATE_LOGGED_IN) ++ goto failed; ++ iscsi_ctask_mtask_cleanup(ctask); ++ ++ switch (conn->tmabort_state) { ++ case TMABORT_SUCCESS: ++ goto success_cleanup; ++ case TMABORT_NOT_FOUND: ++ if (!ctask->sc) { ++ /* ctask completed before tmf abort response */ ++ spin_unlock_bh(&session->lock); ++ debug_scsi("sc completed while abort in progress\n"); ++ goto success_rel_mutex; ++ } ++ /* fall through */ ++ default: ++ /* timedout or failed */ ++ spin_unlock_bh(&session->lock); ++ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); ++ spin_lock_bh(&session->lock); ++ goto failed; ++ } ++ ++success_cleanup: ++ debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); ++ spin_unlock_bh(&session->lock); ++ ++ /* ++ * clean up task if aborted. we have the xmitmutex so grab ++ * the recv lock as a writer ++ */ ++ write_lock_bh(conn->recv_lock); ++ spin_lock(&session->lock); ++ fail_command(conn, ctask, DID_ABORT << 16); ++ spin_unlock(&session->lock); ++ write_unlock_bh(conn->recv_lock); ++ ++success_rel_mutex: ++ mutex_unlock(&conn->xmitmutex); ++ return SUCCESS; ++ ++failed: ++ spin_unlock_bh(&session->lock); ++ mutex_unlock(&conn->xmitmutex); ++ ++ debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt); ++ return FAILED; ++} ++EXPORT_SYMBOL_GPL(iscsi_eh_abort); ++ ++int ++iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size) ++{ ++ int i; ++ ++ *items = kmalloc(max * sizeof(void*), GFP_KERNEL); ++ if (*items == NULL) ++ return -ENOMEM; ++ ++ q->max = max; ++ q->pool = kmalloc(max * sizeof(void*), GFP_KERNEL); ++ if (q->pool == NULL) { ++ kfree(*items); ++ return -ENOMEM; ++ } ++ ++ q->queue = kfifo_init((void*)q->pool, max * sizeof(void*), ++ GFP_KERNEL, NULL); ++ if (q->queue == ERR_PTR(-ENOMEM)) { ++ kfree(q->pool); ++ kfree(*items); ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < max; i++) { ++ q->pool[i] = kmalloc(item_size, GFP_KERNEL); ++ if (q->pool[i] == NULL) { ++ int j; ++ ++ for (j = 0; j < i; j++) ++ kfree(q->pool[j]); ++ ++ kfifo_free(q->queue); ++ kfree(q->pool); ++ kfree(*items); ++ return -ENOMEM; ++ } ++ memset(q->pool[i], 0, item_size); ++ (*items)[i] = q->pool[i]; ++ __kfifo_put(q->queue, (void*)&q->pool[i], sizeof(void*)); ++ } ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_pool_init); ++ ++void iscsi_pool_free(struct iscsi_queue *q, void **items) ++{ ++ int i; ++ ++ for (i = 0; i < q->max; i++) ++ kfree(items[i]); ++ kfree(q->pool); ++ kfree(items); ++} ++EXPORT_SYMBOL_GPL(iscsi_pool_free); ++ ++/* ++ * iSCSI Session's hostdata organization: ++ * ++ * *------------------* <== hostdata_session(host->hostdata) ++ * | ptr to class sess| ++ * |------------------| <== iscsi_hostdata(host->hostdata) ++ * | iscsi_session | ++ * *------------------* ++ */ ++ ++#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \ ++ _sz % sizeof(unsigned long)) ++ ++#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata)) ++ ++/** ++ * iscsi_session_setup - create iscsi cls session and host and session ++ * @scsit: scsi transport template ++ * @iscsit: iscsi transport template ++ * @initial_cmdsn: initial CmdSN ++ * @hostno: host no allocated ++ * ++ * This can be used by software iscsi_transports that allocate ++ * a session per scsi host. ++ **/ ++struct iscsi_cls_session * ++iscsi_session_setup(struct iscsi_transport *iscsit, ++ struct scsi_transport_template *scsit, ++ int cmd_task_size, int mgmt_task_size, ++ uint32_t initial_cmdsn, uint32_t *hostno) ++{ ++ struct Scsi_Host *shost; ++ struct iscsi_session *session; ++ struct iscsi_cls_session *cls_session; ++ int cmd_i; ++ ++ shost = scsi_host_alloc(iscsit->host_template, ++ hostdata_privsize(sizeof(*session))); ++ if (!shost) ++ return NULL; ++ ++ shost->max_id = 1; ++ shost->max_channel = 0; ++ shost->max_lun = iscsit->max_lun; ++ shost->max_cmd_len = iscsit->max_cmd_len; ++ shost->transportt = scsit; ++ shost->transportt->create_work_queue = 1; ++ *hostno = shost->host_no; ++ ++ session = iscsi_hostdata(shost->hostdata); ++ memset(session, 0, sizeof(struct iscsi_session)); ++ session->host = shost; ++ session->state = ISCSI_STATE_FREE; ++ session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX; ++ session->cmds_max = ISCSI_XMIT_CMDS_MAX; ++ session->cmdsn = initial_cmdsn; ++ session->exp_cmdsn = initial_cmdsn + 1; ++ session->max_cmdsn = initial_cmdsn + 1; ++ session->max_r2t = 1; ++ session->tt = iscsit; ++ ++ /* initialize SCSI PDU commands pool */ ++ if (iscsi_pool_init(&session->cmdpool, session->cmds_max, ++ (void***)&session->cmds, ++ cmd_task_size + sizeof(struct iscsi_cmd_task))) ++ goto cmdpool_alloc_fail; ++ ++ /* pre-format cmds pool with ITT */ ++ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { ++ struct iscsi_cmd_task *ctask = session->cmds[cmd_i]; ++ ++ if (cmd_task_size) ++ ctask->dd_data = &ctask[1]; ++ ctask->itt = cmd_i; ++ INIT_LIST_HEAD(&ctask->running); ++ } ++ ++ spin_lock_init(&session->lock); ++ ++ /* initialize immediate command pool */ ++ if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max, ++ (void***)&session->mgmt_cmds, ++ mgmt_task_size + sizeof(struct iscsi_mgmt_task))) ++ goto mgmtpool_alloc_fail; ++ ++ ++ /* pre-format immediate cmds pool with ITT */ ++ for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) { ++ struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i]; ++ ++ if (mgmt_task_size) ++ mtask->dd_data = &mtask[1]; ++ mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i; ++ INIT_LIST_HEAD(&mtask->running); ++ } ++ ++ if (scsi_add_host(shost, NULL)) ++ goto add_host_fail; ++ ++ if (!try_module_get(iscsit->owner)) ++ goto cls_session_fail; ++ ++ cls_session = iscsi_create_session(shost, iscsit, 0); ++ if (!cls_session) ++ goto module_put; ++ *(unsigned long*)shost->hostdata = (unsigned long)cls_session; ++ ++ return cls_session; ++ ++module_put: ++ module_put(iscsit->owner); ++cls_session_fail: ++ scsi_remove_host(shost); ++add_host_fail: ++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); ++mgmtpool_alloc_fail: ++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds); ++cmdpool_alloc_fail: ++ scsi_host_put(shost); ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(iscsi_session_setup); ++ ++/** ++ * iscsi_session_teardown - destroy session, host, and cls_session ++ * shost: scsi host ++ * ++ * This can be used by software iscsi_transports that allocate ++ * a session per scsi host. ++ **/ ++void iscsi_session_teardown(struct iscsi_cls_session *cls_session) ++{ ++ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); ++ struct iscsi_session *session = iscsi_hostdata(shost->hostdata); ++ struct module *owner = cls_session->transport->owner; ++ ++ scsi_remove_host(shost); ++ ++ iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds); ++ iscsi_pool_free(&session->cmdpool, (void**)session->cmds); ++ ++ kfree(session->targetname); ++ ++ iscsi_destroy_session(cls_session); ++ scsi_host_put(shost); ++ module_put(owner); ++} ++EXPORT_SYMBOL_GPL(iscsi_session_teardown); ++ ++/** ++ * iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn ++ * @cls_session: iscsi_cls_session ++ * @conn_idx: cid ++ **/ ++struct iscsi_cls_conn * ++iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx) ++{ ++ struct iscsi_session *session = class_to_transport_session(cls_session); ++ struct iscsi_conn *conn; ++ struct iscsi_cls_conn *cls_conn; ++ char *data; ++ ++ cls_conn = iscsi_create_conn(cls_session, conn_idx); ++ if (!cls_conn) ++ return NULL; ++ conn = cls_conn->dd_data; ++ memset(conn, 0, sizeof(*conn)); ++ ++ conn->session = session; ++ conn->cls_conn = cls_conn; ++ conn->c_stage = ISCSI_CONN_INITIAL_STAGE; ++ conn->id = conn_idx; ++ conn->exp_statsn = 0; ++ conn->tmabort_state = TMABORT_INITIAL; ++ INIT_LIST_HEAD(&conn->run_list); ++ INIT_LIST_HEAD(&conn->mgmt_run_list); ++ INIT_LIST_HEAD(&conn->xmitqueue); ++ ++ /* initialize general immediate & non-immediate PDU commands queue */ ++ conn->immqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*), ++ GFP_KERNEL, NULL); ++ if (conn->immqueue == ERR_PTR(-ENOMEM)) ++ goto immqueue_alloc_fail; ++ ++ conn->mgmtqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*), ++ GFP_KERNEL, NULL); ++ if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) ++ goto mgmtqueue_alloc_fail; ++ ++ INIT_WORK(&conn->xmitwork, iscsi_xmitworker); ++ ++ /* allocate login_mtask used for the login/text sequences */ ++ spin_lock_bh(&session->lock); ++ if (!__kfifo_get(session->mgmtpool.queue, ++ (void*)&conn->login_mtask, ++ sizeof(void*))) { ++ spin_unlock_bh(&session->lock); ++ goto login_mtask_alloc_fail; ++ } ++ spin_unlock_bh(&session->lock); ++ ++ data = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL); ++ if (!data) ++ goto login_mtask_data_alloc_fail; ++ conn->login_mtask->data = conn->data = data; ++ ++ init_timer(&conn->tmabort_timer); ++ mutex_init(&conn->xmitmutex); ++ init_waitqueue_head(&conn->ehwait); ++ ++ return cls_conn; ++ ++login_mtask_data_alloc_fail: ++ __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, ++ sizeof(void*)); ++login_mtask_alloc_fail: ++ kfifo_free(conn->mgmtqueue); ++mgmtqueue_alloc_fail: ++ kfifo_free(conn->immqueue); ++immqueue_alloc_fail: ++ iscsi_destroy_conn(cls_conn); ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_setup); ++ ++/** ++ * iscsi_conn_teardown - teardown iscsi connection ++ * cls_conn: iscsi class connection ++ * ++ * TODO: we may need to make this into a two step process ++ * like scsi-mls remove + put host ++ */ ++void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_session *session = conn->session; ++ unsigned long flags; ++ ++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); ++ mutex_lock(&conn->xmitmutex); ++ ++ spin_lock_bh(&session->lock); ++ conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; ++ if (session->leadconn == conn) { ++ /* ++ * leading connection? then give up on recovery. ++ */ ++ session->state = ISCSI_STATE_TERMINATE; ++ wake_up(&conn->ehwait); ++ } ++ spin_unlock_bh(&session->lock); ++ ++ mutex_unlock(&conn->xmitmutex); ++ ++ /* ++ * Block until all in-progress commands for this connection ++ * time out or fail. ++ */ ++ for (;;) { ++ spin_lock_irqsave(session->host->host_lock, flags); ++ if (!session->host->host_busy) { /* OK for ERL == 0 */ ++ spin_unlock_irqrestore(session->host->host_lock, flags); ++ break; ++ } ++ spin_unlock_irqrestore(session->host->host_lock, flags); ++ msleep_interruptible(500); ++ printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d " ++ "host_failed %d\n", session->host->host_busy, ++ session->host->host_failed); ++ /* ++ * force eh_abort() to unblock ++ */ ++ wake_up(&conn->ehwait); ++ } ++ ++ /* flush queued up work because we free the connection below */ ++ scsi_flush_work(session->host); ++ ++ spin_lock_bh(&session->lock); ++ kfree(conn->data); ++ kfree(conn->persistent_address); ++ __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, ++ sizeof(void*)); ++ if (session->leadconn == conn) { ++ session->leadconn = NULL; ++ /* no connections exits.. reset sequencing */ ++ session->cmdsn = session->max_cmdsn = session->exp_cmdsn = 1; ++ } ++ spin_unlock_bh(&session->lock); ++ ++ kfifo_free(conn->immqueue); ++ kfifo_free(conn->mgmtqueue); ++ ++ iscsi_destroy_conn(cls_conn); ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_teardown); ++ ++int iscsi_conn_start(struct iscsi_cls_conn *cls_conn) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_session *session = conn->session; ++ ++ if (!session) { ++ printk(KERN_ERR "iscsi: can't start unbound connection\n"); ++ return -EPERM; ++ } ++ ++ if ((session->imm_data_en || !session->initial_r2t_en) && ++ session->first_burst > session->max_burst) { ++ printk("iscsi: invalid burst lengths: " ++ "first_burst %d max_burst %d\n", ++ session->first_burst, session->max_burst); ++ return -EINVAL; ++ } ++ ++ spin_lock_bh(&session->lock); ++ conn->c_stage = ISCSI_CONN_STARTED; ++ session->state = ISCSI_STATE_LOGGED_IN; ++ ++ switch(conn->stop_stage) { ++ case STOP_CONN_RECOVER: ++ /* ++ * unblock eh_abort() if it is blocked. re-try all ++ * commands after successful recovery ++ */ ++ conn->stop_stage = 0; ++ conn->tmabort_state = TMABORT_INITIAL; ++ session->age++; ++ spin_unlock_bh(&session->lock); ++ ++ iscsi_unblock_session(session_to_cls(session)); ++ wake_up(&conn->ehwait); ++ return 0; ++ case STOP_CONN_TERM: ++ conn->stop_stage = 0; ++ break; ++ default: ++ break; ++ } ++ spin_unlock_bh(&session->lock); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_start); ++ ++static void ++flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn) ++{ ++ struct iscsi_mgmt_task *mtask, *tmp; ++ ++ /* handle pending */ ++ while (__kfifo_get(conn->immqueue, (void*)&mtask, sizeof(void*)) || ++ __kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) { ++ if (mtask == conn->login_mtask) ++ continue; ++ debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt); ++ __kfifo_put(session->mgmtpool.queue, (void*)&mtask, ++ sizeof(void*)); ++ } ++ ++ /* handle running */ ++ list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) { ++ debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt); ++ list_del(&mtask->running); ++ ++ if (mtask == conn->login_mtask) ++ continue; ++ __kfifo_put(session->mgmtpool.queue, (void*)&mtask, ++ sizeof(void*)); ++ } ++ ++ conn->mtask = NULL; ++} ++ ++/* Fail commands. Mutex and session lock held and recv side suspended */ ++static void fail_all_commands(struct iscsi_conn *conn) ++{ ++ struct iscsi_cmd_task *ctask, *tmp; ++ ++ /* flush pending */ ++ list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) { ++ debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc, ++ ctask->itt); ++ fail_command(conn, ctask, DID_BUS_BUSY << 16); ++ } ++ ++ /* fail all other running */ ++ list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) { ++ debug_scsi("failing in progress sc %p itt 0x%x\n", ++ ctask->sc, ctask->itt); ++ fail_command(conn, ctask, DID_BUS_BUSY << 16); ++ } ++ ++ conn->ctask = NULL; ++} ++ ++static void iscsi_start_session_recovery(struct iscsi_session *session, ++ struct iscsi_conn *conn, int flag) ++{ ++ int old_stop_stage; ++ ++ spin_lock_bh(&session->lock); ++ if (conn->stop_stage == STOP_CONN_TERM) { ++ spin_unlock_bh(&session->lock); ++ return; ++ } ++ ++ /* ++ * When this is called for the in_login state, we only want to clean ++ * up the login task and connection. We do not need to block and set ++ * the recovery state again ++ */ ++ if (flag == STOP_CONN_TERM) ++ session->state = ISCSI_STATE_TERMINATE; ++ else if (conn->stop_stage != STOP_CONN_RECOVER) ++ session->state = ISCSI_STATE_IN_RECOVERY; ++ ++ old_stop_stage = conn->stop_stage; ++ conn->stop_stage = flag; ++ conn->c_stage = ISCSI_CONN_STOPPED; ++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); ++ spin_unlock_bh(&session->lock); ++ ++ write_lock_bh(conn->recv_lock); ++ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); ++ write_unlock_bh(conn->recv_lock); ++ ++ mutex_lock(&conn->xmitmutex); ++ /* ++ * for connection level recovery we should not calculate ++ * header digest. conn->hdr_size used for optimization ++ * in hdr_extract() and will be re-negotiated at ++ * set_param() time. ++ */ ++ if (flag == STOP_CONN_RECOVER) { ++ conn->hdrdgst_en = 0; ++ conn->datadgst_en = 0; ++ if (session->state == ISCSI_STATE_IN_RECOVERY && ++ old_stop_stage != STOP_CONN_RECOVER) { ++ debug_scsi("blocking session\n"); ++ iscsi_block_session(session_to_cls(session)); ++ } ++ } ++ ++ /* ++ * flush queues. ++ */ ++ spin_lock_bh(&session->lock); ++ fail_all_commands(conn); ++ flush_control_queues(session, conn); ++ spin_unlock_bh(&session->lock); ++ ++ mutex_unlock(&conn->xmitmutex); ++} ++ ++void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_session *session = conn->session; ++ ++ switch (flag) { ++ case STOP_CONN_RECOVER: ++ case STOP_CONN_TERM: ++ iscsi_start_session_recovery(session, conn, flag); ++ break; ++ default: ++ printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag); ++ } ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_stop); ++ ++int iscsi_conn_bind(struct iscsi_cls_session *cls_session, ++ struct iscsi_cls_conn *cls_conn, int is_leading) ++{ ++ struct iscsi_session *session = class_to_transport_session(cls_session); ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ ++ spin_lock_bh(&session->lock); ++ if (is_leading) ++ session->leadconn = conn; ++ spin_unlock_bh(&session->lock); ++ ++ /* ++ * Unblock xmitworker(), Login Phase will pass through. ++ */ ++ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); ++ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_bind); ++ ++ ++int iscsi_set_param(struct iscsi_cls_conn *cls_conn, ++ enum iscsi_param param, char *buf, int buflen) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ struct iscsi_session *session = conn->session; ++ uint32_t value; ++ ++ switch(param) { ++ case ISCSI_PARAM_MAX_RECV_DLENGTH: ++ sscanf(buf, "%d", &conn->max_recv_dlength); ++ break; ++ case ISCSI_PARAM_MAX_XMIT_DLENGTH: ++ sscanf(buf, "%d", &conn->max_xmit_dlength); ++ break; ++ case ISCSI_PARAM_HDRDGST_EN: ++ sscanf(buf, "%d", &conn->hdrdgst_en); ++ break; ++ case ISCSI_PARAM_DATADGST_EN: ++ sscanf(buf, "%d", &conn->datadgst_en); ++ break; ++ case ISCSI_PARAM_INITIAL_R2T_EN: ++ sscanf(buf, "%d", &session->initial_r2t_en); ++ break; ++ case ISCSI_PARAM_MAX_R2T: ++ sscanf(buf, "%d", &session->max_r2t); ++ break; ++ case ISCSI_PARAM_IMM_DATA_EN: ++ sscanf(buf, "%d", &session->imm_data_en); ++ break; ++ case ISCSI_PARAM_FIRST_BURST: ++ sscanf(buf, "%d", &session->first_burst); ++ break; ++ case ISCSI_PARAM_MAX_BURST: ++ sscanf(buf, "%d", &session->max_burst); ++ break; ++ case ISCSI_PARAM_PDU_INORDER_EN: ++ sscanf(buf, "%d", &session->pdu_inorder_en); ++ break; ++ case ISCSI_PARAM_DATASEQ_INORDER_EN: ++ sscanf(buf, "%d", &session->dataseq_inorder_en); ++ break; ++ case ISCSI_PARAM_ERL: ++ sscanf(buf, "%d", &session->erl); ++ break; ++ case ISCSI_PARAM_IFMARKER_EN: ++ sscanf(buf, "%d", &value); ++ BUG_ON(value); ++ break; ++ case ISCSI_PARAM_OFMARKER_EN: ++ sscanf(buf, "%d", &value); ++ BUG_ON(value); ++ break; ++ case ISCSI_PARAM_EXP_STATSN: ++ sscanf(buf, "%u", &conn->exp_statsn); ++ break; ++ case ISCSI_PARAM_TARGET_NAME: ++ /* this should not change between logins */ ++ if (session->targetname) ++ break; ++ ++ session->targetname = kstrdup(buf, GFP_KERNEL); ++ if (!session->targetname) ++ return -ENOMEM; ++ break; ++ case ISCSI_PARAM_TPGT: ++ sscanf(buf, "%d", &session->tpgt); ++ break; ++ case ISCSI_PARAM_PERSISTENT_PORT: ++ sscanf(buf, "%d", &conn->persistent_port); ++ break; ++ case ISCSI_PARAM_PERSISTENT_ADDRESS: ++ /* ++ * this is the address returned in discovery so it should ++ * not change between logins. ++ */ ++ if (conn->persistent_address) ++ break; ++ ++ conn->persistent_address = kstrdup(buf, GFP_KERNEL); ++ if (!conn->persistent_address) ++ return -ENOMEM; ++ break; ++ default: ++ return -ENOSYS; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_set_param); ++ ++int iscsi_session_get_param(struct iscsi_cls_session *cls_session, ++ enum iscsi_param param, char *buf) ++{ ++ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); ++ struct iscsi_session *session = iscsi_hostdata(shost->hostdata); ++ int len; ++ ++ switch(param) { ++ case ISCSI_PARAM_INITIAL_R2T_EN: ++ len = sprintf(buf, "%d\n", session->initial_r2t_en); ++ break; ++ case ISCSI_PARAM_MAX_R2T: ++ len = sprintf(buf, "%hu\n", session->max_r2t); ++ break; ++ case ISCSI_PARAM_IMM_DATA_EN: ++ len = sprintf(buf, "%d\n", session->imm_data_en); ++ break; ++ case ISCSI_PARAM_FIRST_BURST: ++ len = sprintf(buf, "%u\n", session->first_burst); ++ break; ++ case ISCSI_PARAM_MAX_BURST: ++ len = sprintf(buf, "%u\n", session->max_burst); ++ break; ++ case ISCSI_PARAM_PDU_INORDER_EN: ++ len = sprintf(buf, "%d\n", session->pdu_inorder_en); ++ break; ++ case ISCSI_PARAM_DATASEQ_INORDER_EN: ++ len = sprintf(buf, "%d\n", session->dataseq_inorder_en); ++ break; ++ case ISCSI_PARAM_ERL: ++ len = sprintf(buf, "%d\n", session->erl); ++ break; ++ case ISCSI_PARAM_TARGET_NAME: ++ len = sprintf(buf, "%s\n", session->targetname); ++ break; ++ case ISCSI_PARAM_TPGT: ++ len = sprintf(buf, "%d\n", session->tpgt); ++ break; ++ default: ++ return -ENOSYS; ++ } ++ ++ return len; ++} ++EXPORT_SYMBOL_GPL(iscsi_session_get_param); ++ ++int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, ++ enum iscsi_param param, char *buf) ++{ ++ struct iscsi_conn *conn = cls_conn->dd_data; ++ int len; ++ ++ switch(param) { ++ case ISCSI_PARAM_MAX_RECV_DLENGTH: ++ len = sprintf(buf, "%u\n", conn->max_recv_dlength); ++ break; ++ case ISCSI_PARAM_MAX_XMIT_DLENGTH: ++ len = sprintf(buf, "%u\n", conn->max_xmit_dlength); ++ break; ++ case ISCSI_PARAM_HDRDGST_EN: ++ len = sprintf(buf, "%d\n", conn->hdrdgst_en); ++ break; ++ case ISCSI_PARAM_DATADGST_EN: ++ len = sprintf(buf, "%d\n", conn->datadgst_en); ++ break; ++ case ISCSI_PARAM_IFMARKER_EN: ++ len = sprintf(buf, "%d\n", conn->ifmarker_en); ++ break; ++ case ISCSI_PARAM_OFMARKER_EN: ++ len = sprintf(buf, "%d\n", conn->ofmarker_en); ++ break; ++ case ISCSI_PARAM_EXP_STATSN: ++ len = sprintf(buf, "%u\n", conn->exp_statsn); ++ break; ++ case ISCSI_PARAM_PERSISTENT_PORT: ++ len = sprintf(buf, "%d\n", conn->persistent_port); ++ break; ++ case ISCSI_PARAM_PERSISTENT_ADDRESS: ++ len = sprintf(buf, "%s\n", conn->persistent_address); ++ break; ++ default: ++ return -ENOSYS; ++ } ++ ++ return len; ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_get_param); ++ ++MODULE_AUTHOR("Mike Christie"); ++MODULE_DESCRIPTION("iSCSI library functions"); ++MODULE_LICENSE("GPL"); +Index: kernel/iscsi_tcp.h +=================================================================== +--- kernel/iscsi_tcp.h (revision 0) ++++ kernel/iscsi_tcp.h (revision 779) +@@ -0,0 +1,167 @@ ++/* ++ * iSCSI Initiator TCP Transport ++ * Copyright (C) 2004 Dmitry Yusupov ++ * Copyright (C) 2004 Alex Aizman ++ * Copyright (C) 2005 - 2006 Mike Christie ++ * Copyright (C) 2006 Red Hat, Inc. All rights reserved. ++ * maintained by open-iscsi@googlegroups.com ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * See the file COPYING included with this distribution for more details. ++ */ ++ ++#ifndef ISCSI_TCP_H ++#define ISCSI_TCP_H ++ ++#include "libiscsi.h" ++ ++/* Socket's Receive state machine */ ++#define IN_PROGRESS_WAIT_HEADER 0x0 ++#define IN_PROGRESS_HEADER_GATHER 0x1 ++#define IN_PROGRESS_DATA_RECV 0x2 ++#define IN_PROGRESS_DDIGEST_RECV 0x3 ++ ++/* xmit state machine */ ++#define XMSTATE_IDLE 0x0 ++#define XMSTATE_R_HDR 0x1 ++#define XMSTATE_W_HDR 0x2 ++#define XMSTATE_IMM_HDR 0x4 ++#define XMSTATE_IMM_DATA 0x8 ++#define XMSTATE_UNS_INIT 0x10 ++#define XMSTATE_UNS_HDR 0x20 ++#define XMSTATE_UNS_DATA 0x40 ++#define XMSTATE_SOL_HDR 0x80 ++#define XMSTATE_SOL_DATA 0x100 ++#define XMSTATE_W_PAD 0x200 ++#define XMSTATE_W_RESEND_PAD 0x400 ++#define XMSTATE_W_RESEND_DATA_DIGEST 0x800 ++ ++#define ISCSI_PAD_LEN 4 ++#define ISCSI_SG_TABLESIZE SG_ALL ++#define ISCSI_TCP_MAX_CMD_LEN 16 ++ ++struct crypto_hash; ++struct socket; ++ ++/* Socket connection recieve helper */ ++struct iscsi_tcp_recv { ++ struct iscsi_hdr *hdr; ++ struct sk_buff *skb; ++ int offset; ++ int len; ++ int hdr_offset; ++ int copy; ++ int copied; ++ int padding; ++ struct iscsi_cmd_task *ctask; /* current cmd in progress */ ++ ++ /* copied and flipped values */ ++ int datalen; ++ int datadgst; ++ char zero_copy_hdr; ++}; ++ ++struct iscsi_tcp_conn { ++ struct iscsi_conn *iscsi_conn; ++ struct socket *sock; ++ struct iscsi_hdr hdr; /* header placeholder */ ++ char hdrext[4*sizeof(__u16) + ++ sizeof(__u32)]; ++ int data_copied; ++ int stop_stage; /* conn_stop() flag: * ++ * stop to recover, * ++ * stop to terminate */ ++ /* iSCSI connection-wide sequencing */ ++ int hdr_size; /* PDU header size */ ++ ++ /* control data */ ++ struct iscsi_tcp_recv in; /* TCP receive context */ ++ int in_progress; /* connection state machine */ ++ ++ /* old values for socket callbacks */ ++ void (*old_data_ready)(struct sock *, int); ++ void (*old_state_change)(struct sock *); ++ void (*old_write_space)(struct sock *); ++ ++ /* data and header digests */ ++ struct hash_desc tx_hash; /* CRC32C (Tx) */ ++ struct hash_desc rx_hash; /* CRC32C (Rx) */ ++ ++ /* MIB custom statistics */ ++ uint32_t sendpage_failures_cnt; ++ uint32_t discontiguous_hdr_cnt; ++ ++ ssize_t (*sendpage)(struct socket *, struct page *, int, size_t, int); ++}; ++ ++struct iscsi_buf { ++ struct scatterlist sg; ++ unsigned int sent; ++ char use_sendmsg; ++}; ++ ++struct iscsi_data_task { ++ struct iscsi_data hdr; /* PDU */ ++ char hdrext[sizeof(__u32)]; /* Header-Digest */ ++ struct iscsi_buf digestbuf; /* digest buffer */ ++ uint32_t digest; /* data digest */ ++}; ++ ++struct iscsi_tcp_mgmt_task { ++ struct iscsi_hdr hdr; ++ char hdrext[sizeof(__u32)]; /* Header-Digest */ ++ int xmstate; /* mgmt xmit progress */ ++ struct iscsi_buf headbuf; /* header buffer */ ++ struct iscsi_buf sendbuf; /* in progress buffer */ ++ int sent; ++}; ++ ++struct iscsi_r2t_info { ++ __be32 ttt; /* copied from R2T */ ++ __be32 exp_statsn; /* copied from R2T */ ++ uint32_t data_length; /* copied from R2T */ ++ uint32_t data_offset; /* copied from R2T */ ++ struct iscsi_buf headbuf; /* Data-Out Header Buffer */ ++ struct iscsi_buf sendbuf; /* Data-Out in progress buffer*/ ++ int sent; /* R2T sequence progress */ ++ int data_count; /* DATA-Out payload progress */ ++ struct scatterlist *sg; /* per-R2T SG list */ ++ int solicit_datasn; ++ struct iscsi_data_task dtask; /* which data task */ ++}; ++ ++struct iscsi_tcp_cmd_task { ++ struct iscsi_cmd hdr; ++ char hdrext[4*sizeof(__u16)+ /* AHS */ ++ sizeof(__u32)]; /* HeaderDigest */ ++ char pad[ISCSI_PAD_LEN]; ++ int pad_count; /* padded bytes */ ++ struct iscsi_buf headbuf; /* header buf (xmit) */ ++ struct iscsi_buf sendbuf; /* in progress buffer*/ ++ int xmstate; /* xmit xtate machine */ ++ int sent; ++ struct scatterlist *sg; /* per-cmd SG list */ ++ struct scatterlist *bad_sg; /* assert statement */ ++ int sg_count; /* SG's to process */ ++ uint32_t exp_r2tsn; ++ int data_offset; ++ struct iscsi_r2t_info *r2t; /* in progress R2T */ ++ struct iscsi_queue r2tpool; ++ struct kfifo *r2tqueue; ++ struct iscsi_r2t_info **r2ts; ++ int digest_count; ++ uint32_t immdigest; /* for imm data */ ++ struct iscsi_buf immbuf; /* for imm data digest */ ++ struct iscsi_data_task unsol_dtask; /* unsol data task */ ++}; ++ ++#endif /* ISCSI_H */ +Index: kernel/libiscsi.h +=================================================================== +--- kernel/libiscsi.h (revision 0) ++++ kernel/libiscsi.h (revision 779) +@@ -0,0 +1,311 @@ ++/* ++ * iSCSI lib definitions ++ * ++ * Copyright (C) 2006 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2004 - 2006 Mike Christie ++ * Copyright (C) 2004 - 2005 Dmitry Yusupov ++ * Copyright (C) 2004 - 2005 Alex Aizman ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++#ifndef LIBISCSI_H ++#define LIBISCSI_H ++ ++#include ++#include ++#include ++#include ++#include "iscsi_proto.h" ++#include "iscsi_if.h" ++ ++struct scsi_transport_template; ++struct scsi_device; ++struct Scsi_Host; ++struct scsi_cmnd; ++struct socket; ++struct iscsi_transport; ++struct iscsi_cls_session; ++struct iscsi_cls_conn; ++struct iscsi_session; ++struct iscsi_nopin; ++ ++/* #define DEBUG_SCSI */ ++#ifdef DEBUG_SCSI ++#define debug_scsi(fmt...) printk(KERN_INFO "iscsi: " fmt) ++#else ++#define debug_scsi(fmt...) ++#endif ++ ++#define ISCSI_XMIT_CMDS_MAX 128 /* must be power of 2 */ ++#define ISCSI_MGMT_CMDS_MAX 32 /* must be power of 2 */ ++#define ISCSI_CONN_MAX 1 ++ ++#define ISCSI_MGMT_ITT_OFFSET 0xa00 ++ ++#define ISCSI_DEF_CMD_PER_LUN 32 ++#define ISCSI_MAX_CMD_PER_LUN 128 ++ ++/* Task Mgmt states */ ++#define TMABORT_INITIAL 0x0 ++#define TMABORT_SUCCESS 0x1 ++#define TMABORT_FAILED 0x2 ++#define TMABORT_TIMEDOUT 0x3 ++#define TMABORT_NOT_FOUND 0x4 ++ ++/* Connection suspend "bit" */ ++#define ISCSI_SUSPEND_BIT 1 ++ ++#define ISCSI_ITT_MASK (0xfff) ++#define ISCSI_CID_SHIFT 12 ++#define ISCSI_CID_MASK (0xffff << ISCSI_CID_SHIFT) ++#define ISCSI_AGE_SHIFT 28 ++#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT) ++ ++struct iscsi_mgmt_task { ++ /* ++ * Becuae LLDs allocate their hdr differently, this is a pointer to ++ * that storage. It must be setup at session creation time. ++ */ ++ struct iscsi_hdr *hdr; ++ char *data; /* mgmt payload */ ++ int data_count; /* counts data to be sent */ ++ uint32_t itt; /* this ITT */ ++ void *dd_data; /* driver/transport data */ ++ struct list_head running; ++}; ++ ++enum { ++ ISCSI_TASK_COMPLETED, ++ ISCSI_TASK_PENDING, ++ ISCSI_TASK_RUNNING, ++}; ++ ++struct iscsi_cmd_task { ++ /* ++ * Becuae LLDs allocate their hdr differently, this is a pointer to ++ * that storage. It must be setup at session creation time. ++ */ ++ struct iscsi_cmd *hdr; ++ int itt; /* this ITT */ ++ int datasn; /* DataSN */ ++ ++ uint32_t unsol_datasn; ++ int imm_count; /* imm-data (bytes) */ ++ int unsol_count; /* unsolicited (bytes)*/ ++ /* offset in unsolicited stream (bytes); */ ++ int unsol_offset; ++ int data_count; /* remaining Data-Out */ ++ struct scsi_cmnd *sc; /* associated SCSI cmd*/ ++ int total_length; ++ struct iscsi_conn *conn; /* used connection */ ++ struct iscsi_mgmt_task *mtask; /* tmf mtask in progr */ ++ ++ /* state set/tested under session->lock */ ++ int state; ++ atomic_t refcount; ++ struct list_head running; /* running cmd list */ ++ void *dd_data; /* driver/transport data */ ++}; ++ ++struct iscsi_conn { ++ struct iscsi_cls_conn *cls_conn; /* ptr to class connection */ ++ void *dd_data; /* iscsi_transport data */ ++ struct iscsi_session *session; /* parent session */ ++ /* ++ * LLDs should set this lock. It protects the transport recv ++ * code ++ */ ++ rwlock_t *recv_lock; ++ /* ++ * conn_stop() flag: stop to recover, stop to terminate ++ */ ++ int stop_stage; ++ ++ /* iSCSI connection-wide sequencing */ ++ uint32_t exp_statsn; ++ ++ /* control data */ ++ int id; /* CID */ ++ int c_stage; /* connection state */ ++ /* ++ * Preallocated buffer for pdus that have data but do not ++ * originate from scsi-ml. We never have two pdus using the ++ * buffer at the same time. It is only allocated to ++ * the default max recv size because the pdus we support ++ * should always fit in this buffer ++ */ ++ char *data; ++ struct iscsi_mgmt_task *login_mtask; /* mtask used for login/text */ ++ struct iscsi_mgmt_task *mtask; /* xmit mtask in progress */ ++ struct iscsi_cmd_task *ctask; /* xmit ctask in progress */ ++ ++ /* xmit */ ++ struct kfifo *immqueue; /* immediate xmit queue */ ++ struct kfifo *mgmtqueue; /* mgmt (control) xmit queue */ ++ struct list_head mgmt_run_list; /* list of control tasks */ ++ struct list_head xmitqueue; /* data-path cmd queue */ ++ struct list_head run_list; /* list of cmds in progress */ ++ struct work_struct xmitwork; /* per-conn. xmit workqueue */ ++ /* ++ * serializes connection xmit, access to kfifos: ++ * xmitqueue, immqueue, mgmtqueue ++ */ ++ struct mutex xmitmutex; ++ ++ unsigned long suspend_tx; /* suspend Tx */ ++ unsigned long suspend_rx; /* suspend Rx */ ++ ++ /* abort */ ++ wait_queue_head_t ehwait; /* used in eh_abort() */ ++ struct iscsi_tm tmhdr; ++ struct timer_list tmabort_timer; ++ int tmabort_state; /* see TMABORT_INITIAL, etc.*/ ++ ++ /* negotiated params */ ++ int max_recv_dlength; /* initiator_max_recv_dsl*/ ++ int max_xmit_dlength; /* target_max_recv_dsl */ ++ int hdrdgst_en; ++ int datadgst_en; ++ int ifmarker_en; ++ int ofmarker_en; ++ /* values userspace uses to id a conn */ ++ int persistent_port; ++ char *persistent_address; ++ ++ /* MIB-statistics */ ++ uint64_t txdata_octets; ++ uint64_t rxdata_octets; ++ uint32_t scsicmd_pdus_cnt; ++ uint32_t dataout_pdus_cnt; ++ uint32_t scsirsp_pdus_cnt; ++ uint32_t datain_pdus_cnt; ++ uint32_t r2t_pdus_cnt; ++ uint32_t tmfcmd_pdus_cnt; ++ int32_t tmfrsp_pdus_cnt; ++ ++ /* custom statistics */ ++ uint32_t eh_abort_cnt; ++}; ++ ++struct iscsi_queue { ++ struct kfifo *queue; /* FIFO Queue */ ++ void **pool; /* Pool of elements */ ++ int max; /* Max number of elements */ ++}; ++ ++struct iscsi_session { ++ /* iSCSI session-wide sequencing */ ++ uint32_t cmdsn; ++ uint32_t exp_cmdsn; ++ uint32_t max_cmdsn; ++ ++ /* configuration */ ++ int initial_r2t_en; ++ int max_r2t; ++ int imm_data_en; ++ int first_burst; ++ int max_burst; ++ int time2wait; ++ int time2retain; ++ int pdu_inorder_en; ++ int dataseq_inorder_en; ++ int erl; ++ int tpgt; ++ char *targetname; ++ ++ /* control data */ ++ struct iscsi_transport *tt; ++ struct Scsi_Host *host; ++ struct iscsi_conn *leadconn; /* leading connection */ ++ spinlock_t lock; /* protects session state, * ++ * sequence numbers, * ++ * session resources: * ++ * - cmdpool, * ++ * - mgmtpool, * ++ * - r2tpool */ ++ int state; /* session state */ ++ int age; /* counts session re-opens */ ++ ++ int cmds_max; /* size of cmds array */ ++ struct iscsi_cmd_task **cmds; /* Original Cmds arr */ ++ struct iscsi_queue cmdpool; /* PDU's pool */ ++ int mgmtpool_max; /* size of mgmt array */ ++ struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */ ++ struct iscsi_queue mgmtpool; /* Mgmt PDU's pool */ ++}; ++ ++/* ++ * scsi host template ++ */ ++extern int iscsi_change_queue_depth(struct scsi_device *sdev, int depth); ++extern int iscsi_eh_abort(struct scsi_cmnd *sc); ++extern int iscsi_eh_host_reset(struct scsi_cmnd *sc); ++extern int iscsi_queuecommand(struct scsi_cmnd *sc, ++ void (*done)(struct scsi_cmnd *)); ++ ++/* ++ * session management ++ */ ++extern struct iscsi_cls_session * ++iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *, ++ int, int, uint32_t, uint32_t *); ++extern void iscsi_session_teardown(struct iscsi_cls_session *); ++extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *); ++extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *); ++extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn, ++ enum iscsi_param param, char *buf, int buflen); ++extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session, ++ enum iscsi_param param, char *buf); ++ ++#define session_to_cls(_sess) \ ++ hostdata_session(_sess->host->hostdata) ++ ++/* ++ * connection management ++ */ ++extern struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *, ++ uint32_t); ++extern void iscsi_conn_teardown(struct iscsi_cls_conn *); ++extern int iscsi_conn_start(struct iscsi_cls_conn *); ++extern void iscsi_conn_stop(struct iscsi_cls_conn *, int); ++extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *, ++ int); ++extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err); ++extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, ++ enum iscsi_param param, char *buf); ++ ++/* ++ * pdu and task processing ++ */ ++extern int iscsi_check_assign_cmdsn(struct iscsi_session *, ++ struct iscsi_nopin *); ++extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *, ++ struct iscsi_data *hdr); ++extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *, ++ char *, uint32_t); ++extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *, ++ char *, int); ++extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *, ++ char *, int); ++extern int iscsi_verify_itt(struct iscsi_conn *, struct iscsi_hdr *, ++ uint32_t *); ++ ++/* ++ * generic helpers ++ */ ++extern void iscsi_pool_free(struct iscsi_queue *, void **); ++extern int iscsi_pool_init(struct iscsi_queue *, int, void ***, int); ++ ++#endif +Index: kernel/scsi_transport_iscsi.c +=================================================================== +--- kernel/scsi_transport_iscsi.c (revision 0) ++++ kernel/scsi_transport_iscsi.c (revision 779) +@@ -0,0 +1,1475 @@ ++/* ++ * iSCSI transport class definitions ++ * ++ * Copyright (C) IBM Corporation, 2004 ++ * Copyright (C) Mike Christie, 2004 - 2005 ++ * Copyright (C) Dmitry Yusupov, 2004 - 2005 ++ * Copyright (C) Alex Aizman, 2004 - 2005 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "scsi_transport_iscsi.h" ++#include "iscsi_if.h" ++ ++#define ISCSI_SESSION_ATTRS 11 ++#define ISCSI_CONN_ATTRS 11 ++#define ISCSI_HOST_ATTRS 0 ++#define ISCSI_TRANSPORT_VERSION "2.0-724" ++ ++struct iscsi_internal { ++ int daemon_pid; ++ struct scsi_transport_template t; ++ struct iscsi_transport *iscsi_transport; ++ struct list_head list; ++ struct class_device cdev; ++ ++ struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1]; ++ struct transport_container conn_cont; ++ struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1]; ++ struct transport_container session_cont; ++ struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1]; ++}; ++ ++static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ ++ ++/* ++ * list of registered transports and lock that must ++ * be held while accessing list. The iscsi_transport_lock must ++ * be acquired after the rx_queue_mutex. ++ */ ++static LIST_HEAD(iscsi_transports); ++static DEFINE_SPINLOCK(iscsi_transport_lock); ++ ++#define to_iscsi_internal(tmpl) \ ++ container_of(tmpl, struct iscsi_internal, t) ++ ++#define cdev_to_iscsi_internal(_cdev) \ ++ container_of(_cdev, struct iscsi_internal, cdev) ++ ++static void iscsi_transport_release(struct class_device *cdev) ++{ ++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); ++ kfree(priv); ++} ++ ++/* ++ * iscsi_transport_class represents the iscsi_transports that are ++ * registered. ++ */ ++static struct class iscsi_transport_class = { ++ .name = "iscsi_transport", ++ .release = iscsi_transport_release, ++}; ++ ++static ssize_t ++show_transport_handle(struct class_device *cdev, char *buf) ++{ ++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); ++ return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport)); ++} ++static CLASS_DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL); ++ ++#define show_transport_attr(name, format) \ ++static ssize_t \ ++show_transport_##name(struct class_device *cdev, char *buf) \ ++{ \ ++ struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev); \ ++ return sprintf(buf, format"\n", priv->iscsi_transport->name); \ ++} \ ++static CLASS_DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL); ++ ++show_transport_attr(caps, "0x%x"); ++show_transport_attr(max_lun, "%d"); ++show_transport_attr(max_conn, "%d"); ++show_transport_attr(max_cmd_len, "%d"); ++ ++static struct attribute *iscsi_transport_attrs[] = { ++ &class_device_attr_handle.attr, ++ &class_device_attr_caps.attr, ++ &class_device_attr_max_lun.attr, ++ &class_device_attr_max_conn.attr, ++ &class_device_attr_max_cmd_len.attr, ++ NULL, ++}; ++ ++static struct attribute_group iscsi_transport_group = { ++ .attrs = iscsi_transport_attrs, ++}; ++ ++static int iscsi_setup_host(struct transport_container *tc, struct device *dev, ++ struct class_device *cdev) ++{ ++ struct Scsi_Host *shost = dev_to_shost(dev); ++ struct iscsi_host *ihost = shost->shost_data; ++ ++ memset(ihost, 0, sizeof(*ihost)); ++ INIT_LIST_HEAD(&ihost->sessions); ++ mutex_init(&ihost->mutex); ++ return 0; ++} ++ ++static DECLARE_TRANSPORT_CLASS(iscsi_host_class, ++ "iscsi_host", ++ iscsi_setup_host, ++ NULL, ++ NULL); ++ ++static DECLARE_TRANSPORT_CLASS(iscsi_session_class, ++ "iscsi_session", ++ NULL, ++ NULL, ++ NULL); ++ ++static DECLARE_TRANSPORT_CLASS(iscsi_connection_class, ++ "iscsi_connection", ++ NULL, ++ NULL, ++ NULL); ++ ++static struct sock *nls; ++static DEFINE_MUTEX(rx_queue_mutex); ++ ++static LIST_HEAD(sesslist); ++static DEFINE_SPINLOCK(sesslock); ++static LIST_HEAD(connlist); ++static DEFINE_SPINLOCK(connlock); ++ ++static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn) ++{ ++ struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent); ++ return sess->sid; ++} ++ ++/* ++ * Returns the matching session to a given sid ++ */ ++static struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid) ++{ ++ unsigned long flags; ++ struct iscsi_cls_session *sess; ++ ++ spin_lock_irqsave(&sesslock, flags); ++ list_for_each_entry(sess, &sesslist, sess_list) { ++ if (sess->sid == sid) { ++ spin_unlock_irqrestore(&sesslock, flags); ++ return sess; ++ } ++ } ++ spin_unlock_irqrestore(&sesslock, flags); ++ return NULL; ++} ++ ++/* ++ * Returns the matching connection to a given sid / cid tuple ++ */ ++static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid) ++{ ++ unsigned long flags; ++ struct iscsi_cls_conn *conn; ++ ++ spin_lock_irqsave(&connlock, flags); ++ list_for_each_entry(conn, &connlist, conn_list) { ++ if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) { ++ spin_unlock_irqrestore(&connlock, flags); ++ return conn; ++ } ++ } ++ spin_unlock_irqrestore(&connlock, flags); ++ return NULL; ++} ++ ++/* ++ * The following functions can be used by LLDs that allocate ++ * their own scsi_hosts or by software iscsi LLDs ++ */ ++static void iscsi_session_release(struct device *dev) ++{ ++ struct iscsi_cls_session *session = iscsi_dev_to_session(dev); ++ struct Scsi_Host *shost; ++ ++ shost = iscsi_session_to_shost(session); ++ scsi_host_put(shost); ++ kfree(session); ++} ++ ++static int iscsi_is_session_dev(const struct device *dev) ++{ ++ return dev->release == iscsi_session_release; ++} ++ ++static int iscsi_user_scan(struct Scsi_Host *shost, uint channel, ++ uint id, uint lun) ++{ ++ struct iscsi_host *ihost = shost->shost_data; ++ struct iscsi_cls_session *session; ++ ++ mutex_lock(&ihost->mutex); ++ list_for_each_entry(session, &ihost->sessions, host_list) { ++ if ((channel == SCAN_WILD_CARD || channel == 0) && ++ (id == SCAN_WILD_CARD || id == session->target_id)) ++ scsi_scan_target(&session->dev, 0, ++ session->target_id, lun, 1); ++ } ++ mutex_unlock(&ihost->mutex); ++ ++ return 0; ++} ++ ++static void session_recovery_timedout(struct work_struct *work) ++{ ++ struct iscsi_cls_session *session = ++ container_of(work, struct iscsi_cls_session, ++ recovery_work.work); ++ ++ dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " ++ "out after %d secs\n", session->recovery_tmo); ++ ++ if (session->transport->session_recovery_timedout) ++ session->transport->session_recovery_timedout(session); ++ ++ scsi_target_unblock(&session->dev); ++} ++ ++void iscsi_unblock_session(struct iscsi_cls_session *session) ++{ ++ if (!cancel_delayed_work(&session->recovery_work)) ++ flush_scheduled_work(); ++ scsi_target_unblock(&session->dev); ++} ++EXPORT_SYMBOL_GPL(iscsi_unblock_session); ++ ++void iscsi_block_session(struct iscsi_cls_session *session) ++{ ++ scsi_target_block(&session->dev); ++ schedule_delayed_work(&session->recovery_work, ++ session->recovery_tmo * HZ); ++} ++EXPORT_SYMBOL_GPL(iscsi_block_session); ++ ++struct iscsi_cls_session * ++iscsi_alloc_session(struct Scsi_Host *shost, ++ struct iscsi_transport *transport) ++{ ++ struct iscsi_cls_session *session; ++ ++ session = kzalloc(sizeof(*session) + transport->sessiondata_size, ++ GFP_KERNEL); ++ if (!session) ++ return NULL; ++ ++ session->transport = transport; ++ session->recovery_tmo = 120; ++ INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); ++ INIT_LIST_HEAD(&session->host_list); ++ INIT_LIST_HEAD(&session->sess_list); ++ ++ /* this is released in the dev's release function */ ++ scsi_host_get(shost); ++ session->dev.parent = &shost->shost_gendev; ++ session->dev.release = iscsi_session_release; ++ device_initialize(&session->dev); ++ if (transport->sessiondata_size) ++ session->dd_data = &session[1]; ++ return session; ++} ++EXPORT_SYMBOL_GPL(iscsi_alloc_session); ++ ++int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) ++{ ++ struct Scsi_Host *shost = iscsi_session_to_shost(session); ++ struct iscsi_host *ihost; ++ int err; ++ ++ ihost = shost->shost_data; ++ session->sid = atomic_add_return(1, &iscsi_session_nr); ++ session->target_id = target_id; ++ ++ snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", ++ session->sid); ++ err = device_add(&session->dev); ++ if (err) { ++ dev_printk(KERN_ERR, &session->dev, "iscsi: could not " ++ "register session's dev\n"); ++ goto release_host; ++ } ++ transport_register_device(&session->dev); ++ ++ mutex_lock(&ihost->mutex); ++ list_add(&session->host_list, &ihost->sessions); ++ mutex_unlock(&ihost->mutex); ++ return 0; ++ ++release_host: ++ scsi_host_put(shost); ++ return err; ++} ++EXPORT_SYMBOL_GPL(iscsi_add_session); ++ ++/** ++ * iscsi_create_session - create iscsi class session ++ * @shost: scsi host ++ * @transport: iscsi transport ++ * ++ * This can be called from a LLD or iscsi_transport. ++ **/ ++struct iscsi_cls_session * ++iscsi_create_session(struct Scsi_Host *shost, ++ struct iscsi_transport *transport, ++ unsigned int target_id) ++{ ++ struct iscsi_cls_session *session; ++ ++ session = iscsi_alloc_session(shost, transport); ++ if (!session) ++ return NULL; ++ ++ if (iscsi_add_session(session, target_id)) { ++ iscsi_free_session(session); ++ return NULL; ++ } ++ return session; ++} ++EXPORT_SYMBOL_GPL(iscsi_create_session); ++ ++void iscsi_remove_session(struct iscsi_cls_session *session) ++{ ++ struct Scsi_Host *shost = iscsi_session_to_shost(session); ++ struct iscsi_host *ihost = shost->shost_data; ++ ++ if (!cancel_delayed_work(&session->recovery_work)) ++ flush_scheduled_work(); ++ ++ mutex_lock(&ihost->mutex); ++ list_del(&session->host_list); ++ mutex_unlock(&ihost->mutex); ++ ++ scsi_remove_target(&session->dev); ++ ++ transport_unregister_device(&session->dev); ++ device_del(&session->dev); ++} ++EXPORT_SYMBOL_GPL(iscsi_remove_session); ++ ++void iscsi_free_session(struct iscsi_cls_session *session) ++{ ++ put_device(&session->dev); ++} ++ ++EXPORT_SYMBOL_GPL(iscsi_free_session); ++ ++/** ++ * iscsi_destroy_session - destroy iscsi session ++ * @session: iscsi_session ++ * ++ * Can be called by a LLD or iscsi_transport. There must not be ++ * any running connections. ++ **/ ++int iscsi_destroy_session(struct iscsi_cls_session *session) ++{ ++ iscsi_remove_session(session); ++ iscsi_free_session(session); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_destroy_session); ++ ++static void iscsi_conn_release(struct device *dev) ++{ ++ struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); ++ struct device *parent = conn->dev.parent; ++ ++ kfree(conn); ++ put_device(parent); ++} ++ ++static int iscsi_is_conn_dev(const struct device *dev) ++{ ++ return dev->release == iscsi_conn_release; ++} ++ ++/** ++ * iscsi_create_conn - create iscsi class connection ++ * @session: iscsi cls session ++ * @cid: connection id ++ * ++ * This can be called from a LLD or iscsi_transport. The connection ++ * is child of the session so cid must be unique for all connections ++ * on the session. ++ * ++ * Since we do not support MCS, cid will normally be zero. In some cases ++ * for software iscsi we could be trying to preallocate a connection struct ++ * in which case there could be two connection structs and cid would be ++ * non-zero. ++ **/ ++struct iscsi_cls_conn * ++iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) ++{ ++ struct iscsi_transport *transport = session->transport; ++ struct iscsi_cls_conn *conn; ++ int err; ++ ++ conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL); ++ if (!conn) ++ return NULL; ++ ++ if (transport->conndata_size) ++ conn->dd_data = &conn[1]; ++ ++ INIT_LIST_HEAD(&conn->conn_list); ++ conn->transport = transport; ++ conn->cid = cid; ++ ++ /* this is released in the dev's release function */ ++ if (!get_device(&session->dev)) ++ goto free_conn; ++ ++ snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u", ++ session->sid, cid); ++ conn->dev.parent = &session->dev; ++ conn->dev.release = iscsi_conn_release; ++ err = device_register(&conn->dev); ++ if (err) { ++ dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register " ++ "connection's dev\n"); ++ goto release_parent_ref; ++ } ++ transport_register_device(&conn->dev); ++ return conn; ++ ++release_parent_ref: ++ put_device(&session->dev); ++free_conn: ++ kfree(conn); ++ return NULL; ++} ++ ++EXPORT_SYMBOL_GPL(iscsi_create_conn); ++ ++/** ++ * iscsi_destroy_conn - destroy iscsi class connection ++ * @session: iscsi cls session ++ * ++ * This can be called from a LLD or iscsi_transport. ++ **/ ++int iscsi_destroy_conn(struct iscsi_cls_conn *conn) ++{ ++ transport_unregister_device(&conn->dev); ++ device_unregister(&conn->dev); ++ return 0; ++} ++ ++EXPORT_SYMBOL_GPL(iscsi_destroy_conn); ++ ++/* ++ * iscsi interface functions ++ */ ++static struct iscsi_internal * ++iscsi_if_transport_lookup(struct iscsi_transport *tt) ++{ ++ struct iscsi_internal *priv; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&iscsi_transport_lock, flags); ++ list_for_each_entry(priv, &iscsi_transports, list) { ++ if (tt == priv->iscsi_transport) { ++ spin_unlock_irqrestore(&iscsi_transport_lock, flags); ++ return priv; ++ } ++ } ++ spin_unlock_irqrestore(&iscsi_transport_lock, flags); ++ return NULL; ++} ++ ++static int ++iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp) ++{ ++ int rc; ++ ++ rc = netlink_broadcast(nls, skb, 0, 1, gfp); ++ if (rc < 0) { ++ printk(KERN_ERR "iscsi: can not broadcast skb (%d)\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int ++iscsi_unicast_skb(struct sk_buff *skb, int pid) ++{ ++ int rc; ++ ++ rc = netlink_unicast(nls, skb, pid, MSG_DONTWAIT); ++ if (rc < 0) { ++ printk(KERN_ERR "iscsi: can not unicast skb (%d)\n", rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, ++ char *data, uint32_t data_size) ++{ ++ struct nlmsghdr *nlh; ++ struct sk_buff *skb; ++ struct iscsi_uevent *ev; ++ char *pdu; ++ struct iscsi_internal *priv; ++ int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) + ++ data_size); ++ ++ priv = iscsi_if_transport_lookup(conn->transport); ++ if (!priv) ++ return -EINVAL; ++ ++ skb = alloc_skb(len, GFP_ATOMIC); ++ if (!skb) { ++ iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED); ++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not deliver " ++ "control PDU: OOM\n"); ++ return -ENOMEM; ++ } ++ ++ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); ++ ev = NLMSG_DATA(nlh); ++ memset(ev, 0, sizeof(*ev)); ++ ev->transport_handle = iscsi_handle(conn->transport); ++ ev->type = ISCSI_KEVENT_RECV_PDU; ++ ev->r.recv_req.cid = conn->cid; ++ ev->r.recv_req.sid = iscsi_conn_get_sid(conn); ++ pdu = (char*)ev + sizeof(*ev); ++ memcpy(pdu, hdr, sizeof(struct iscsi_hdr)); ++ memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size); ++ ++ return iscsi_unicast_skb(skb, priv->daemon_pid); ++} ++EXPORT_SYMBOL_GPL(iscsi_recv_pdu); ++ ++void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) ++{ ++ struct nlmsghdr *nlh; ++ struct sk_buff *skb; ++ struct iscsi_uevent *ev; ++ struct iscsi_internal *priv; ++ int len = NLMSG_SPACE(sizeof(*ev)); ++ ++ priv = iscsi_if_transport_lookup(conn->transport); ++ if (!priv) ++ return; ++ ++ skb = alloc_skb(len, GFP_ATOMIC); ++ if (!skb) { ++ dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored " ++ "conn error (%d)\n", error); ++ return; ++ } ++ ++ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); ++ ev = NLMSG_DATA(nlh); ++ ev->transport_handle = iscsi_handle(conn->transport); ++ ev->type = ISCSI_KEVENT_CONN_ERROR; ++ ev->r.connerror.error = error; ++ ev->r.connerror.cid = conn->cid; ++ ev->r.connerror.sid = iscsi_conn_get_sid(conn); ++ ++ iscsi_broadcast_skb(skb, GFP_ATOMIC); ++ ++ dev_printk(KERN_INFO, &conn->dev, "iscsi: detected conn error (%d)\n", ++ error); ++} ++EXPORT_SYMBOL_GPL(iscsi_conn_error); ++ ++static int ++iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, ++ void *payload, int size) ++{ ++ struct sk_buff *skb; ++ struct nlmsghdr *nlh; ++ int len = NLMSG_SPACE(size); ++ int flags = multi ? NLM_F_MULTI : 0; ++ int t = done ? NLMSG_DONE : type; ++ ++ skb = alloc_skb(len, GFP_ATOMIC); ++ if (!skb) { ++ printk(KERN_ERR "Could not allocate skb to send reply.\n"); ++ return -ENOMEM; ++ } ++ ++ nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0); ++ nlh->nlmsg_flags = flags; ++ memcpy(NLMSG_DATA(nlh), payload, size); ++ return iscsi_unicast_skb(skb, pid); ++} ++ ++static int ++iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) ++{ ++ struct iscsi_uevent *ev = NLMSG_DATA(nlh); ++ struct iscsi_stats *stats; ++ struct sk_buff *skbstat; ++ struct iscsi_cls_conn *conn; ++ struct nlmsghdr *nlhstat; ++ struct iscsi_uevent *evstat; ++ struct iscsi_internal *priv; ++ int len = NLMSG_SPACE(sizeof(*ev) + ++ sizeof(struct iscsi_stats) + ++ sizeof(struct iscsi_stats_custom) * ++ ISCSI_STATS_CUSTOM_MAX); ++ int err = 0; ++ ++ priv = iscsi_if_transport_lookup(transport); ++ if (!priv) ++ return -EINVAL; ++ ++ conn = iscsi_conn_lookup(ev->u.get_stats.sid, ev->u.get_stats.cid); ++ if (!conn) ++ return -EEXIST; ++ ++ do { ++ int actual_size; ++ ++ skbstat = alloc_skb(len, GFP_ATOMIC); ++ if (!skbstat) { ++ dev_printk(KERN_ERR, &conn->dev, "iscsi: can not " ++ "deliver stats: OOM\n"); ++ return -ENOMEM; ++ } ++ ++ nlhstat = __nlmsg_put(skbstat, priv->daemon_pid, 0, 0, ++ (len - sizeof(*nlhstat)), 0); ++ evstat = NLMSG_DATA(nlhstat); ++ memset(evstat, 0, sizeof(*evstat)); ++ evstat->transport_handle = iscsi_handle(conn->transport); ++ evstat->type = nlh->nlmsg_type; ++ evstat->u.get_stats.cid = ++ ev->u.get_stats.cid; ++ evstat->u.get_stats.sid = ++ ev->u.get_stats.sid; ++ stats = (struct iscsi_stats *) ++ ((char*)evstat + sizeof(*evstat)); ++ memset(stats, 0, sizeof(*stats)); ++ ++ transport->get_stats(conn, stats); ++ actual_size = NLMSG_SPACE(sizeof(struct iscsi_uevent) + ++ sizeof(struct iscsi_stats) + ++ sizeof(struct iscsi_stats_custom) * ++ stats->custom_length); ++ actual_size -= sizeof(*nlhstat); ++ actual_size = NLMSG_LENGTH(actual_size); ++ skb_trim(skbstat, NLMSG_ALIGN(actual_size)); ++ nlhstat->nlmsg_len = actual_size; ++ ++ err = iscsi_unicast_skb(skbstat, priv->daemon_pid); ++ } while (err < 0 && err != -ECONNREFUSED); ++ ++ return err; ++} ++ ++/** ++ * iscsi_if_destroy_session_done - send session destr. completion event ++ * @conn: last connection for session ++ * ++ * This is called by HW iscsi LLDs to notify userpsace that its HW has ++ * removed a session. ++ **/ ++int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn) ++{ ++ struct iscsi_internal *priv; ++ struct iscsi_cls_session *session; ++ struct Scsi_Host *shost; ++ struct iscsi_uevent *ev; ++ struct sk_buff *skb; ++ struct nlmsghdr *nlh; ++ unsigned long flags; ++ int rc, len = NLMSG_SPACE(sizeof(*ev)); ++ ++ priv = iscsi_if_transport_lookup(conn->transport); ++ if (!priv) ++ return -EINVAL; ++ ++ session = iscsi_dev_to_session(conn->dev.parent); ++ shost = iscsi_session_to_shost(session); ++ ++ skb = alloc_skb(len, GFP_KERNEL); ++ if (!skb) { ++ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " ++ "session creation event\n"); ++ return -ENOMEM; ++ } ++ ++ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); ++ ev = NLMSG_DATA(nlh); ++ ev->transport_handle = iscsi_handle(conn->transport); ++ ev->type = ISCSI_KEVENT_DESTROY_SESSION; ++ ev->r.d_session.host_no = shost->host_no; ++ ev->r.d_session.sid = session->sid; ++ ++ /* ++ * this will occur if the daemon is not up, so we just warn ++ * the user and when the daemon is restarted it will handle it ++ */ ++ rc = iscsi_broadcast_skb(skb, GFP_KERNEL); ++ if (rc < 0) ++ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " ++ "session destruction event. Check iscsi daemon\n"); ++ ++ spin_lock_irqsave(&sesslock, flags); ++ list_del(&session->sess_list); ++ spin_unlock_irqrestore(&sesslock, flags); ++ ++ spin_lock_irqsave(&connlock, flags); ++ conn->active = 0; ++ list_del(&conn->conn_list); ++ spin_unlock_irqrestore(&connlock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(iscsi_if_destroy_session_done); ++ ++/** ++ * iscsi_if_create_session_done - send session creation completion event ++ * @conn: leading connection for session ++ * ++ * This is called by HW iscsi LLDs to notify userpsace that its HW has ++ * created a session or a existing session is back in the logged in state. ++ **/ ++int iscsi_if_create_session_done(struct iscsi_cls_conn *conn) ++{ ++ struct iscsi_internal *priv; ++ struct iscsi_cls_session *session; ++ struct Scsi_Host *shost; ++ struct iscsi_uevent *ev; ++ struct sk_buff *skb; ++ struct nlmsghdr *nlh; ++ unsigned long flags; ++ int rc, len = NLMSG_SPACE(sizeof(*ev)); ++ ++ priv = iscsi_if_transport_lookup(conn->transport); ++ if (!priv) ++ return -EINVAL; ++ ++ session = iscsi_dev_to_session(conn->dev.parent); ++ shost = iscsi_session_to_shost(session); ++ ++ skb = alloc_skb(len, GFP_KERNEL); ++ if (!skb) { ++ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " ++ "session creation event\n"); ++ return -ENOMEM; ++ } ++ ++ nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); ++ ev = NLMSG_DATA(nlh); ++ ev->transport_handle = iscsi_handle(conn->transport); ++ ev->type = ISCSI_UEVENT_CREATE_SESSION; ++ ev->r.c_session_ret.host_no = shost->host_no; ++ ev->r.c_session_ret.sid = session->sid; ++ ++ /* ++ * this will occur if the daemon is not up, so we just warn ++ * the user and when the daemon is restarted it will handle it ++ */ ++ rc = iscsi_broadcast_skb(skb, GFP_KERNEL); ++ if (rc < 0) ++ dev_printk(KERN_ERR, &conn->dev, "Cannot notify userspace of " ++ "session creation event. Check iscsi daemon\n"); ++ ++ spin_lock_irqsave(&sesslock, flags); ++ list_add(&session->sess_list, &sesslist); ++ spin_unlock_irqrestore(&sesslock, flags); ++ ++ spin_lock_irqsave(&connlock, flags); ++ list_add(&conn->conn_list, &connlist); ++ conn->active = 1; ++ spin_unlock_irqrestore(&connlock, flags); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(iscsi_if_create_session_done); ++ ++static int ++iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) ++{ ++ struct iscsi_transport *transport = priv->iscsi_transport; ++ struct iscsi_cls_session *session; ++ unsigned long flags; ++ uint32_t hostno; ++ ++ session = transport->create_session(transport, &priv->t, ++ ev->u.c_session.initial_cmdsn, ++ &hostno); ++ if (!session) ++ return -ENOMEM; ++ ++ spin_lock_irqsave(&sesslock, flags); ++ list_add(&session->sess_list, &sesslist); ++ spin_unlock_irqrestore(&sesslock, flags); ++ ++ ev->r.c_session_ret.host_no = hostno; ++ ev->r.c_session_ret.sid = session->sid; ++ return 0; ++} ++ ++static int ++iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) ++{ ++ struct iscsi_cls_conn *conn; ++ struct iscsi_cls_session *session; ++ unsigned long flags; ++ ++ session = iscsi_session_lookup(ev->u.c_conn.sid); ++ if (!session) { ++ printk(KERN_ERR "iscsi: invalid session %d\n", ++ ev->u.c_conn.sid); ++ return -EINVAL; ++ } ++ ++ conn = transport->create_conn(session, ev->u.c_conn.cid); ++ if (!conn) { ++ printk(KERN_ERR "iscsi: couldn't create a new " ++ "connection for session %d\n", ++ session->sid); ++ return -ENOMEM; ++ } ++ ++ ev->r.c_conn_ret.sid = session->sid; ++ ev->r.c_conn_ret.cid = conn->cid; ++ ++ spin_lock_irqsave(&connlock, flags); ++ list_add(&conn->conn_list, &connlist); ++ conn->active = 1; ++ spin_unlock_irqrestore(&connlock, flags); ++ ++ return 0; ++} ++ ++static int ++iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) ++{ ++ unsigned long flags; ++ struct iscsi_cls_conn *conn; ++ ++ conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); ++ if (!conn) ++ return -EINVAL; ++ spin_lock_irqsave(&connlock, flags); ++ conn->active = 0; ++ list_del(&conn->conn_list); ++ spin_unlock_irqrestore(&connlock, flags); ++ ++ if (transport->destroy_conn) ++ transport->destroy_conn(conn); ++ return 0; ++} ++ ++static int ++iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) ++{ ++ char *data = (char*)ev + sizeof(*ev); ++ struct iscsi_cls_conn *conn; ++ struct iscsi_cls_session *session; ++ int err = 0, value = 0; ++ ++ session = iscsi_session_lookup(ev->u.set_param.sid); ++ conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid); ++ if (!conn || !session) ++ return -EINVAL; ++ ++ switch (ev->u.set_param.param) { ++ case ISCSI_PARAM_SESS_RECOVERY_TMO: ++ sscanf(data, "%d", &value); ++ if (value != 0) ++ session->recovery_tmo = value; ++ break; ++ default: ++ err = transport->set_param(conn, ev->u.set_param.param, ++ data, ev->u.set_param.len); ++ } ++ ++ return err; ++} ++ ++static int ++iscsi_if_transport_ep(struct iscsi_transport *transport, ++ struct iscsi_uevent *ev, int msg_type) ++{ ++ struct sockaddr *dst_addr; ++ int rc = 0; ++ ++ switch (msg_type) { ++ case ISCSI_UEVENT_TRANSPORT_EP_CONNECT: ++ if (!transport->ep_connect) ++ return -EINVAL; ++ ++ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev)); ++ rc = transport->ep_connect(dst_addr, ++ ev->u.ep_connect.non_blocking, ++ &ev->r.ep_connect_ret.handle); ++ break; ++ case ISCSI_UEVENT_TRANSPORT_EP_POLL: ++ if (!transport->ep_poll) ++ return -EINVAL; ++ ++ ev->r.retcode = transport->ep_poll(ev->u.ep_poll.ep_handle, ++ ev->u.ep_poll.timeout_ms); ++ break; ++ case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: ++ if (!transport->ep_disconnect) ++ return -EINVAL; ++ ++ transport->ep_disconnect(ev->u.ep_disconnect.ep_handle); ++ break; ++ } ++ return rc; ++} ++ ++static int ++iscsi_tgt_dscvr(struct iscsi_transport *transport, ++ struct iscsi_uevent *ev) ++{ ++ struct sockaddr *dst_addr; ++ ++ if (!transport->tgt_dscvr) ++ return -EINVAL; ++ ++ dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev)); ++ return transport->tgt_dscvr(ev->u.tgt_dscvr.type, ++ ev->u.tgt_dscvr.host_no, ++ ev->u.tgt_dscvr.enable, dst_addr); ++} ++ ++static int ++iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ++{ ++ int err = 0; ++ struct iscsi_uevent *ev = NLMSG_DATA(nlh); ++ struct iscsi_transport *transport = NULL; ++ struct iscsi_internal *priv; ++ struct iscsi_cls_session *session; ++ struct iscsi_cls_conn *conn; ++ unsigned long flags; ++ ++ priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); ++ if (!priv) ++ return -EINVAL; ++ transport = priv->iscsi_transport; ++ ++ if (!try_module_get(transport->owner)) ++ return -EINVAL; ++ ++ priv->daemon_pid = NETLINK_CREDS(skb)->pid; ++ ++ switch (nlh->nlmsg_type) { ++ case ISCSI_UEVENT_CREATE_SESSION: ++ err = iscsi_if_create_session(priv, ev); ++ break; ++ case ISCSI_UEVENT_DESTROY_SESSION: ++ session = iscsi_session_lookup(ev->u.d_session.sid); ++ if (session) { ++ spin_lock_irqsave(&sesslock, flags); ++ list_del(&session->sess_list); ++ spin_unlock_irqrestore(&sesslock, flags); ++ ++ transport->destroy_session(session); ++ } else ++ err = -EINVAL; ++ break; ++ case ISCSI_UEVENT_CREATE_CONN: ++ err = iscsi_if_create_conn(transport, ev); ++ break; ++ case ISCSI_UEVENT_DESTROY_CONN: ++ err = iscsi_if_destroy_conn(transport, ev); ++ break; ++ case ISCSI_UEVENT_BIND_CONN: ++ session = iscsi_session_lookup(ev->u.b_conn.sid); ++ conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); ++ ++ if (session && conn) ++ ev->r.retcode = transport->bind_conn(session, conn, ++ ev->u.b_conn.transport_eph, ++ ev->u.b_conn.is_leading); ++ else ++ err = -EINVAL; ++ break; ++ case ISCSI_UEVENT_SET_PARAM: ++ err = iscsi_set_param(transport, ev); ++ break; ++ case ISCSI_UEVENT_START_CONN: ++ conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid); ++ if (conn) ++ ev->r.retcode = transport->start_conn(conn); ++ else ++ err = -EINVAL; ++ break; ++ case ISCSI_UEVENT_STOP_CONN: ++ conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); ++ if (conn) ++ transport->stop_conn(conn, ev->u.stop_conn.flag); ++ else ++ err = -EINVAL; ++ break; ++ case ISCSI_UEVENT_SEND_PDU: ++ conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); ++ if (conn) ++ ev->r.retcode = transport->send_pdu(conn, ++ (struct iscsi_hdr*)((char*)ev + sizeof(*ev)), ++ (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size, ++ ev->u.send_pdu.data_size); ++ else ++ err = -EINVAL; ++ break; ++ case ISCSI_UEVENT_GET_STATS: ++ err = iscsi_if_get_stats(transport, nlh); ++ break; ++ case ISCSI_UEVENT_TRANSPORT_EP_CONNECT: ++ case ISCSI_UEVENT_TRANSPORT_EP_POLL: ++ case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: ++ err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type); ++ break; ++ case ISCSI_UEVENT_TGT_DSCVR: ++ err = iscsi_tgt_dscvr(transport, ev); ++ break; ++ default: ++ err = -EINVAL; ++ break; ++ } ++ ++ module_put(transport->owner); ++ return err; ++} ++ ++/* ++ * Get message from skb (based on rtnetlink_rcv_skb). Each message is ++ * processed by iscsi_if_recv_msg. Malformed skbs with wrong lengths or ++ * invalid creds are discarded silently. ++ */ ++static void ++iscsi_if_rx(struct sock *sk, int len) ++{ ++ struct sk_buff *skb; ++ ++ mutex_lock(&rx_queue_mutex); ++ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { ++ if (NETLINK_CREDS(skb)->uid) { ++ skb_pull(skb, skb->len); ++ goto free_skb; ++ } ++ ++ while (skb->len >= NLMSG_SPACE(0)) { ++ int err; ++ uint32_t rlen; ++ struct nlmsghdr *nlh; ++ struct iscsi_uevent *ev; ++ ++ nlh = (struct nlmsghdr *)skb->data; ++ if (nlh->nlmsg_len < sizeof(*nlh) || ++ skb->len < nlh->nlmsg_len) { ++ break; ++ } ++ ++ ev = NLMSG_DATA(nlh); ++ rlen = NLMSG_ALIGN(nlh->nlmsg_len); ++ if (rlen > skb->len) ++ rlen = skb->len; ++ ++ err = iscsi_if_recv_msg(skb, nlh); ++ if (err) { ++ ev->type = ISCSI_KEVENT_IF_ERROR; ++ ev->iferror = err; ++ } ++ do { ++ /* ++ * special case for GET_STATS: ++ * on success - sending reply and stats from ++ * inside of if_recv_msg(), ++ * on error - fall through. ++ */ ++ if (ev->type == ISCSI_UEVENT_GET_STATS && !err) ++ break; ++ err = iscsi_if_send_reply( ++ NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, ++ nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); ++ } while (err < 0 && err != -ECONNREFUSED); ++ skb_pull(skb, rlen); ++ } ++free_skb: ++ kfree_skb(skb); ++ } ++ mutex_unlock(&rx_queue_mutex); ++} ++ ++#define iscsi_cdev_to_conn(_cdev) \ ++ iscsi_dev_to_conn(_cdev->dev) ++ ++#define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store) \ ++struct class_device_attribute class_device_attr_##_prefix##_##_name = \ ++ __ATTR(_name,_mode,_show,_store) ++ ++/* ++ * iSCSI connection attrs ++ */ ++#define iscsi_conn_attr_show(param) \ ++static ssize_t \ ++show_conn_param_##param(struct class_device *cdev, char *buf) \ ++{ \ ++ struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev); \ ++ struct iscsi_transport *t = conn->transport; \ ++ return t->get_conn_param(conn, param, buf); \ ++} ++ ++#define iscsi_conn_attr(field, param) \ ++ iscsi_conn_attr_show(param) \ ++static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param, \ ++ NULL); ++ ++iscsi_conn_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH); ++iscsi_conn_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH); ++iscsi_conn_attr(header_digest, ISCSI_PARAM_HDRDGST_EN); ++iscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN); ++iscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN); ++iscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN); ++iscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT); ++iscsi_conn_attr(port, ISCSI_PARAM_CONN_PORT); ++iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN); ++iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS); ++iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS); ++ ++#define iscsi_cdev_to_session(_cdev) \ ++ iscsi_dev_to_session(_cdev->dev) ++ ++/* ++ * iSCSI session attrs ++ */ ++#define iscsi_session_attr_show(param) \ ++static ssize_t \ ++show_session_param_##param(struct class_device *cdev, char *buf) \ ++{ \ ++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \ ++ struct iscsi_transport *t = session->transport; \ ++ return t->get_session_param(session, param, buf); \ ++} ++ ++#define iscsi_session_attr(field, param) \ ++ iscsi_session_attr_show(param) \ ++static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \ ++ NULL); ++ ++iscsi_session_attr(targetname, ISCSI_PARAM_TARGET_NAME); ++iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN); ++iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T); ++iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN); ++iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST); ++iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST); ++iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN); ++iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN); ++iscsi_session_attr(erl, ISCSI_PARAM_ERL); ++iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT); ++ ++#define iscsi_priv_session_attr_show(field, format) \ ++static ssize_t \ ++show_priv_session_##field(struct class_device *cdev, char *buf) \ ++{ \ ++ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\ ++ return sprintf(buf, format"\n", session->field); \ ++} ++ ++#define iscsi_priv_session_attr(field, format) \ ++ iscsi_priv_session_attr_show(field, format) \ ++static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO, show_priv_session_##field, \ ++ NULL) ++iscsi_priv_session_attr(recovery_tmo, "%d"); ++ ++#define SETUP_PRIV_SESSION_RD_ATTR(field) \ ++do { \ ++ priv->session_attrs[count] = &class_device_attr_priv_sess_##field; \ ++ count++; \ ++} while (0) ++ ++ ++#define SETUP_SESSION_RD_ATTR(field, param_flag) \ ++do { \ ++ if (tt->param_mask & param_flag) { \ ++ priv->session_attrs[count] = &class_device_attr_sess_##field; \ ++ count++; \ ++ } \ ++} while (0) ++ ++#define SETUP_CONN_RD_ATTR(field, param_flag) \ ++do { \ ++ if (tt->param_mask & param_flag) { \ ++ priv->conn_attrs[count] = &class_device_attr_conn_##field; \ ++ count++; \ ++ } \ ++} while (0) ++ ++static int iscsi_session_match(struct attribute_container *cont, ++ struct device *dev) ++{ ++ struct iscsi_cls_session *session; ++ struct Scsi_Host *shost; ++ struct iscsi_internal *priv; ++ ++ if (!iscsi_is_session_dev(dev)) ++ return 0; ++ ++ session = iscsi_dev_to_session(dev); ++ shost = iscsi_session_to_shost(session); ++ if (!shost->transportt) ++ return 0; ++ ++ priv = to_iscsi_internal(shost->transportt); ++ if (priv->session_cont.ac.class != &iscsi_session_class.class) ++ return 0; ++ ++ return &priv->session_cont.ac == cont; ++} ++ ++static int iscsi_conn_match(struct attribute_container *cont, ++ struct device *dev) ++{ ++ struct iscsi_cls_session *session; ++ struct iscsi_cls_conn *conn; ++ struct Scsi_Host *shost; ++ struct iscsi_internal *priv; ++ ++ if (!iscsi_is_conn_dev(dev)) ++ return 0; ++ ++ conn = iscsi_dev_to_conn(dev); ++ session = iscsi_dev_to_session(conn->dev.parent); ++ shost = iscsi_session_to_shost(session); ++ ++ if (!shost->transportt) ++ return 0; ++ ++ priv = to_iscsi_internal(shost->transportt); ++ if (priv->conn_cont.ac.class != &iscsi_connection_class.class) ++ return 0; ++ ++ return &priv->conn_cont.ac == cont; ++} ++ ++static int iscsi_host_match(struct attribute_container *cont, ++ struct device *dev) ++{ ++ struct Scsi_Host *shost; ++ struct iscsi_internal *priv; ++ ++ if (!scsi_is_host_device(dev)) ++ return 0; ++ ++ shost = dev_to_shost(dev); ++ if (!shost->transportt || ++ shost->transportt->host_attrs.ac.class != &iscsi_host_class.class) ++ return 0; ++ ++ priv = to_iscsi_internal(shost->transportt); ++ return &priv->t.host_attrs.ac == cont; ++} ++ ++struct scsi_transport_template * ++iscsi_register_transport(struct iscsi_transport *tt) ++{ ++ struct iscsi_internal *priv; ++ unsigned long flags; ++ int count = 0, err; ++ ++ BUG_ON(!tt); ++ ++ priv = iscsi_if_transport_lookup(tt); ++ if (priv) ++ return NULL; ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return NULL; ++ INIT_LIST_HEAD(&priv->list); ++ priv->daemon_pid = -1; ++ priv->iscsi_transport = tt; ++ priv->t.user_scan = iscsi_user_scan; ++ ++ priv->cdev.class = &iscsi_transport_class; ++ snprintf(priv->cdev.class_id, BUS_ID_SIZE, "%s", tt->name); ++ err = class_device_register(&priv->cdev); ++ if (err) ++ goto free_priv; ++ ++ err = sysfs_create_group(&priv->cdev.kobj, &iscsi_transport_group); ++ if (err) ++ goto unregister_cdev; ++ ++ /* host parameters */ ++ priv->t.host_attrs.ac.attrs = &priv->host_attrs[0]; ++ priv->t.host_attrs.ac.class = &iscsi_host_class.class; ++ priv->t.host_attrs.ac.match = iscsi_host_match; ++ priv->t.host_size = sizeof(struct iscsi_host); ++ priv->host_attrs[0] = NULL; ++ transport_container_register(&priv->t.host_attrs); ++ ++ /* connection parameters */ ++ priv->conn_cont.ac.attrs = &priv->conn_attrs[0]; ++ priv->conn_cont.ac.class = &iscsi_connection_class.class; ++ priv->conn_cont.ac.match = iscsi_conn_match; ++ transport_container_register(&priv->conn_cont); ++ ++ SETUP_CONN_RD_ATTR(max_recv_dlength, ISCSI_MAX_RECV_DLENGTH); ++ SETUP_CONN_RD_ATTR(max_xmit_dlength, ISCSI_MAX_XMIT_DLENGTH); ++ SETUP_CONN_RD_ATTR(header_digest, ISCSI_HDRDGST_EN); ++ SETUP_CONN_RD_ATTR(data_digest, ISCSI_DATADGST_EN); ++ SETUP_CONN_RD_ATTR(ifmarker, ISCSI_IFMARKER_EN); ++ SETUP_CONN_RD_ATTR(ofmarker, ISCSI_OFMARKER_EN); ++ SETUP_CONN_RD_ATTR(address, ISCSI_CONN_ADDRESS); ++ SETUP_CONN_RD_ATTR(port, ISCSI_CONN_PORT); ++ SETUP_CONN_RD_ATTR(exp_statsn, ISCSI_EXP_STATSN); ++ SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS); ++ SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT); ++ ++ BUG_ON(count > ISCSI_CONN_ATTRS); ++ priv->conn_attrs[count] = NULL; ++ count = 0; ++ ++ /* session parameters */ ++ priv->session_cont.ac.attrs = &priv->session_attrs[0]; ++ priv->session_cont.ac.class = &iscsi_session_class.class; ++ priv->session_cont.ac.match = iscsi_session_match; ++ transport_container_register(&priv->session_cont); ++ ++ SETUP_SESSION_RD_ATTR(initial_r2t, ISCSI_INITIAL_R2T_EN); ++ SETUP_SESSION_RD_ATTR(max_outstanding_r2t, ISCSI_MAX_R2T); ++ SETUP_SESSION_RD_ATTR(immediate_data, ISCSI_IMM_DATA_EN); ++ SETUP_SESSION_RD_ATTR(first_burst_len, ISCSI_FIRST_BURST); ++ SETUP_SESSION_RD_ATTR(max_burst_len, ISCSI_MAX_BURST); ++ SETUP_SESSION_RD_ATTR(data_pdu_in_order, ISCSI_PDU_INORDER_EN); ++ SETUP_SESSION_RD_ATTR(data_seq_in_order, ISCSI_DATASEQ_INORDER_EN); ++ SETUP_SESSION_RD_ATTR(erl, ISCSI_ERL); ++ SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME); ++ SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT); ++ SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo); ++ ++ BUG_ON(count > ISCSI_SESSION_ATTRS); ++ priv->session_attrs[count] = NULL; ++ ++ spin_lock_irqsave(&iscsi_transport_lock, flags); ++ list_add(&priv->list, &iscsi_transports); ++ spin_unlock_irqrestore(&iscsi_transport_lock, flags); ++ ++ printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name); ++ return &priv->t; ++ ++unregister_cdev: ++ class_device_unregister(&priv->cdev); ++free_priv: ++ kfree(priv); ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(iscsi_register_transport); ++ ++int iscsi_unregister_transport(struct iscsi_transport *tt) ++{ ++ struct iscsi_internal *priv; ++ unsigned long flags; ++ ++ BUG_ON(!tt); ++ ++ mutex_lock(&rx_queue_mutex); ++ ++ priv = iscsi_if_transport_lookup(tt); ++ BUG_ON (!priv); ++ ++ spin_lock_irqsave(&iscsi_transport_lock, flags); ++ list_del(&priv->list); ++ spin_unlock_irqrestore(&iscsi_transport_lock, flags); ++ ++ transport_container_unregister(&priv->conn_cont); ++ transport_container_unregister(&priv->session_cont); ++ transport_container_unregister(&priv->t.host_attrs); ++ ++ sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group); ++ class_device_unregister(&priv->cdev); ++ mutex_unlock(&rx_queue_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(iscsi_unregister_transport); ++ ++static __init int iscsi_transport_init(void) ++{ ++ int err; ++ ++ printk(KERN_INFO "Loading iSCSI transport class v%s.\n", ++ ISCSI_TRANSPORT_VERSION); ++ ++ atomic_set(&iscsi_session_nr, 0); ++ ++ err = class_register(&iscsi_transport_class); ++ if (err) ++ return err; ++ ++ err = transport_class_register(&iscsi_host_class); ++ if (err) ++ goto unregister_transport_class; ++ ++ err = transport_class_register(&iscsi_connection_class); ++ if (err) ++ goto unregister_host_class; ++ ++ err = transport_class_register(&iscsi_session_class); ++ if (err) ++ goto unregister_conn_class; ++ ++ nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, ++ THIS_MODULE); ++ if (!nls) { ++ err = -ENOBUFS; ++ goto unregister_session_class; ++ } ++ ++ return 0; ++ ++unregister_session_class: ++ transport_class_unregister(&iscsi_session_class); ++unregister_conn_class: ++ transport_class_unregister(&iscsi_connection_class); ++unregister_host_class: ++ transport_class_unregister(&iscsi_host_class); ++unregister_transport_class: ++ class_unregister(&iscsi_transport_class); ++ return err; ++} ++ ++static void __exit iscsi_transport_exit(void) ++{ ++ sock_release(nls->sk_socket); ++ transport_class_unregister(&iscsi_connection_class); ++ transport_class_unregister(&iscsi_session_class); ++ transport_class_unregister(&iscsi_host_class); ++ class_unregister(&iscsi_transport_class); ++} ++ ++module_init(iscsi_transport_init); ++module_exit(iscsi_transport_exit); ++ ++MODULE_AUTHOR("Mike Christie , " ++ "Dmitry Yusupov , " ++ "Alex Aizman "); ++MODULE_DESCRIPTION("iSCSI Transport Interface"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(ISCSI_TRANSPORT_VERSION); +Index: kernel/Makefile +=================================================================== +--- kernel/Makefile (revision 754) ++++ kernel/Makefile (revision 779) +@@ -3,7 +3,7 @@ + # + # This Makefile invokes KBuild from outside the kernel directory when + # used from the main open-iscsi package. It also contains all of the +-# KBuild stuff needed to build the the modules. ++# KBuild stuff needed to build the modules. + # + + # Kbuild stuff, the following is the only part of this file that KBuild +@@ -36,84 +36,119 @@ + # this is the basic Kbuild invocation, just append your make target + KBUILD_BASE = +$(MAKE) -C $(KSRC) M=`pwd` KBUILD_OUTPUT=$(KBUILD_OUTPUT) $(KARCH) V=$(V) + +-# fun stuff for maintaining multiple versions ++all: kernel_check ++ $(KBUILD_BASE) modules ++ ++# ============ BEGIN code for kernel_check and source patching ================ ++# We calculate the Linux version for compilation. Than according to version, ++# if needed, we patch source-code to match the compiling kernel. ++# if you need a new kernel sub-version just add a target below. ++# ++# IMPORTANT: do "make clean" before submitting to SVN so source is in unpatched ++# form. ++ ++#some constants ++13_patch=2.6.13_compat.patch ++14to15_patch=2.6.14-and-2.6.15-compat.patch ++16to18_patch=2.6.16-18_compat.patch ++19_patch=2.6.19_compat.patch ++all_patches=13_patch 14to15_patch 16to18_patch 19_patch ++cur_patched=cur_patched ++ ++## fun stuff for maintaining multiple versions ++ ++# check to see if code is unpatched ++unpatch_code=$(shell test -e $(cur_patched) && echo do_unpatch_code ) ++ + KSUBLEVEL = $(shell cat $(KSRC)/Makefile | awk -F= '/^SUBLEVEL =/ {print $$2}' | \ + sed 's/^[ \t]*//;s/[ \t]*$$//') + +-ifeq ($(KSUBLEVEL), 11) +-EXTRA_CFLAGS += -DNETLINK_ISCSI=12 +-else +-ifeq ($(KSUBLEVEL), 12) +-EXTRA_CFLAGS += -DNETLINK_ISCSI=12 +-endif +-endif ++KERNEL_TARGET=linux_2_6_$(KSUBLEVEL) ++kernel_check: $(KERNEL_TARGET) + +-all: kernel_check +- $(KBUILD_BASE) modules + +-# ====== BEGIN code for kernel_check ++linux_2_6_13: has_13_patch + ++linux_2_6_14: has_14to15_patch + +-# generic make function +-# syntax: +-# @$(call generic_check_command, CHECKING-COMMAND, PATCHNAME) +-generic_check_command = if $(1) ; then \ +- echo "kernel check... FAILED"; \ +- echo "Apply the $(2) first!"; exit 1; fi ; \ +- echo "kernel check... OK" ++linux_2_6_15: has_14to15_patch + +-# this variable defines how we check for iscsi_compat +-check_iscsi_compat_command = ! grep iscsi_compat scsi_transport_iscsi.c >/dev/null +-# this variable is the actual make function +-check_iscsi_compat = $(call generic_check_command, $(check_iscsi_compat_command), $(1)) +-# syntax: +-# @$(call check_iscsi_compat, PATCHNAME) ++linux_2_6_16: has_16to18_patch + +-# this if-else ladder is horrible +-# but I don't know any quick way to clean it up +-# since Make doesn't support a switch construct +-.NOTPARALLEL: kernel_check +-kernel_check: +-ifeq ($(KSUBLEVEL),16) +- echo "kernel check... OK" +- cp 2.6.16-2.6.19/*.c . +- cp 2.6.16-2.6.19/*.h . +-else +-ifeq ($(KSUBLEVEL),17) +- echo "kernel check... OK" +- cp 2.6.16-2.6.19/*.c . +- cp 2.6.16-2.6.19/*.h . +-else +-ifeq ($(KSUBLEVEL),18) +- echo "kernel check... OK" +- cp 2.6.16-2.6.19/*.c . +- cp 2.6.16-2.6.19/*.h . +-else +-ifeq ($(KSUBLEVEL),19) +- echo "kernel check... OK" +- cp 2.6.16-2.6.19/*.c . +- cp 2.6.16-2.6.19/*.h . +-else +-ifeq ($(KSUBLEVEL),20) +- echo "kernel check... OK" +- cp 2.6.20/*.c . +- cp 2.6.20/*.h . +-else +- @echo "kernel check... UNSUPPORTED KERNEL DETECTED" +- exit 1; +-endif +-endif +-endif +-endif +-endif ++linux_2_6_17: has_16to18_patch + +-# ====== END code for kernel_check ++linux_2_6_18: has_16to18_patch + +-clean: ++linux_2_6_19: has_19_patch ++ ++linux_2_6_20: $(unpatch_code) ++ ++linux_2_6_21: $(unpatch_code) ++ ++do_unpatch_code: ++ echo "Un-patching source code for use with linux-2.6.20 and up ..." ++ patch -R -E -p1 < $(cur_patched) ++ rm -f `readlink $(cur_patched)` ++ rm -f $(cur_patched) ++ ++# these below targets must be the same as the variable name prefixed by has_ ++# otherwise below compat_patch: target will not work ++has_13_patch: $(13_patch) ++ echo "Patching source code for linux-2.6.13 ..." ++ if [ -e $(cur_patched) ]; then \ ++ make -C . clean; \ ++ fi ++ patch -p1 < $(13_patch) ++ cp $(13_patch) $@ ++ ln -s $@ $(cur_patched) ++ ++has_14to15_patch: $(14to15_patch) ++ echo "Patching source code for linux-2.6.14-15 ..." ++ if [ -e $(cur_patched) ]; then \ ++ make -C . clean; \ ++ fi ++ patch -p1 < $(14to15_patch) ++ cp $(14to15_patch) $@ ++ ln -s $@ $(cur_patched) ++ ++has_16to18_patch: $(16to18_patch) ++ echo "Patching source code for linux-2.6.16-18 ..." ++ if [ -e $(cur_patched) ]; then \ ++ make -C . clean; \ ++ fi ++ patch -p1 < $(16to18_patch) ++ cp $(16to18_patch) $@ ++ ln -s $@ $(cur_patched) ++ ++has_19_patch: $(19_patch) ++ echo "Patching source code for linux-2.6.19 ..." ++ if [ -e $(cur_patched) ]; then \ ++ make -C . clean; \ ++ fi ++ patch -p1 < $(19_patch) ++ cp $(19_patch) $@ ++ ln -s $@ $(cur_patched) ++ ++# ============ END code for kernel_check and source patching ================= ++ ++clean: $(unpatch_code) + $(KBUILD_BASE) clean +- rm *.c *.h Module.symvers +-#echo rm -f -r *.mod.c .*cmd *.o *.ko .tmp_versions ++ rm -f Module.symvers + ++## The folowing compat_patch target is what we need to do to prepare a clean ++# compat_patch set after new code is check-in to svn. To keep patches fuzzless. ++# the new patches are writen into .new files so svn diff of next file will ++# not trip on them. ++compat_patch: $(unpatch_code) ++ test -z "$(svn diff|head)" || { \ ++ echo "please run make compat_patch after changse are submited to svn"; \ ++ exit 1; \ ++ } ++ for the_patch in all_patches ; do \ ++ make -C . has_$(the_patch); \ ++ svn diff > ${!the_patch}.new; \ ++ done ++ + # the following is only for convienience + # do not submit to Linus + # it's also called from the toplevel makefile +Index: kernel/scsi_transport_iscsi.h +=================================================================== +--- kernel/scsi_transport_iscsi.h (revision 0) ++++ kernel/scsi_transport_iscsi.h (revision 779) +@@ -0,0 +1,224 @@ ++/* ++ * iSCSI transport class definitions ++ * ++ * Copyright (C) IBM Corporation, 2004 ++ * Copyright (C) Mike Christie, 2004 - 2006 ++ * Copyright (C) Dmitry Yusupov, 2004 - 2005 ++ * Copyright (C) Alex Aizman, 2004 - 2005 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ */ ++#ifndef SCSI_TRANSPORT_ISCSI_H ++#define SCSI_TRANSPORT_ISCSI_H ++ ++#include ++#include "iscsi_if.h" ++ ++struct scsi_transport_template; ++struct iscsi_transport; ++struct Scsi_Host; ++struct iscsi_cls_conn; ++struct iscsi_conn; ++struct iscsi_cmd_task; ++struct iscsi_mgmt_task; ++struct sockaddr; ++ ++/** ++ * struct iscsi_transport - iSCSI Transport template ++ * ++ * @name: transport name ++ * @caps: iSCSI Data-Path capabilities ++ * @create_session: create new iSCSI session object ++ * @destroy_session: destroy existing iSCSI session object ++ * @create_conn: create new iSCSI connection ++ * @bind_conn: associate this connection with existing iSCSI session ++ * and specified transport descriptor ++ * @destroy_conn: destroy inactive iSCSI connection ++ * @set_param: set iSCSI parameter. Return 0 on success, -ENODATA ++ * when param is not supported, and a -Exx value on other ++ * error. ++ * @get_param get iSCSI parameter. Must return number of bytes ++ * copied to buffer on success, -ENODATA when param ++ * is not supported, and a -Exx value on other error ++ * @start_conn: set connection to be operational ++ * @stop_conn: suspend/recover/terminate connection ++ * @send_pdu: send iSCSI PDU, Login, Logout, NOP-Out, Reject, Text. ++ * @session_recovery_timedout: notify LLD a block during recovery timed out ++ * @init_cmd_task: Initialize a iscsi_cmd_task and any internal structs. ++ * Called from queuecommand with session lock held. ++ * @init_mgmt_task: Initialize a iscsi_mgmt_task and any internal structs. ++ * Called from iscsi_conn_send_generic with xmitmutex. ++ * @xmit_cmd_task: Requests LLD to transfer cmd task. Returns 0 or the ++ * the number of bytes transferred on success, and -Exyz ++ * value on error. ++ * @xmit_mgmt_task: Requests LLD to transfer mgmt task. Returns 0 or the ++ * the number of bytes transferred on success, and -Exyz ++ * value on error. ++ * @cleanup_cmd_task: requests LLD to fail cmd task. Called with xmitmutex ++ * and session->lock after the connection has been ++ * suspended and terminated during recovery. If called ++ * from abort task then connection is not suspended ++ * or terminated but sk_callback_lock is held ++ * ++ * Template API provided by iSCSI Transport ++ */ ++struct iscsi_transport { ++ struct module *owner; ++ char *name; ++ unsigned int caps; ++ /* LLD sets this to indicate what values it can export to sysfs */ ++ unsigned int param_mask; ++ struct scsi_host_template *host_template; ++ /* LLD connection data size */ ++ int conndata_size; ++ /* LLD session data size */ ++ int sessiondata_size; ++ int max_lun; ++ unsigned int max_conn; ++ unsigned int max_cmd_len; ++ struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it, ++ struct scsi_transport_template *t, uint32_t sn, uint32_t *hn); ++ void (*destroy_session) (struct iscsi_cls_session *session); ++ struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess, ++ uint32_t cid); ++ int (*bind_conn) (struct iscsi_cls_session *session, ++ struct iscsi_cls_conn *cls_conn, ++ uint64_t transport_eph, int is_leading); ++ int (*start_conn) (struct iscsi_cls_conn *conn); ++ void (*stop_conn) (struct iscsi_cls_conn *conn, int flag); ++ void (*destroy_conn) (struct iscsi_cls_conn *conn); ++ int (*set_param) (struct iscsi_cls_conn *conn, enum iscsi_param param, ++ char *buf, int buflen); ++ int (*get_conn_param) (struct iscsi_cls_conn *conn, ++ enum iscsi_param param, char *buf); ++ int (*get_session_param) (struct iscsi_cls_session *session, ++ enum iscsi_param param, char *buf); ++ int (*send_pdu) (struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, ++ char *data, uint32_t data_size); ++ void (*get_stats) (struct iscsi_cls_conn *conn, ++ struct iscsi_stats *stats); ++ void (*init_cmd_task) (struct iscsi_cmd_task *ctask); ++ void (*init_mgmt_task) (struct iscsi_conn *conn, ++ struct iscsi_mgmt_task *mtask, ++ char *data, uint32_t data_size); ++ int (*xmit_cmd_task) (struct iscsi_conn *conn, ++ struct iscsi_cmd_task *ctask); ++ void (*cleanup_cmd_task) (struct iscsi_conn *conn, ++ struct iscsi_cmd_task *ctask); ++ int (*xmit_mgmt_task) (struct iscsi_conn *conn, ++ struct iscsi_mgmt_task *mtask); ++ void (*session_recovery_timedout) (struct iscsi_cls_session *session); ++ int (*ep_connect) (struct sockaddr *dst_addr, int non_blocking, ++ uint64_t *ep_handle); ++ int (*ep_poll) (uint64_t ep_handle, int timeout_ms); ++ void (*ep_disconnect) (uint64_t ep_handle); ++ int (*tgt_dscvr) (enum iscsi_tgt_dscvr type, uint32_t host_no, ++ uint32_t enable, struct sockaddr *dst_addr); ++}; ++ ++/* ++ * transport registration upcalls ++ */ ++extern struct scsi_transport_template *iscsi_register_transport(struct iscsi_transport *tt); ++extern int iscsi_unregister_transport(struct iscsi_transport *tt); ++ ++/* ++ * control plane upcalls ++ */ ++extern void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error); ++extern int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, ++ char *data, uint32_t data_size); ++ ++ ++/* Connection's states */ ++#define ISCSI_CONN_INITIAL_STAGE 0 ++#define ISCSI_CONN_STARTED 1 ++#define ISCSI_CONN_STOPPED 2 ++#define ISCSI_CONN_CLEANUP_WAIT 3 ++ ++struct iscsi_cls_conn { ++ struct list_head conn_list; /* item in connlist */ ++ void *dd_data; /* LLD private data */ ++ struct iscsi_transport *transport; ++ uint32_t cid; /* connection id */ ++ ++ int active; /* must be accessed with the connlock */ ++ struct device dev; /* sysfs transport/container device */ ++}; ++ ++#define iscsi_dev_to_conn(_dev) \ ++ container_of(_dev, struct iscsi_cls_conn, dev) ++ ++/* Session's states */ ++#define ISCSI_STATE_FREE 1 ++#define ISCSI_STATE_LOGGED_IN 2 ++#define ISCSI_STATE_FAILED 3 ++#define ISCSI_STATE_TERMINATE 4 ++#define ISCSI_STATE_IN_RECOVERY 5 ++#define ISCSI_STATE_RECOVERY_FAILED 6 ++ ++struct iscsi_cls_session { ++ struct list_head sess_list; /* item in session_list */ ++ struct list_head host_list; ++ struct iscsi_transport *transport; ++ ++ /* recovery fields */ ++ int recovery_tmo; ++ struct delayed_work recovery_work; ++ ++ int target_id; ++ ++ int sid; /* session id */ ++ void *dd_data; /* LLD private data */ ++ struct device dev; /* sysfs transport/container device */ ++}; ++ ++#define iscsi_dev_to_session(_dev) \ ++ container_of(_dev, struct iscsi_cls_session, dev) ++ ++#define iscsi_session_to_shost(_session) \ ++ dev_to_shost(_session->dev.parent) ++ ++#define starget_to_session(_stgt) \ ++ iscsi_dev_to_session(_stgt->dev.parent) ++ ++struct iscsi_host { ++ struct list_head sessions; ++ struct mutex mutex; ++}; ++ ++/* ++ * session and connection functions that can be used by HW iSCSI LLDs ++ */ ++extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost, ++ struct iscsi_transport *transport); ++extern int iscsi_add_session(struct iscsi_cls_session *session, ++ unsigned int target_id); ++extern int iscsi_if_create_session_done(struct iscsi_cls_conn *conn); ++extern int iscsi_if_destroy_session_done(struct iscsi_cls_conn *conn); ++extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost, ++ struct iscsi_transport *t, ++ unsigned int target_id); ++extern void iscsi_remove_session(struct iscsi_cls_session *session); ++extern void iscsi_free_session(struct iscsi_cls_session *session); ++extern int iscsi_destroy_session(struct iscsi_cls_session *session); ++extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess, ++ uint32_t cid); ++extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn); ++extern void iscsi_unblock_session(struct iscsi_cls_session *session); ++extern void iscsi_block_session(struct iscsi_cls_session *session); ++ ++ ++#endif +Index: kernel/2.6.19_compat.patch +=================================================================== +--- kernel/2.6.19_compat.patch (revision 0) ++++ kernel/2.6.19_compat.patch (revision 779) +@@ -0,0 +1,74 @@ ++diff -Nurp a/libiscsi.c b/libiscsi.c ++--- a/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 +++++ b/libiscsi.c 2007-02-21 11:57:49.000000000 +0200 ++@@ -716,10 +716,9 @@ again: ++ return rc; ++ } ++ ++-static void iscsi_xmitworker(struct work_struct *work) +++static void iscsi_xmitworker(void *data) ++ { ++- struct iscsi_conn *conn = ++- container_of(work, struct iscsi_conn, xmitwork); +++ struct iscsi_conn *conn = data; ++ int rc; ++ /* ++ * serialize Xmit worker on a per-connection basis. ++@@ -1509,7 +1508,7 @@ iscsi_conn_setup(struct iscsi_cls_sessio ++ if (conn->mgmtqueue == ERR_PTR(-ENOMEM)) ++ goto mgmtqueue_alloc_fail; ++ ++- INIT_WORK(&conn->xmitwork, iscsi_xmitworker); +++ INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn); ++ ++ /* allocate login_mtask used for the login/text sequences */ ++ spin_lock_bh(&session->lock); ++diff -Nurp a/libiscsi.h b/libiscsi.h ++--- a/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 +++++ b/libiscsi.h 2007-01-10 12:38:25.000000000 +0200 ++@@ -25,8 +25,6 @@ ++ ++ #include ++ #include ++-#include ++-#include ++ #include "iscsi_proto.h" ++ #include "iscsi_if.h" ++ ++diff -Nurp a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c ++--- a/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 +++++ b/scsi_transport_iscsi.c 2007-01-10 12:38:25.000000000 +0200 ++@@ -234,11 +234,9 @@ static int iscsi_user_scan(struct Scsi_H ++ return 0; ++ } ++ ++-static void session_recovery_timedout(struct work_struct *work) +++static void session_recovery_timedout(void *data) ++ { ++- struct iscsi_cls_session *session = ++- container_of(work, struct iscsi_cls_session, ++- recovery_work.work); +++ struct iscsi_cls_session *session = data; ++ ++ dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed " ++ "out after %d secs\n", session->recovery_tmo); ++@@ -278,7 +276,7 @@ iscsi_alloc_session(struct Scsi_Host *sh ++ ++ session->transport = transport; ++ session->recovery_tmo = 120; ++- INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout); +++ INIT_WORK(&session->recovery_work, session_recovery_timedout, session); ++ INIT_LIST_HEAD(&session->host_list); ++ INIT_LIST_HEAD(&session->sess_list); ++ ++diff -Nurp a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h ++--- a/scsi_transport_iscsi.h 2007-01-10 12:38:26.000000000 +0200 +++++ b/scsi_transport_iscsi.h 2007-01-10 12:38:25.000000000 +0200 ++@@ -176,7 +176,7 @@ struct iscsi_cls_session { ++ ++ /* recovery fields */ ++ int recovery_tmo; ++- struct delayed_work recovery_work; +++ struct work_struct recovery_work; ++ ++ int target_id; +Index: utils/fwparam_ibft/fwparam_ibft.c +=================================================================== +--- utils/fwparam_ibft/fwparam_ibft.c (revision 0) ++++ utils/fwparam_ibft/fwparam_ibft.c (revision 779) +@@ -0,0 +1,516 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ * ++ * Copyright (C) IBM Corporation, 2006 ++ * ++ * Authors: Patrick Mansfield ++ * Mike Anderson ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "fwparam_ibft.h" ++ ++char *progname; ++int debug; ++char default_file_name[] = "/dev/mem"; ++char *filename = default_file_name; ++int boot_selected_only; ++ ++const char nulls[16]; /* defaults to zero */ ++ ++/* ++ * Prefix strings, for the "prefixN:NAME=value". ++ */ ++#define NETWORK "network" ++#define INITIATOR "iscsi-initiator" ++#define TGT "target" ++ ++ ++void ++verify_hdr(char *name, struct ibft_hdr *hdr, int id, int version, int length) ++{ ++#define VERIFY_HDR_FIELD(val) \ ++ if (hdr->val != val) { \ ++ fprintf(stderr, \ ++ "%s: error, %s structure expected %s %d but" \ ++ " got %d\n", \ ++ progname, name, #val, hdr->val, val); \ ++ exit(1); \ ++ } ++ ++ if (debug > 1) ++ fprintf(stderr, "%s: verifying %s header\n", __FUNCTION__, ++ name); ++ ++ VERIFY_HDR_FIELD(id); ++ VERIFY_HDR_FIELD(version); ++ VERIFY_HDR_FIELD(length); ++ ++#undef VERIFY_HDR_FIELD ++} ++ ++#define CHECK_HDR(ident, name) \ ++ verify_hdr(#name, &ident->hdr, id_##name, version_##name, \ ++ sizeof(*ident)) ++ ++/* ++ * Format 8 byte scsi LUN. Just format 8 bytes of hex, we could also ++ * format in the format as specified in rfc4173 (1-2-3-4, or 1-2), that is ++ * a nice format for humans :) ++ */ ++void ++format_lun(char *buf, size_t size, uint8_t *lun) ++{ ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ snprintf(buf++, size--, "%x", lun[i]); ++} ++ ++void ++dump_lun(char *prefix, char *id, uint8_t *lun) ++{ ++ char buf[32]; ++ ++ format_lun(buf, sizeof(buf), lun); ++ ++ if (prefix) ++ printf("%s%s=%s\n", prefix, id, buf); ++ else ++ printf("%s=%s\n", id, buf); ++ ++} ++ ++void ++dump_word(char *prefix, char *id, unsigned short value) ++{ ++ if (prefix) ++ printf("%s%s=%d\n", prefix, id, value); ++ else ++ printf("%s=%d\n", id, value); ++} ++ ++void ++dump_string(char *prefix, char *id, char *value, int len) ++{ ++ if (len == 0) ++ return; ++ /* ++ * Not checking if the offset is non-zero, it is not even passed ++ * in, else we need to pass a start and offset rather than value. ++ */ ++ ++ /* ++ * prints the string in "value" that has "len" characters (the ++ * printf "*" * means use the next argument as the length). ++ */ ++ if (prefix) ++ printf("%s%s=%.*s\n", prefix, id, len, value); ++ else ++ printf("%s=%.*s\n", id, len, value); ++} ++ ++void ++format_ipaddr(char *buf, size_t size, uint8_t *ip) ++{ ++ if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ++ ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0 && ++ ip[8] == 0 && ip[9] == 0 && ip[10] == 0xff && ip[11] == 0xff) { ++ /* ++ * IPV4 ++ */ ++ snprintf(buf, size, "%d.%d.%d.%d", ip[12], ip[13], ip[14], ip[15]); ++ } else { ++ /* XXX ... */ ++ fprintf(stderr, "%s: warning no IPV6 support.\n", progname); ++ buf[0] = '\0'; ++ return; ++ } ++ ++} ++ ++/* ++ * Dump the 16 byte ipaddr, as IPV6 or IPV4. ++ */ ++void ++dump_ipaddr(char *prefix, char *id, uint8_t *ip) ++{ ++ char buf[32]; ++ ++ /* ++ * Assumes all zero means no IP address. ++ */ ++ if (!memcmp(ip, nulls, sizeof(nulls))) ++ return; ++ ++ format_ipaddr(buf, sizeof(buf), ip); ++ ++ if (prefix) ++ printf("%s%s=%s\n", prefix, id, buf); ++ else ++ printf("%s=%s\n", id, buf); ++ ++} ++ ++/* ++ * Dump the 8 byte mac address ++ */ ++void ++dump_mac(char *prefix, char *id, uint8_t *mac) ++{ ++ int i; ++ ++ if (prefix) ++ printf("%s%s=", prefix, id); ++ else ++ printf("%s=", id); ++ ++ for (i = 0; i < 5; i++) ++ printf("%02x:", mac[i]); ++ printf("%02x\n", mac[i]); ++} ++ ++ ++void ++dump_initiator_prefix(void *ibft_loc, struct ibft_initiator *initiator, char *prefix) ++{ ++ if (!initiator) ++ return; ++ /* ++ * Not all fields are (or were) supported by open-iscsi. Plus, ++ * some of these are for discovery. ++ */ ++ dump_ipaddr(prefix, "ISNS", initiator->isns_server); ++ dump_ipaddr(prefix, "SLP", initiator->slp_server); ++ dump_ipaddr(prefix, "PRIMARY_RADIUS_SERVER", initiator->pri_radius_server); ++ dump_ipaddr(prefix, "SECONDARY_RADIUS_SERVER", initiator->sec_radius_server); ++ dump_string(prefix, "NAME", ibft_loc + ++ initiator->initiator_name_off, initiator->initiator_name_len); ++} ++ ++void ++dump_nic_prefix(void *ibft_loc, struct ibft_nic *nic, char *prefix) ++{ ++ ++ if (!nic) ++ return; ++ ++ dump_mac(prefix, "HWADDR", nic->mac); ++ /* ++ * Assume dhcp if any non-zero portions of its address are set ++ * (again, undocumented). ++ */ ++ if (memcmp(nic->dhcp, nulls, sizeof(nic->dhcp))) { ++ dump_ipaddr(prefix, "DHCP", nic->dhcp); ++ } else { ++ dump_ipaddr(prefix, "IPADDR", nic->ip_addr); ++ /* ++ * XXX: Not sure how a mask "prefix" will be used in network ++ * bringup, this sounds less flexible than the normal ++ * masks used. ++ */ ++ printf("%s%s:%d\n", prefix, "MASK", nic->subnet_mask_prefix); ++ dump_ipaddr(prefix, "GATEWAY", nic->gateway); ++ dump_ipaddr(prefix, "DNSADDR1", nic->primary_dns); ++ dump_ipaddr(prefix, "DNSADDR2", nic->secondary_dns); ++ } ++ ++ dump_string(prefix, "HOSTNAME", ibft_loc + nic->hostname_off, ++ nic->hostname_len); ++ /* ++ * XXX unknown vlan: ++ */ ++ dump_word(prefix, "VLAN", nic->vlan); ++ /* ++ * XXX sort of unknown pci_bdf: 8 bits bus, 5 bits device, 3 bits ++ * function. ++ */ ++ if (prefix ) ++ printf("%s%s=%d:%d:%d\n", prefix, "PCI_BDF", ++ /* bus */ (nic->pci_bdf & 0xff00) >> 8, ++ /* device */ (nic->pci_bdf & 0xf8) >> 3, ++ /* function */ (nic->pci_bdf & 0x07)); ++ else ++ printf("%s=%d:%d:%d\n", "PCI_BDF", ++ /* bus */ (nic->pci_bdf & 0xff00) >> 8, ++ /* device */ (nic->pci_bdf & 0xf8) >> 3, ++ /* function */ (nic->pci_bdf & 0x07)); ++} ++ ++void ++dump_tgt_prefix(void *ibft_loc, struct ibft_tgt *tgt, char *prefix) ++{ ++ ++ if (!tgt) ++ return; ++ ++ dump_ipaddr(prefix, "IPADDR", tgt->ip_addr); ++ dump_word(prefix, "PORT", tgt->port); ++ /* ++ * XXX there should at least be a "no LUN specified field", or ++ * have different location objects, so the setup can search for ++ * the appropriate LU (like mount by label, or use of the ++ * /dev/disk/by-id names, or .... ++ * ++ * Like: ++ * uint8_t lu_type; 0: nothing specified, 1: LUN, 2: misc ++ * name - OS can use any way it wants, would have embedded a ++ * "NAME=string", like "LABEL=myrootvolume", or ++ * "DEV_NAME=/dev/disk/by-id/scsi-198279562093043094003030903". ++ * union lu_value { ++ * uint8_t lun[8]; ++ * uint8_t misc_name[64]; ++ * }; ++ * ++ * Maybe just add an extension header, and let the admin/user put ++ * strings like: "area:VALUE=string" into it? ++ */ ++ dump_lun(prefix, "LUN", tgt->lun); ++ dump_string(prefix, "NAME", ibft_loc + tgt->tgt_name_off, ++ tgt->tgt_name_len); ++ /* ++ * Note: don't dump the nic association, just let the IP address take ++ * care of the routing. ++ */ ++ /* ++ * Note: don't dump the chap "type", just the chap names and secrets ++ * if any are specified - they imply CHAP and reversed CHAP. ++ */ ++ dump_string(prefix, "CHAP_NAME", ibft_loc + tgt->chap_name_off, ++ tgt->chap_name_len); ++ dump_string(prefix, "CHAP_PASSWORD", ibft_loc + tgt->chap_secret_off, ++ tgt->chap_secret_len); ++ dump_string(prefix, "CHAP_NAME_IN", ibft_loc + tgt->rev_chap_name_off, ++ tgt->rev_chap_name_len); ++ dump_string(prefix, "CHAP_PASSWORD_IN", ++ ibft_loc + tgt->rev_chap_secret_off, ++ tgt->rev_chap_secret_len); ++} ++ ++/* ++ * Read in and dump ASCII output for ibft starting at ibft_loc. ++ */ ++int ++dump_ibft(void *ibft_loc) ++{ ++ struct ibft_table_hdr *ibft_hdr = ibft_loc; ++ struct ibft_control *control; ++ struct ibft_initiator *initiator = NULL; ++ struct ibft_nic *nic0 = NULL, *nic1 = NULL; ++ struct ibft_tgt *tgt0 = NULL, *tgt1 = NULL; ++ char sum = 0, *buf = ibft_loc; ++ char prefix[32]; ++ ++ for (; buf <= (char *) (ibft_loc + ibft_hdr->length);) ++ sum += *buf++; ++ if (sum) ++ fprintf(stderr, "Checksum not zero 0x%x \n", sum); ++ ++ control = ibft_loc + sizeof(*ibft_hdr); ++ CHECK_HDR(control, control); ++ ++ /* ++ * The ibft is setup to return multiple pieces for each ++ * object (like multiple nic's or multiple targets), but it only ++ * maps 1 initiator, two targets, and two nics, follow that layout ++ * here (i.e. don't search for others). ++ * ++ * Also, unknown what to do for extensions piece, it is not ++ * documented. ++ */ ++ ++ if (control->initiator_off) { ++ initiator = ibft_loc + control->initiator_off; ++ CHECK_HDR(initiator, initiator); ++ } ++ ++ if (control->nic0_off) { ++ nic0 = ibft_loc + control->nic0_off; ++ CHECK_HDR(nic0, nic); ++ } ++ ++ if (control->nic1_off) { ++ nic1 = ibft_loc + control->nic1_off; ++ CHECK_HDR(nic1, nic); ++ } ++ ++ if (control->tgt0_off) { ++ tgt0 = ibft_loc + control->tgt0_off; ++ CHECK_HDR(tgt0, target); ++ } ++ ++ if (control->tgt1_off) { ++ tgt1 = ibft_loc + control->tgt1_off; ++ CHECK_HDR(tgt1, target); ++ } ++ ++ if (boot_selected_only) { ++ ++ snprintf(prefix, sizeof(prefix), "iSCSI_INITIATOR_"); ++ ++ if (initiator && (initiator->hdr.flags & ++ INIT_FLAG_FW_SEL_BOOT)) ++ dump_initiator_prefix(ibft_loc, initiator, prefix); ++ ++ if (nic0 && (nic0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) ++ dump_nic_prefix(ibft_loc, nic0, prefix); ++ else if (nic1 && (nic1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) ++ dump_nic_prefix(ibft_loc, nic1, prefix); ++ ++ snprintf(prefix, sizeof(prefix), "iSCSI_TARGET_"); ++ if (tgt0 && (tgt0->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) ++ dump_tgt_prefix(ibft_loc, tgt0, prefix); ++ else if (tgt1 && (tgt1->hdr.flags & INIT_FLAG_FW_SEL_BOOT)) ++ dump_tgt_prefix(ibft_loc, tgt1, prefix); ++ ++ } else { ++ ++ snprintf(prefix, sizeof(prefix), "%s%d:", INITIATOR, 0); ++ dump_initiator_prefix(ibft_loc, initiator, prefix); ++ ++ snprintf(prefix, sizeof(prefix), "%s%d:", NETWORK, 0); ++ dump_nic_prefix(ibft_loc, nic0, prefix); ++ snprintf(prefix, sizeof(prefix), "%s%d:", TGT, 0); ++ dump_tgt_prefix(ibft_loc, tgt0, prefix); ++ ++ snprintf(prefix, sizeof(prefix), "%s%d:", NETWORK, 1); ++ dump_nic_prefix(ibft_loc, nic1, prefix); ++ snprintf(prefix, sizeof(prefix), "%s%d:", TGT, 1); ++ dump_tgt_prefix(ibft_loc, tgt1, prefix); ++ } ++ ++ return 0; ++} ++ ++/* ++ * return the address of the location of string in filebuf, search up to ++ * max bytes of *filebuf, if not found returns NULL. ++ */ ++char * ++search_file(char *filebuf, char *string, int len, int max) ++{ ++ char *cur = filebuf; ++ char *end = filebuf + max; ++ int i = 0; ++ ++ if (debug > 1) { ++ fprintf(stderr, ++ "%s: cur 0x%p, end 0x%p, string '%.4s', len %d\n", ++ __FUNCTION__, cur, end, string, len); ++ } ++ while ((cur < end) && memcmp(cur, string, len)) { ++ if (debug > 2) { ++ fprintf(stderr, "i %d, cur 0x%p: 0x%x ('%c')\n", ++ i, cur, cur[0], cur[0]); ++ i++; ++ } ++ cur++; ++ } ++ if (cur < end) ++ return cur; ++ else ++ return NULL; ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int fd, option, ret; ++ char *filebuf, *ibft_loc; ++ int start = 512 * 1024; /* 512k */ ++ int end_search = (1024 * 1024) - start; /* 512k */ ++ ++ progname = argv[0]; ++ ++ while (1) { ++ option = getopt(argc, argv, "f:m:s:e:vhb"); ++ if (option == -1) ++ break; ++ switch (option) { ++ case 'b': ++ boot_selected_only = 1; ++ break; ++ case 'e': ++ end_search = strtoul(optarg, NULL, 0); ++ break; ++ case 'f': ++ filename = optarg; ++ break; ++ case 's': ++ start = strtoul(optarg, NULL, 0); ++ break; ++ case 'v': ++ debug++; ++ break; ++ default: ++ fprintf(stderr, "Unknown or bad option '%c'\n", option); ++ case 'h': ++ printf("Usage: %s OPTIONS\n" ++ "-b print only fw boot selected sections\n" ++ "-f file_to_search (default /dev/mem)\n" ++ "-s offset to start search\n" ++ "-e length of search\n" ++ "-v verbose\n", ++ progname); ++ exit(1); ++ } ++ } ++ ++ if (debug) ++ fprintf(stderr, "file: %s; start %d, end_search %d, debug %d\n", ++ filename, start, end_search, debug); ++ fd = open(filename, O_RDONLY); ++ if (fd < 0) { ++ fprintf(stderr, "Could not open %s: %s (%d)\n", ++ filename, strerror(errno), errno); ++ exit(1); ++ } ++ ++ /* ++ * XXX Possibly warn and exit if start > filesize(fd), or if start + ++ * end_search > filesize(fd). Else, we will get a bus error for ++ * small files (with memmap, and for testing at least, it would ++ * be hard to find a system with less than 1024k). ++ */ ++ filebuf = mmap(NULL, end_search, PROT_READ, MAP_PRIVATE, fd, start); ++ if (filebuf == MAP_FAILED) { ++ fprintf(stderr, "Could not mmap %s: %s (%d)\n", ++ filename, strerror(errno), errno); ++ exit(1); ++ } ++ ++ ibft_loc = search_file(filebuf, iBFTSTR, strlen(iBFTSTR), end_search); ++ if (ibft_loc) { ++ if (dump_ibft(ibft_loc)) ++ ret = 0; ++ else ++ ret = 1; ++ } else ++ ret = 1; ++ munmap(filebuf, end_search); ++ close(fd); ++ exit(ret); ++} +Index: utils/fwparam_ibft/fwparam_ibft.h +=================================================================== +--- utils/fwparam_ibft/fwparam_ibft.h (revision 0) ++++ utils/fwparam_ibft/fwparam_ibft.h (revision 779) +@@ -0,0 +1,139 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ * ++ * Copyright (C) IBM Corporation, 2006 ++ * ++ * Authors: Doug Maxey ++ * Patrick Mansfield ++ * ++ */ ++ ++#ifndef FWPARAM_IBFT_H_ ++#define FWPARAM_IBFT_H_ ++ ++/* #include */ ++#include ++ ++/* ++ * Structures here are is based on Doug's original code, and Patrick's ++ * interpretation of the IBM internal design document title the "iSCSI ++ * Boot Firmware Table (iBFT)". ++ */ ++#define iBFTSTR "iBFT" ++#define iBFT_SIG { 'i','B','F','T' } ++ ++ ++/* ++ * These macros are lower case to make the verify_hdr macro easier. ++ */ ++#define version_control 1 ++#define version_initiator 1 ++#define version_nic 1 ++#define version_target 1 ++#define version_extensions 1 ++ ++enum ibft_id { ++ id_control = 1, ++ id_initiator, ++ id_nic, ++ id_target, ++ id_extensions, ++}; ++ ++struct ibft_hdr { ++ uint8_t id; ++ uint8_t version; ++ uint16_t length; ++ uint8_t ind; ++ uint8_t flags; ++}; ++ ++struct ibft_table_hdr { ++ uint8_t signature[4]; ++ uint32_t length; ++ uint8_t revision; ++ uint8_t checksum; ++ uint8_t oemid[6]; ++ uint8_t oem_table_id[8]; ++ uint8_t rsvd1[24]; ++} __attribute__((__packed__)); ++ ++struct ibft_control { ++ struct ibft_hdr hdr; ++ uint16_t extensions; ++ uint16_t initiator_off; ++ uint16_t nic0_off; ++ uint16_t tgt0_off; ++ uint16_t nic1_off; ++ uint16_t tgt1_off; ++} __attribute__((__packed__)); ++ ++struct ibft_initiator { ++#define INIT_FLAG_VALID 1 ++#define INIT_FLAG_FW_SEL_BOOT 2 ++ struct ibft_hdr hdr; ++ uint8_t isns_server[16]; ++ uint8_t slp_server[16]; ++ uint8_t pri_radius_server[16]; ++ uint8_t sec_radius_server[16]; ++ uint16_t initiator_name_len; ++ uint16_t initiator_name_off; ++} __attribute__((__packed__)); ++ ++struct ibft_nic { ++#define NIC_FLAG_VALID 1 ++#define NIC_FLAG_FW_SEL_BOOT 2 ++ struct ibft_hdr hdr; ++ uint8_t ip_addr[16]; ++ uint8_t subnet_mask_prefix; ++ uint8_t origin; ++ uint8_t gateway[16]; ++ uint8_t primary_dns[16]; ++ uint8_t secondary_dns[16]; ++ uint8_t dhcp[16]; ++ uint16_t vlan; ++ uint8_t mac[6]; ++ uint16_t pci_bdf; ++ uint16_t hostname_len; ++ uint16_t hostname_off; ++} __attribute__((__packed__)); ++ ++struct ibft_tgt { ++#define TGT_FLAG_VALID 1 ++#define TGT_FLAG_FW_SEL_BOOT 2 ++#define TGT_FLAG_USE_RADIUS_CHAT 4 ++#define TGT_FLAG_USE_RADIUS_RCHAT 8 ++ struct ibft_hdr hdr; ++ uint8_t ip_addr[16]; ++ uint16_t port; ++ uint8_t lun[8]; ++#define TGT_CHAP 1 ++#define TGT_MUTUAL_CHAP 2 ++ uint8_t chap_type; ++ uint8_t nic_assoc; ++ uint16_t tgt_name_len; ++ uint16_t tgt_name_off; ++ uint16_t chap_name_len; ++ uint16_t chap_name_off; ++ uint16_t chap_secret_len; ++ uint16_t chap_secret_off; ++ uint16_t rev_chap_name_len; ++ uint16_t rev_chap_name_off; ++ uint16_t rev_chap_secret_len; ++ uint16_t rev_chap_secret_off; ++} __attribute__((__packed__)); ++ ++#endif /* FWPARAM_IBFT_H_ */ +Index: utils/fwparam_ibft/Makefile +=================================================================== +--- utils/fwparam_ibft/Makefile (revision 0) ++++ utils/fwparam_ibft/Makefile (revision 779) +@@ -0,0 +1,11 @@ ++OPTFLAGS ?= -O2 -g ++WARNFLAGS ?= -Wall -Wstrict-prototypes ++CFLAGS += $(OPTFLAGS) $(WARNFLAGS) ++PROGRAMS = fwparam_ibft ++ ++all: $(PROGRAMS) ++ ++fwparam_ibft: fwparam_ibft.c fwparam_ibft.h ++ $(CC) $(CFLAGS) $^ -o $@ ++clean: ++ rm -f *.o $(PROGRAMS) +Index: utils/iscsi-iname.c +=================================================================== +--- utils/iscsi-iname.c (revision 0) ++++ utils/iscsi-iname.c (revision 779) +@@ -0,0 +1,140 @@ ++/* ++ * iSCSI InitiatorName creation utility ++ * Copyright (C) 2001 Cisco Systems, Inc. ++ * maintained by linux-iscsi-devel@lists.sourceforge.net ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published ++ * by the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * See the file COPYING included with this distribution for more details. ++ * ++ * $Id: iscsi-iname.c,v 1.1.2.3 2005/03/15 06:33:44 wysochanski Exp $ ++ * ++ * iscsi-iname.c - Compute an iSCSI InitiatorName for this host. ++ * Note that to ensure uniqueness, the system time is ++ * a factor. This name must be cached and only regenerated ++ * if there is no cached value. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "md5.h" ++ ++#define RANDOM_NUM_GENERATOR "/dev/urandom" ++ ++int ++main(int argc, char *argv[]) ++{ ++ char iname[256]; ++ struct timeval time; ++ struct utsname system_info; ++ long hostid; ++ struct MD5Context context; ++ unsigned char digest[16]; ++ unsigned char *bytes = digest; ++ unsigned char entropy[16]; ++ int e; ++ int fd; ++ char *prefix; ++ ++ /* initialize */ ++ memset(iname, 0, sizeof (iname)); ++ memset(digest, 0, sizeof (digest)); ++ memset(&context, 0, sizeof (context)); ++ MD5Init(&context); ++ ++ /* take a prefix if given, otherwise use a default. */ ++ if (argc > 1 && argv[1]) { ++ prefix = argv[1]; ++ if (( strcmp(prefix, "-h") == 0 ) || ++ ( strcmp(prefix, "--help") == 0 )) { ++ printf("\nDisplays the iSCSI initiator name\n"); ++ exit(0); ++ } else if ( strcmp(prefix, "-p") == 0 ) { ++ prefix = argv[2]; ++ } else { ++ printf("\nUsage: iscsi-iname [-h | --help | " ++ "-p ]\n"); ++ exit(0); ++ } ++ } else { ++ prefix = "iqn.2005-03.org.open-iscsi"; ++ } ++ ++ /* try to feed some entropy from the pool to MD5 in order to get ++ * uniqueness properties ++ */ ++ ++ if ((fd = open(RANDOM_NUM_GENERATOR, O_RDONLY))) { ++ e = read(fd, &entropy, 16); ++ if (e >= 1) ++ MD5Update(&context, (md5byte *)entropy, e); ++ close(fd); ++ } ++ ++ /* time the name is created is a factor in order to get ++ * uniqueness properties ++ */ ++ if (gettimeofday(&time, NULL) < 0) { ++ perror("error: gettimeofday failed"); ++ return 1; ++ } ++ MD5Update(&context, (md5byte *) & time.tv_sec, sizeof (time.tv_sec)); ++ MD5Update(&context, (md5byte *) & time.tv_usec, sizeof (time.tv_usec)); ++ ++ /* hostid */ ++ hostid = gethostid(); ++ MD5Update(&context, (md5byte *) & hostid, sizeof (hostid)); ++ ++ /* get the hostname and system name */ ++ if (uname(&system_info) < 0) { ++ perror("error: uname failed"); ++ return 1; ++ } ++ MD5Update(&context, (md5byte *) system_info.sysname, ++ sizeof (system_info.sysname)); ++ MD5Update(&context, (md5byte *) system_info.nodename, ++ sizeof (system_info.nodename)); ++ MD5Update(&context, (md5byte *) system_info.release, ++ sizeof (system_info.release)); ++ MD5Update(&context, (md5byte *) system_info.version, ++ sizeof (system_info.version)); ++ MD5Update(&context, (md5byte *) system_info.machine, ++ sizeof (system_info.machine)); ++ ++ /* compute the md5 hash of all the bits we just collected */ ++ MD5Final(digest, &context); ++ ++ /* vary which md5 bytes we pick (though we probably don't need to do ++ * this, since hopefully MD5 produces results such that each byte is as ++ * good as any other). ++ */ ++ ++ if ((fd = open(RANDOM_NUM_GENERATOR, O_RDONLY))) { ++ if (read(fd, entropy, 1) == 1) ++ bytes = &digest[(entropy[0] % (sizeof(digest) - 6))]; ++ close(fd); ++ } ++ ++ /* print the prefix followed by 6 bytes of the MD5 hash */ ++ sprintf(iname, "%s:%x%x%x%x%x%x", prefix, ++ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]); ++ ++ iname[sizeof (iname) - 1] = '\0'; ++ printf("%s\n", iname); ++ return 0; ++} +Index: utils/md5.c +=================================================================== +--- utils/md5.c (revision 0) ++++ utils/md5.c (revision 779) +@@ -0,0 +1,242 @@ ++/* ++ * This code implements the MD5 message-digest algorithm. ++ * The algorithm is due to Ron Rivest. This code was ++ * written by Colin Plumb in 1993, no copyright is claimed. ++ * This code is in the public domain; do with it what you wish. ++ * ++ * Equivalent code is available from RSA Data Security, Inc. ++ * This code has been tested against that, and is equivalent, ++ * except that you don't need to include two pages of legalese ++ * with every copy. ++ * ++ * To compute the message digest of a chunk of bytes, declare an ++ * MD5Context structure, pass it to MD5Init, call MD5Update as ++ * needed on buffers full of bytes, and then call MD5Final, which ++ * will fill a supplied 16-byte array with the digest. ++ * ++ * Changed so as no longer to depend on Colin Plumb's `usual.h' header ++ * definitions; now uses stuff from dpkg's config.h. ++ * - Ian Jackson . ++ * Still in the public domain. ++ */ ++ ++#include ++ ++#include "md5.h" ++ ++#if (__BYTE_ORDER == __BIG_ENDIAN) ++/* ++ * we can compile this away for little endian since ++ * it does not do anything on those archs ++ */ ++void ++byteSwap(uint32_t * buf, unsigned words) ++{ ++ md5byte *p = (md5byte *) buf; ++ ++ do { ++ *buf++ = (uint32_t) ((unsigned) p[3] << 8 | p[2]) << 16 | ++ ((unsigned) p[1] << 8 | p[0]); ++ p += 4; ++ } while (--words); ++} ++#else ++#define byteSwap(buf,words) ++#endif ++ ++/* ++ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious ++ * initialization constants. ++ */ ++void ++MD5Init(struct MD5Context *ctx) ++{ ++ ctx->buf[0] = 0x67452301; ++ ctx->buf[1] = 0xefcdab89; ++ ctx->buf[2] = 0x98badcfe; ++ ctx->buf[3] = 0x10325476; ++ ++ ctx->bytes[0] = 0; ++ ctx->bytes[1] = 0; ++} ++ ++/* ++ * Update context to reflect the concatenation of another buffer full ++ * of bytes. ++ */ ++void ++MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) ++{ ++ uint32_t t; ++ ++ /* Update byte count */ ++ ++ t = ctx->bytes[0]; ++ if ((ctx->bytes[0] = t + len) < t) ++ ctx->bytes[1]++; /* Carry from low to high */ ++ ++ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ ++ if (t > len) { ++ memcpy((md5byte *) ctx->in + 64 - t, buf, len); ++ return; ++ } ++ /* First chunk is an odd size */ ++ memcpy((md5byte *) ctx->in + 64 - t, buf, t); ++ byteSwap(ctx->in, 16); ++ MD5Transform(ctx->buf, ctx->in); ++ buf += t; ++ len -= t; ++ ++ /* Process data in 64-byte chunks */ ++ while (len >= 64) { ++ memcpy(ctx->in, buf, 64); ++ byteSwap(ctx->in, 16); ++ MD5Transform(ctx->buf, ctx->in); ++ buf += 64; ++ len -= 64; ++ } ++ ++ /* Handle any remaining bytes of data. */ ++ memcpy(ctx->in, buf, len); ++} ++ ++/* ++ * Final wrapup - pad to 64-byte boundary with the bit pattern ++ * 1 0* (64-bit count of bits processed, MSB-first) ++ */ ++void ++MD5Final(md5byte digest[16], struct MD5Context *ctx) ++{ ++ int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ ++ md5byte *p = (md5byte *) ctx->in + count; ++ ++ /* Set the first char of padding to 0x80. There is always room. */ ++ *p++ = 0x80; ++ ++ /* Bytes of padding needed to make 56 bytes (-8..55) */ ++ count = 56 - 1 - count; ++ ++ if (count < 0) { /* Padding forces an extra block */ ++ memset(p, 0, count + 8); ++ byteSwap(ctx->in, 16); ++ MD5Transform(ctx->buf, ctx->in); ++ p = (md5byte *) ctx->in; ++ count = 56; ++ } ++ memset(p, 0, count); ++ byteSwap(ctx->in, 14); ++ ++ /* Append length in bits and transform */ ++ ctx->in[14] = ctx->bytes[0] << 3; ++ ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; ++ MD5Transform(ctx->buf, ctx->in); ++ ++ byteSwap(ctx->buf, 4); ++ memcpy(digest, ctx->buf, 16); ++ memset(ctx, 0, sizeof (ctx)); /* In case it's sensitive */ ++} ++ ++#ifndef ASM_MD5 ++ ++/* The four core functions - F1 is optimized somewhat */ ++ ++/* #define F1(x, y, z) (x & y | ~x & z) */ ++#define F1(x, y, z) (z ^ (x & (y ^ z))) ++#define F2(x, y, z) F1(z, x, y) ++#define F3(x, y, z) (x ^ y ^ z) ++#define F4(x, y, z) (y ^ (x | ~z)) ++ ++/* This is the central step in the MD5 algorithm. */ ++#define MD5STEP(f,w,x,y,z,in,s) \ ++ (w += f(x,y,z) + in, w = (w<>(32-s)) + x) ++ ++/* ++ * The core of the MD5 algorithm, this alters an existing MD5 hash to ++ * reflect the addition of 16 longwords of new data. MD5Update blocks ++ * the data and converts bytes into longwords for this routine. ++ */ ++void ++MD5Transform(uint32_t buf[4], uint32_t const in[16]) ++{ ++ register uint32_t a, b, c, d; ++ ++ a = buf[0]; ++ b = buf[1]; ++ c = buf[2]; ++ d = buf[3]; ++ ++ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); ++ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); ++ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); ++ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); ++ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); ++ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); ++ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); ++ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); ++ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); ++ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); ++ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); ++ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); ++ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); ++ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); ++ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); ++ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); ++ ++ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); ++ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); ++ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); ++ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); ++ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); ++ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); ++ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); ++ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); ++ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); ++ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); ++ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); ++ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); ++ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); ++ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); ++ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); ++ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); ++ ++ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); ++ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); ++ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); ++ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); ++ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); ++ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); ++ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); ++ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); ++ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); ++ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); ++ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); ++ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); ++ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); ++ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); ++ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); ++ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); ++ ++ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); ++ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); ++ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); ++ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); ++ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); ++ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); ++ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); ++ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); ++ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); ++ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); ++ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); ++ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); ++ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); ++ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); ++ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); ++ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); ++ ++ buf[0] += a; ++ buf[1] += b; ++ buf[2] += c; ++ buf[3] += d; ++} ++ ++#endif +Index: utils/md5.h +=================================================================== +--- utils/md5.h (revision 0) ++++ utils/md5.h (revision 779) +@@ -0,0 +1,41 @@ ++/* ++ * This is the header file for the MD5 message-digest algorithm. ++ * The algorithm is due to Ron Rivest. This code was ++ * written by Colin Plumb in 1993, no copyright is claimed. ++ * This code is in the public domain; do with it what you wish. ++ * ++ * Equivalent code is available from RSA Data Security, Inc. ++ * This code has been tested against that, and is equivalent, ++ * except that you don't need to include two pages of legalese ++ * with every copy. ++ * ++ * To compute the message digest of a chunk of bytes, declare an ++ * MD5Context structure, pass it to MD5Init, call MD5Update as ++ * needed on buffers full of bytes, and then call MD5Final, which ++ * will fill a supplied 16-byte array with the digest. ++ * ++ * Changed so as no longer to depend on Colin Plumb's `usual.h' ++ * header definitions; now uses stuff from dpkg's config.h ++ * - Ian Jackson . ++ * Still in the public domain. ++ */ ++ ++#ifndef MD5_H ++#define MD5_H ++ ++#include ++ ++#define md5byte unsigned char ++ ++struct MD5Context { ++ uint32_t buf[4]; ++ uint32_t bytes[2]; ++ uint32_t in[16]; ++}; ++ ++void MD5Init(struct MD5Context *context); ++void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); ++void MD5Final(unsigned char digest[16], struct MD5Context *context); ++void MD5Transform(uint32_t buf[4], uint32_t const in[16]); ++ ++#endif /* !MD5_H */ +Index: utils/Makefile +=================================================================== +--- utils/Makefile (revision 0) ++++ utils/Makefile (revision 779) +@@ -0,0 +1,12 @@ ++# This Makefile will work only with GNU make. ++ ++CFLAGS += $(OPTFLAGS) -O2 -fno-inline -Wall -Wstrict-prototypes -g ++PROGRAMS = iscsi-iname ++ ++all: $(PROGRAMS) ++ ++iscsi-iname: md5.o iscsi-iname.o ++ $(CC) $(CFLAGS) $^ $(DBM_LIB) -o $@ ++ ++clean: ++ rm -f *.o $(PROGRAMS) +Index: include/iscsi_proto.h +=================================================================== +--- include/iscsi_proto.h (revision 754) ++++ include/iscsi_proto.h (revision 779) +@@ -41,6 +41,29 @@ + #define zero_data(p) {p[0]=0;p[1]=0;p[2]=0;} + + /* ++ * If running svn modules we may need to define these. ++ * This should not go upstream since this is already properly defined there ++ */ ++#ifdef __CHECKER__ ++#define __bitwise__ __attribute__((bitwise)) ++#else ++#define __bitwise__ ++#endif ++#ifdef __CHECK_ENDIAN__ ++#define __bitwise __bitwise__ ++#else ++#define __bitwise ++#endif ++ ++/* initiator tags; opaque for target */ ++typedef uint32_t __bitwise__ itt_t; ++/* below makes sense only for initiator that created this tag */ ++#define build_itt(itt, id, age) ((__force itt_t)\ ++ ((itt) | ((id) << ISCSI_CID_SHIFT) | ((age) << ISCSI_AGE_SHIFT))) ++#define get_itt(itt) ((__force uint32_t)(itt_t)(itt) & ISCSI_ITT_MASK) ++#define RESERVED_ITT ((__force itt_t)0xffffffff) ++ ++/* + * iSCSI Template Message Header + */ + struct iscsi_hdr { +@@ -50,7 +73,7 @@ + uint8_t hlength; /* AHSs total length */ + uint8_t dlength[3]; /* Data length */ + uint8_t lun[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag, opaque for target */ + __be32 ttt; /* Target Task Tag */ + __be32 statsn; + __be32 exp_statsn; +@@ -111,7 +134,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be32 data_length; + __be32 cmdsn; + __be32 exp_statsn; +@@ -148,7 +171,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be32 rsvd1; + __be32 statsn; + __be32 exp_cmdsn; +@@ -206,7 +229,7 @@ + uint8_t rsvd3; + uint8_t dlength[3]; + uint8_t lun[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be32 ttt; /* Target Transfer Tag */ + __be32 cmdsn; + __be32 exp_statsn; +@@ -221,7 +244,7 @@ + uint8_t rsvd3; + uint8_t dlength[3]; + uint8_t lun[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be32 ttt; /* Target Transfer Tag */ + __be32 statsn; + __be32 exp_cmdsn; +@@ -237,8 +260,8 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; +- __be32 itt; /* Initiator Task Tag */ +- __be32 rtt; /* Reference Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ ++ itt_t rtt; /* Reference Task Tag */ + __be32 cmdsn; + __be32 exp_statsn; + __be32 refcmdsn; +@@ -267,8 +290,8 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd2[8]; +- __be32 itt; /* Initiator Task Tag */ +- __be32 rtt; /* Reference Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ ++ itt_t rtt; /* Reference Task Tag */ + __be32 statsn; + __be32 exp_cmdsn; + __be32 max_cmdsn; +@@ -293,7 +316,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be32 ttt; /* Target Transfer Tag */ + __be32 statsn; + __be32 exp_cmdsn; +@@ -311,7 +334,7 @@ + uint8_t rsvd3; + uint8_t dlength[3]; + uint8_t lun[8]; +- __be32 itt; ++ itt_t itt; + __be32 ttt; + __be32 rsvd4; + __be32 exp_statsn; +@@ -331,7 +354,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t lun[8]; +- __be32 itt; ++ itt_t itt; + __be32 ttt; + __be32 statsn; + __be32 exp_cmdsn; +@@ -355,7 +378,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd4[8]; +- __be32 itt; ++ itt_t itt; + __be32 ttt; + __be32 cmdsn; + __be32 exp_statsn; +@@ -373,7 +396,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd4[8]; +- __be32 itt; ++ itt_t itt; + __be32 ttt; + __be32 statsn; + __be32 exp_cmdsn; +@@ -392,7 +415,7 @@ + uint8_t dlength[3]; + uint8_t isid[6]; /* Initiator Session ID */ + __be16 tsih; /* Target Session Handle */ +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be16 cid; + __be16 rsvd3; + __be32 cmdsn; +@@ -421,7 +444,7 @@ + uint8_t dlength[3]; + uint8_t isid[6]; /* Initiator Session ID */ + __be16 tsih; /* Target Session Handle */ +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be32 rsvd3; + __be32 statsn; + __be32 exp_cmdsn; +@@ -478,7 +501,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd2[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be16 cid; + uint8_t rsvd3[2]; + __be32 cmdsn; +@@ -505,7 +528,7 @@ + uint8_t hlength; + uint8_t dlength[3]; + uint8_t rsvd3[8]; +- __be32 itt; /* Initiator Task Tag */ ++ itt_t itt; /* Initiator Task Tag */ + __be32 rsvd4; + __be32 statsn; + __be32 exp_cmdsn; +@@ -528,7 +551,7 @@ + uint8_t opcode; + uint8_t flags; + uint8_t rsvd2[14]; +- __be32 itt; ++ itt_t itt; + __be32 begrun; + __be32 runlength; + __be32 exp_statsn; +@@ -580,8 +603,18 @@ + #define VALUE_MAXLEN 255 + #define TARGET_NAME_MAXLEN VALUE_MAXLEN + +-#define DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH 8192 ++#define ISCSI_DEF_MAX_RECV_SEG_LEN 8192 ++#define ISCSI_MIN_MAX_RECV_SEG_LEN 512 ++#define ISCSI_MAX_MAX_RECV_SEG_LEN 16777215 + ++#define ISCSI_DEF_FIRST_BURST_LEN 65536 ++#define ISCSI_MIN_FIRST_BURST_LEN 512 ++#define ISCSI_MAX_FIRST_BURST_LEN 16777215 ++ ++#define ISCSI_DEF_MAX_BURST_LEN 262144 ++#define ISCSI_MIN_MAX_BURST_LEN 512 ++#define ISCSI_MAX_MAX_BURST_LEN 16777215 ++ + /************************* RFC 3720 End *****************************/ + + #endif /* ISCSI_PROTO_H */ +Index: doc/iscsiadm.8 +=================================================================== +--- doc/iscsiadm.8 (revision 754) ++++ doc/iscsiadm.8 (revision 779) +@@ -5,15 +5,25 @@ + \fBiscsiadm\fR -m discovery [ -dhV ] [ -t type -p ip:port [ -l ] ] | + [ -o operation ] [ -n name ] [ -v value ] + +-\fBiscsiadm\fR -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port | -M sysdir ] [ -l | -u ] ] ++\fBiscsiadm\fR -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port ] [ -l | -u ] ] + [ [ -o operation ] [ -n name ] [ -v value ] [ -p ip:port ] ] + +-\fBiscsiadm\fR -m session [ -dhV ] [ -r sessionid [ -R ] [ -u | -s ] ] ++\fBiscsiadm\fR -m session [ -dhV ] [ -r sessionid | sysfsdir [ -R ] [ -u | -s ] ] + + .SH "DESCRIPTION" + The iscsiadm utility is a command-line tool allowing discovery and login + to iSCSI targets, as well as access and management of the open-iscsi + database. ++ ++Open-iscsi does not use the term node as defined by the iSCSI RFC, ++where a node is a single iSCSI initiator or target. Open-iscsi uses the ++term node to refer to a portal on a target, so tools like iscsiadm ++require that --targetname and --portal argument be used when in node mode. ++ ++For session mode, a session id (sid) is used. The sid of a session can be ++found by running iscsiadm -m session -i. The session id is not currently ++persistent and is partially determined by when the session is setup. ++ + .PP + Note that many of the node and discovery operations require that the iSCSI + daemon (iscsid) be running. +@@ -72,7 +82,7 @@ + Specifies a database operator \fIop\fR. \fIop\fR must be one of + \fInew\fR, \fIdelete\fR, \fIupdate\fR or \fIshow\fR. + .IP +-This option is only valid for all modes. ++This option is only valid for all modes, but delete should not be used on a running session. + .IP + \fInew\fR is currently valid only for node and session mode. It creates a new + database record for a given \fIportal\fR (IP address and port number). +@@ -95,16 +105,23 @@ + .IP + This option is only valid for discovery, or for node operations with + the \fInew\fR operator. ++.IP ++This should be used along with --target in node mode, to specify what the open-iscsi docs refer to as a node or node record. Note: open-iscsi's use of the word node, does not match the iSCSI RFC's iSCSI Node term. + + .TP + \fB\-T\fR, \fB\-\-targetname=\fItargetname\fR + Use target \fItargetname\fR. ++.IP ++This should be used along with --portal in node mode, to specify what the open-iscsi docs refer to as a node or node record. Note: open-iscsi's use of the word node, does not match the iSCSI RFC's iSCSI Node term. + + .TP +-\fB\-r\fR, \fB\-\-sid=\fIsid\fR +-Use session ID \fIsid\fR. ++\fB\-r\fR, \fB\-\-sid=\fIsid | sysfsdir\fR ++Use session ID \fIsid\fR. The sid of a session can be found from running ++iscsiadm in session mode with the --info argument. ++ ++Instead of sid, a sysfs path containing the session can be used. For example using one of the following: /sys/devices/platform/hostH/sessionS/targetH:B:I/H:B:I:L, /sys/devices/platform/hostH/sessionS/targetH:B:I, or /sys/devices/platform/hostH/sessionS, for the sysfsdir argument would result in the session with sid S to be used. + .IP +-\fIsid\fR is only required for session mode. ++\fIsid | sysfsdir\fR is only required for session mode. + + .TP + \fB\-R\fR, \fB\-\-rescan\fR +@@ -129,8 +146,8 @@ + .TP + \fB\-t\fR, \fB\-\-type=\fItype\fR + \fItype\fR must be \fIsendtargets\fR (or abbreviated as \fIst\fR), +-\fIslp\fR, or \fIisns\fR. Currently only sendtargets is supported, see the +-DISCOVERY TYPES section. ++\fIslp\fR, or \fIisns\fR. Currently only sendtargets and iSNS is supported, ++see the DISCOVERY TYPES section. + .IP + This option is only valid for discovery mode. + +@@ -178,18 +195,18 @@ + .TP + .B + iSNS +-iSNS (Internet Storage Name Service) is a proposed standard to record +-information about storage volumes within a larger network. In theory +-it can be implemented for iSCSI and Fibre Channel. However, no +-implementation of the central nameserver mandated by this standard is +-currently available. ++iSNS (Internet Storage Name Service) records information about storage ++volumes within a larger network. To utilize iSNS, the address of the ++iSNS server must be set in iscsid.conf using the "isns.address" value, ++and iscsiadm must be run in discovery mode with the "isns" discovery type. ++ + .P +-Currently open-iscsi supports only the ++iscsiadm supports the + .B +-SendTargets ++iSNS (isns) + or + .B +-st ++SendTargets (st) + discovery type. An SLP implementation is under development. + + .SH EXAMPLES +Index: usr/mgmt_ipc.c +=================================================================== +--- usr/mgmt_ipc.c (revision 754) ++++ usr/mgmt_ipc.c (revision 779) +@@ -207,6 +207,12 @@ + return MGMT_IPC_ERR; + } + ++static mgmt_ipc_err_e ++mgmt_ipc_isns_dev_attr_query(queue_task_t *qtask) ++{ ++ return isns_dev_attr_query_task(qtask); ++} ++ + static int + mgmt_peeruser(int sock, char *user) + { +@@ -291,6 +297,21 @@ + #endif + } + ++void ++mgmt_ipc_write_rsp(queue_task_t *qtask, mgmt_ipc_err_e err) ++{ ++ log_debug(4, "%s: rsp to fd %d", __FUNCTION__, ++ qtask->mgmt_ipc_fd); ++ ++ if (qtask->mgmt_ipc_fd < 0) ++ return; ++ ++ qtask->rsp.err = err; ++ write(qtask->mgmt_ipc_fd, &qtask->rsp, sizeof(qtask->rsp)); ++ close(qtask->mgmt_ipc_fd); ++ free(qtask); ++} ++ + static int + mgmt_ipc_handle(int accept_fd) + { +@@ -332,8 +353,8 @@ + rc = -ENOMEM; + goto err; + } +- memcpy(&qtask->u.login.req, &req, sizeof(iscsiadm_req_t)); +- qtask->u.login.mgmt_ipc_fd = fd; ++ memcpy(&qtask->req, &req, sizeof(iscsiadm_req_t)); ++ qtask->mgmt_ipc_fd = fd; + + switch(req.command) { + case MGMT_IPC_SESSION_LOGIN: +@@ -379,6 +400,9 @@ + immrsp = 1; + rc = 1; + break; ++ case MGMT_IPC_ISNS_DEV_ATTR_QUERY: ++ rsp.err = mgmt_ipc_isns_dev_attr_query(qtask); ++ break; + default: + log_error("unknown request: %s(%d) %u", + __FUNCTION__, __LINE__, req.command); +@@ -428,10 +452,12 @@ + + #define POLL_CTRL 0 + #define POLL_IPC 1 +-#define POLL_MAX 2 ++#define POLL_ISNS 2 ++#define POLL_MAX 3 + + /* TODO: this should go somewhere else */ +-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd) ++void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd, ++ int isns_fd) + { + struct pollfd poll_array[POLL_MAX]; + int res; +@@ -441,6 +467,13 @@ + poll_array[POLL_IPC].fd = mgmt_ipc_fd; + poll_array[POLL_IPC].events = POLLIN; + ++ if (isns_fd < 0) ++ poll_array[POLL_ISNS].fd = poll_array[POLL_ISNS].events = 0; ++ else { ++ poll_array[POLL_ISNS].fd = isns_fd; ++ poll_array[POLL_ISNS].events = POLLIN; ++ } ++ + while (1) { + res = poll(poll_array, POLL_MAX, ACTOR_RESOLUTION); + if (res > 0) { +@@ -451,6 +484,9 @@ + if (poll_array[POLL_IPC].revents) + if (mgmt_ipc_handle(mgmt_ipc_fd) == 1) + break; ++ if (poll_array[POLL_ISNS].revents) ++ isns_handle(isns_fd); ++ + } else if (res < 0) { + if (errno == EINTR) { + log_debug(1, "event_loop interrupted"); +Index: usr/iscsi_settings.h +=================================================================== +--- usr/iscsi_settings.h (revision 0) ++++ usr/iscsi_settings.h (revision 779) +@@ -0,0 +1,15 @@ ++/* ++ * Default initiator settings. These may not be the same as ++ * in the RFC. See iscsi_proto.h for those. ++ */ ++/* timeouts in seconds */ ++#define DEF_LOGIN_TIMEO 15 ++#define DEF_LOGOUT_TIMEO 15 ++#define DEF_NOOP_OUT_INTERVAL 10 ++#define DEF_NOOP_OUT_TIMEO 15 ++#define DEF_REPLACEMENT_TIMEO 120 ++ ++/* data and segment lengths in bytes */ ++#define DEF_INI_FIRST_BURST_LEN 262144 ++#define DEF_INI_MAX_BURST_LEN 16776192 ++#define DEF_INI_MAX_RECV_SEG_LEN 131072 +Index: usr/mgmt_ipc.h +=================================================================== +--- usr/mgmt_ipc.h (revision 754) ++++ usr/mgmt_ipc.h (revision 779) +@@ -44,6 +44,7 @@ + MGMT_IPC_ERR_TRANS_CAPS = 14, + MGMT_IPC_ERR_EXISTS = 15, + MGMT_IPC_ERR_INVALID_REQ = 16, ++ MGMT_IPC_ERR_ISNS_UNAVAILABLE = 17, + } mgmt_ipc_err_e; + + typedef enum iscsiadm_cmd { +@@ -60,6 +61,7 @@ + MGMT_IPC_IMMEDIATE_STOP = 11, + MGMT_IPC_SESSION_SYNC = 12, + MGMT_IPC_SESSION_INFO = 13, ++ MGMT_IPC_ISNS_DEV_ATTR_QUERY = 14, + } iscsiadm_cmd_e; + + typedef enum iscsi_conn_state_e { +@@ -125,7 +127,10 @@ + struct iscsi_ipc *ipc; + + void need_reap(void); +-void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd); ++void event_loop(struct iscsi_ipc *ipc, int control_fd, int mgmt_ipc_fd, int isns_fd); ++ ++struct queue_task; ++void mgmt_ipc_write_rsp(struct queue_task *qtask, mgmt_ipc_err_e err); + int mgmt_ipc_listen(void); + void mgmt_ipc_close(int fd); + +Index: usr/ioctl.c +=================================================================== +--- usr/ioctl.c (revision 754) ++++ usr/ioctl.c (revision 779) +@@ -44,10 +44,10 @@ + static void *pdu_sendbuf; + + #define IOCTL_BUF_DEFAULT_MAX \ +- (DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + sizeof(struct iscsi_hdr)) ++ (ISCSI_DEF_MAX_RECV_SEG_LEN + sizeof(struct iscsi_hdr)) + + #define PDU_SENDBUF_DEFAULT_MAX \ +- (DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + sizeof(struct iscsi_hdr)) ++ (ISCSI_DEF_MAX_RECV_SEG_LEN + sizeof(struct iscsi_hdr)) + + static int + kread(char *data, int count) +Index: usr/util.c +=================================================================== +--- usr/util.c (revision 754) ++++ usr/util.c (revision 779) +@@ -19,6 +19,7 @@ + #include "config.h" + #include "initiator.h" + #include "version.h" ++#include "iscsi_settings.h" + + void daemon_init(void) + { +@@ -170,11 +171,11 @@ + rec->session.auth.password_in_length = 0; + rec->session.err_timeo.abort_timeout = 10; + rec->session.err_timeo.reset_timeout = 30; +- rec->session.timeo.replacement_timeout = 120; ++ rec->session.timeo.replacement_timeout = DEF_REPLACEMENT_TIMEO; + rec->session.iscsi.InitialR2T = 0; + rec->session.iscsi.ImmediateData = 1; +- rec->session.iscsi.FirstBurstLength = 256 * 1024; +- rec->session.iscsi.MaxBurstLength = (16 * 1024 * 1024) - 1024; ++ rec->session.iscsi.FirstBurstLength = DEF_INI_FIRST_BURST_LEN; ++ rec->session.iscsi.MaxBurstLength = DEF_INI_MAX_BURST_LEN; + rec->session.iscsi.DefaultTime2Wait = 0; + rec->session.iscsi.DefaultTime2Retain = 0; + rec->session.iscsi.MaxConnections = 1; +@@ -186,17 +187,18 @@ + rec->conn[i].port = DEF_ISCSI_PORT; + rec->conn[i].tcp.window_size = 512 * 1024; + rec->conn[i].tcp.type_of_service = 0; +- rec->conn[i].timeo.login_timeout=15; +- rec->conn[i].timeo.logout_timeout=15; ++ rec->conn[i].timeo.login_timeout= DEF_LOGIN_TIMEO; ++ rec->conn[i].timeo.logout_timeout= DEF_LOGOUT_TIMEO; + rec->conn[i].timeo.auth_timeout = 45; + rec->conn[i].timeo.active_timeout=5; + rec->conn[i].timeo.idle_timeout = 60; + rec->conn[i].timeo.ping_timeout = 5; + +- rec->conn[i].timeo.noop_out_interval = 0; +- rec->conn[i].timeo.noop_out_timeout = 0; ++ rec->conn[i].timeo.noop_out_interval = DEF_NOOP_OUT_INTERVAL; ++ rec->conn[i].timeo.noop_out_timeout = DEF_NOOP_OUT_TIMEO; + +- rec->conn[i].iscsi.MaxRecvDataSegmentLength = 128 * 1024; ++ rec->conn[i].iscsi.MaxRecvDataSegmentLength = ++ DEF_INI_MAX_RECV_SEG_LEN; + rec->conn[i].iscsi.HeaderDigest = CONFIG_DIGEST_PREFER_OFF; + rec->conn[i].iscsi.DataDigest = CONFIG_DIGEST_NEVER; + rec->conn[i].iscsi.IFMarker = 0; +@@ -224,6 +226,8 @@ + /* 13 */ "daemon access denied", + /* 14 */ "iSCSI transport capability failure", + /* 15 */ "already exists", ++ /* 16 */ "Unknown request", ++ /* 17 */ "encountered iSNS failure", + }; + log_error("initiator reported error (%d - %s)", err, err_msgs[err]); + } +Index: usr/Makefile +=================================================================== +--- usr/Makefile (revision 754) ++++ usr/Makefile (revision 779) +@@ -34,11 +34,11 @@ + PROGRAMS = iscsid iscsiadm iscsistart + + # sources shared between iscsid, iscsiadm and iscsistart +-ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iscsi_sysfs.o ++ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iscsi_sysfs.o idbm.o + # sources shared between iscsid and iscsiadm +-COMMON_SRCS = $(ISCSI_LIB_SRCS) idbm.o ++COMMON_SRCS = $(ISCSI_LIB_SRCS) + # core initiator files +-INITIATOR_SRCS = initiator.o queue.o actor.o mgmt_ipc.o transport.o ++INITIATOR_SRCS = initiator.o queue.o actor.o mgmt_ipc.o isns.o transport.o + + all: $(PROGRAMS) + +Index: usr/initiator.c +=================================================================== +--- usr/initiator.c (revision 754) ++++ usr/initiator.c (revision 779) +@@ -45,6 +45,7 @@ + #include "log.h" + #include "util.h" + #include "iscsi_sysfs.h" ++#include "iscsi_settings.h" + + static void __session_mainloop(void *data); + static void __conn_error_handle(iscsi_session_t*, iscsi_conn_t*); +@@ -136,21 +137,6 @@ + set_device_online); + } + +-static void +-write_mgmt_rsp(queue_task_t *qtask, mgmt_ipc_err_e err) +-{ +- log_debug(4, "%s: rsp to fd %d", __FUNCTION__, +- qtask->u.login.mgmt_ipc_fd); +- if (qtask->u.login.mgmt_ipc_fd == 0) +- return; +- +- qtask->u.login.rsp.err = err; +- write(qtask->u.login.mgmt_ipc_fd, &qtask->u.login.rsp, +- sizeof(qtask->u.login.rsp)); +- close(qtask->u.login.mgmt_ipc_fd); +- free(qtask); +-} +- + static conn_login_status_e + __login_response_status(iscsi_conn_t *conn, + enum iscsi_login_status login_status) +@@ -367,24 +353,65 @@ + /* connection's timeouts */ + conn->id = cid; + conn->logout_timeout = conn_rec->timeo.logout_timeout; ++ if (!conn->logout_timeout) { ++ log_error("Invalid timeo.logout_timeout. Must be greater " ++ "than zero. Using default %d.\n", ++ DEF_LOGOUT_TIMEO); ++ conn->logout_timeout = DEF_LOGOUT_TIMEO; ++ } ++ + conn->login_timeout = conn_rec->timeo.login_timeout; ++ if (!conn->login_timeout) { ++ log_error("Invalid timeo.login_timeout. Must be greater " ++ "than zero. Using default %d.\n", ++ DEF_LOGIN_TIMEO); ++ conn->login_timeout = DEF_LOGIN_TIMEO; ++ } ++ ++ /* noop-out setting */ ++ conn->noop_out_interval = conn_rec->timeo.noop_out_interval; ++ conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout; ++ if (conn->noop_out_interval && !conn->noop_out_timeout) { ++ log_error("Invalid timeo.noop_out_timeout. Must be greater " ++ "than zero. Using default %d.\n", ++ DEF_NOOP_OUT_TIMEO); ++ conn->noop_out_timeout = DEF_NOOP_OUT_TIMEO; ++ } ++ ++ if (conn->noop_out_timeout && !conn->noop_out_interval) { ++ log_error("Invalid timeo.noop_out_interval. Must be greater " ++ "than zero. Using default %d.\n", ++ DEF_NOOP_OUT_INTERVAL); ++ conn->noop_out_timeout = DEF_NOOP_OUT_INTERVAL; ++ } ++ ++ /* ++ * currently not used (leftover from linux-iscsi which we ++ * may do one day) ++ */ + conn->auth_timeout = conn_rec->timeo.auth_timeout; + conn->active_timeout = conn_rec->timeo.active_timeout; + conn->idle_timeout = conn_rec->timeo.idle_timeout; + conn->ping_timeout = conn_rec->timeo.ping_timeout; + +- /* noop-out setting */ +- conn->noop_out_interval = conn_rec->timeo.noop_out_interval; +- conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout; +- + /* operational parameters */ + conn->max_recv_dlength = + __padding(conn_rec->iscsi.MaxRecvDataSegmentLength); ++ if (conn->max_recv_dlength < ISCSI_MIN_MAX_RECV_SEG_LEN || ++ conn->max_recv_dlength > ISCSI_MAX_MAX_RECV_SEG_LEN) { ++ log_error("Invalid iscsi.MaxRecvDataSegmentLength. Must be " ++ "within %u and %u. Setting to %u\n", ++ ISCSI_MIN_MAX_RECV_SEG_LEN, ++ ISCSI_MAX_MAX_RECV_SEG_LEN, ++ DEF_INI_MAX_RECV_SEG_LEN); ++ conn->max_recv_dlength = DEF_INI_MAX_RECV_SEG_LEN; ++ } ++ + /* + * iSCSI default, unless declared otherwise by the + * target during login + */ +- conn->max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; ++ conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; + conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest; + conn->datadgst_en = conn_rec->iscsi.DataDigest; + +@@ -486,6 +513,7 @@ + session->ctrl_fd = control_fd; + session->transport_handle = provider->handle; + session->provider = provider; ++ session->reopen_qtask.mgmt_ipc_fd = -1; + + /* save node record. we might need it for redirection */ + memcpy(&session->nrec, rec, sizeof(node_rec_t)); +@@ -506,7 +534,27 @@ + session->initial_r2t_en = rec->session.iscsi.InitialR2T; + session->imm_data_en = rec->session.iscsi.ImmediateData; + session->first_burst = __padding(rec->session.iscsi.FirstBurstLength); ++ if (session->first_burst < ISCSI_MIN_FIRST_BURST_LEN || ++ session->first_burst > ISCSI_MAX_FIRST_BURST_LEN) { ++ log_error("Invalid iscsi.FirstBurstLength of %u. Must be " ++ "within %u and %u. Setting to %u\n", ++ session->first_burst, ++ ISCSI_MIN_FIRST_BURST_LEN, ++ ISCSI_MAX_FIRST_BURST_LEN, ++ DEF_INI_FIRST_BURST_LEN); ++ session->first_burst = DEF_INI_FIRST_BURST_LEN; ++ } ++ + session->max_burst = __padding(rec->session.iscsi.MaxBurstLength); ++ if (session->max_burst < ISCSI_MIN_MAX_BURST_LEN || ++ session->max_burst > ISCSI_MAX_MAX_BURST_LEN) { ++ log_error("Invalid iscsi.MaxBurstLength of %u. Must be " ++ "within %u and %u. Setting to %u\n", ++ session->max_burst, ISCSI_MIN_MAX_BURST_LEN, ++ ISCSI_MAX_MAX_BURST_LEN, DEF_INI_MAX_BURST_LEN); ++ session->max_burst = DEF_INI_MAX_BURST_LEN; ++ } ++ + session->def_time2wait = rec->session.iscsi.DefaultTime2Wait; + session->def_time2retain = rec->session.iscsi.DefaultTime2Retain; + session->erl = rec->session.iscsi.ERL; +@@ -523,7 +571,7 @@ + if (session->replacement_timeout == 0) { + log_error("Cannot set replacement_timeout to zero. Setting " + "120 seconds\n"); +- session->replacement_timeout = 120; ++ session->replacement_timeout = DEF_REPLACEMENT_TIMEO; + } + + /* OUI and uniqifying number */ +@@ -580,7 +628,7 @@ + iscsi_conn_t *conn = qtask->conn; + iscsi_session_t *session = conn->session; + +- write_mgmt_rsp(qtask, err); ++ mgmt_ipc_write_rsp(qtask, err); + session_conn_destroy(session, conn->id); + if (conn->id == 0) + __session_destroy(session); +@@ -668,7 +716,9 @@ + iscsi_conn_t *conn = (iscsi_conn_t*)data; + iscsi_session_t *session = conn->session; + +- log_debug(3, "noop out rsp timeout, closing conn...\n"); ++ log_warning("Nop-out timedout after %d seconds on connection %d:%d " ++ "state (%d). Dropping session.", conn->noop_out_timeout, ++ session->id, conn->id, conn->state); + /* XXX: error handle */ + __conn_error_handle(session, conn); + } +@@ -730,7 +780,7 @@ + * iSCSI default, unless declared otherwise by the + * target during login + */ +- conn->max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; ++ conn->max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; + conn->hdrdgst_en = conn_rec->iscsi.HeaderDigest; + conn->datadgst_en = conn_rec->iscsi.DataDigest; + +@@ -876,14 +926,14 @@ + + pid = scan_host(session); + if (pid == 0) { +- write_mgmt_rsp(qtask, MGMT_IPC_OK); ++ mgmt_ipc_write_rsp(qtask, MGMT_IPC_OK); + exit(0); + } else if (pid > 0) { +- close(qtask->u.login.mgmt_ipc_fd); ++ close(qtask->mgmt_ipc_fd); + need_reap(); + free(qtask); + } else +- write_mgmt_rsp(qtask, MGMT_IPC_ERR_INTERNAL); ++ mgmt_ipc_write_rsp(qtask, MGMT_IPC_ERR_INTERNAL); + } + + static void +@@ -1070,8 +1120,10 @@ + log_warning("connection%d:%d is operational now", + session->id, conn->id); + } else { ++ session->sync_qtask = NULL; ++ + __session_online_devs(session); +- write_mgmt_rsp(c->qtask, MGMT_IPC_OK); ++ mgmt_ipc_write_rsp(c->qtask, MGMT_IPC_OK); + log_warning("connection%d:%d is operational after recovery " + "(%d attempts)", session->id, conn->id, + session->reopen_cnt); +@@ -1240,7 +1292,7 @@ + case STATE_LOGOUT_REQUESTED: + /* read incomming PDU */ + if (!iscsi_io_recv_pdu(conn, &hdr, ISCSI_DIGEST_NONE, +- conn->data, DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH, ++ conn->data, ISCSI_DEF_MAX_RECV_SEG_LEN, + ISCSI_DIGEST_NONE, 0)) { + return; + } +@@ -1261,17 +1313,17 @@ + } + break; + case STATE_XPT_WAIT: +- recvpool_put(conn, conn->recv_handle); ++ recvpool_put(conn, (void *)conn->recv_handle); + log_debug(1, "ignoring incomming PDU in XPT_WAIT. " + "let connection re-establish or fail"); + break; + case STATE_CLEANUP_WAIT: +- recvpool_put(conn, conn->recv_handle); ++ recvpool_put(conn, (void *)conn->recv_handle); + log_debug(1, "ignoring incomming PDU in XPT_WAIT. " + "let connection cleanup"); + break; + default: +- recvpool_put(conn, conn->recv_handle); ++ recvpool_put(conn, (void *)conn->recv_handle); + log_error("Invalid state. Dropping PDU.\n"); + } + } +@@ -1544,8 +1596,14 @@ + break; + case STATE_IN_LOGIN: + if (session->r_stage == R_STAGE_SESSION_REOPEN) { +- session_conn_reopen(conn, &session->reopen_qtask, +- STOP_CONN_RECOVER); ++ queue_task_t *qtask; ++ ++ if (session->sync_qtask) ++ qtask = session->sync_qtask; ++ else ++ qtask = &session->reopen_qtask; ++ ++ session_conn_reopen(conn, qtask, STOP_CONN_RECOVER); + return; + } + +@@ -1579,7 +1637,7 @@ + iscsi_conn_t *conn = item->context; + iscsi_session_t *session = conn->session; + +- log_warning("detected iSCSI connection %d:%d error (%d) " ++ log_warning("Kernel reported iSCSI connection %d:%d error (%d) " + "state (%d)", session->id, conn->id, error, + conn->state); + __conn_error_handle(session, conn); +@@ -1820,8 +1878,8 @@ + actor_timer(&conn->connect_timer, conn->login_timeout*1000, + __connect_timedout, qtask); + +- qtask->u.login.rsp.command = MGMT_IPC_SESSION_LOGIN; +- qtask->u.login.rsp.err = MGMT_IPC_OK; ++ qtask->rsp.command = MGMT_IPC_SESSION_LOGIN; ++ qtask->rsp.err = MGMT_IPC_OK; + + return MGMT_IPC_OK; + } +@@ -1879,7 +1937,8 @@ + goto destroy_session; + } + +- qtask->u.login.rsp.command = MGMT_IPC_SESSION_SYNC; ++ session->sync_qtask = qtask; ++ qtask->rsp.command = MGMT_IPC_SESSION_SYNC; + + session_conn_reopen(&session->conn[0], qtask, STOP_CONN_RECOVER); + log_debug(3, "Started sync iSCSI session %d", session->id); +@@ -1898,9 +1957,10 @@ + mgmt_ipc_err_e rc = MGMT_IPC_OK; + + conn = &session->conn[0]; +- if (conn->state == STATE_XPT_WAIT && ++ if (session->sync_qtask || ++ (conn->state == STATE_XPT_WAIT && + (session->r_stage == R_STAGE_NO_CHANGE || +- session->r_stage == R_STAGE_SESSION_REDIRECT)) { ++ session->r_stage == R_STAGE_SESSION_REDIRECT))) { + log_error("session in invalid state for logout. " + "Try again later\n"); + return MGMT_IPC_ERR_INTERNAL; +@@ -1911,7 +1971,7 @@ + /* FIXME: implement Logout Request */ + + qtask->conn = conn; +- qtask->u.login.rsp.command = MGMT_IPC_SESSION_LOGOUT; ++ qtask->rsp.command = MGMT_IPC_SESSION_LOGOUT; + conn->logout_qtask = qtask; + + switch (conn->state) { +Index: usr/isns_proto.h +=================================================================== +--- usr/isns_proto.h (revision 0) ++++ usr/isns_proto.h (revision 779) +@@ -0,0 +1,200 @@ ++/* ++ * iSNS protocol data types ++ * ++ * Copyright (C) 2006 FUJITA Tomonori ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++ ++#ifndef ISNS_PROTO_H ++#define ISNS_PROTO_H ++ ++#define ISNS_PORT 3205 ++#define ISNS_ALIGN 4 ++ ++struct isns_hdr { ++ uint16_t version; ++ uint16_t function; ++ uint16_t length; ++ uint16_t flags; ++ uint16_t transaction; ++ uint16_t sequence; ++ uint32_t pdu[0]; ++} __attribute__ ((packed)); ++ ++struct isns_tlv { ++ uint32_t tag; ++ uint32_t length; ++ uint32_t value[0]; ++} __attribute__ ((packed)); ++ ++/* Commands and responses (4.1.3) */ ++#define ISNS_FUNC_DEV_ATTR_REG 0x0001 ++#define ISNS_FUNC_DEV_ATTR_QRY 0x0002 ++#define ISNS_FUNC_DEV_GET_NEXT 0x0003 ++#define ISNS_FUNC_DEV_DEREG 0x0004 ++#define ISNS_FUNC_SCN_REG 0x0005 ++#define ISNS_FUNC_SCN_DEREG 0x0006 ++#define ISNS_FUNC_SCN_EVENT 0x0007 ++#define ISNS_FUNC_SCN 0x0008 ++#define ISNS_FUNC_DD_REG 0x0009 ++#define ISNS_FUNC_DD_DEREG 0x000a ++#define ISNS_FUNC_DDS_REG 0x000b ++#define ISNS_FUNC_DDS_DEREG 0x000c ++#define ISNS_FUNC_ESI 0x000d ++#define ISNS_FUNC_HEARTBEAT 0x000e ++ ++#define ISNS_FUNC_DEV_ATTR_REG_RSP 0x8001 ++#define ISNS_FUNC_DEV_ATTR_QRY_RSP 0x8002 ++#define ISNS_FUNC_DEV_GET_NEXT_RSP 0x8003 ++#define ISNS_FUNC_DEV_DEREG_RSP 0x8004 ++#define ISNS_FUNC_SCN_REG_RSP 0x8005 ++#define ISNS_FUNC_SCN_DEREG_RSP 0x8006 ++#define ISNS_FUNC_SCN_EVENT_RSP 0x8007 ++#define ISNS_FUNC_SCN_RSP 0x8008 ++#define ISNS_FUNC_DD_REG_RSP 0x8009 ++#define ISNS_FUNC_DD_DEREG_RSP 0x800a ++#define ISNS_FUNC_DDS_REG_RSP 0x800b ++#define ISNS_FUNC_DDS_DEREG_RSP 0x800c ++#define ISNS_FUNC_ESI_RSP 0x800d ++ ++/* iSNSP flags (5.1.4) */ ++#define ISNS_FLAG_CLIENT (1U << 15) ++#define ISNS_FLAG_SERVER (1U << 14) ++#define ISNS_FLAG_AUTH (1U << 13) ++#define ISNS_FLAG_REPLACE (1U << 12) ++#define ISNS_FLAG_LAST_PDU (1U << 11) ++#define ISNS_FLAG_FIRST_PDU (1U << 10) ++ ++/* Response Status Codes (5.4) */ ++#define ISNS_STATUS_SUCCESS 0 ++#define ISNS_STATUS_UNKNOWN_ERROR 1 ++#define ISNS_STATUS_FORMAT_ERROR 2 ++#define ISNS_STATUS_INVALID_REGISTRATION 3 ++#define ISNS_STATUS_RESERVED 4 ++#define ISNS_STATUS_INVALID_QUERY 5 ++#define ISNS_STATUS_SOURCE_UNKNOWN 6 ++#define ISNS_STATUS_SOURCE_ABSENT 7 ++#define ISNS_STATUS_SOURCE_UNAUTHORIZED 8 ++#define ISNS_STATUS_NO_SUCH_ENTRY 9 ++#define ISNS_STATUS_VERSION_NOT_SUPPORTED 10 ++#define ISNS_STATUS_INTERNAL_ERROR 11 ++#define ISNS_STATUS_BUSY 12 ++#define ISNS_STATUS_OPTION_NOT_UNDERSTOOD 13 ++#define ISNS_STATUS_INVALID_UPDATE 14 ++#define ISNS_STATUS_MESSAGE_NOT_SUPPORTED 15 ++#define ISNS_STATUS_SCN_EVENT_REJECTED 16 ++#define ISNS_STATUS_SCN_REGISTRATION_REJECTED 17 ++#define ISNS_STATUS_ATTRIBUTE_NOT_IMPLEMENTED 18 ++#define ISNS_STATUS_FC_DOMAIN_ID_NOT_AVAILABLE 19 ++#define ISNS_STATUS_FC_DOMAIN_ID_NOT_ALLOCATED 20 ++#define ISNS_STATUS_ESI_NOT_AVAILABLE 21 ++#define ISNS_STATUS_INVALIDE_DEREGISTRATION 22 ++#define ISNS_STATUS_REGISTRATION_NOT_SUPPORTED 23 ++ ++/* Node type (5.4.2) */ ++#define ISNS_NODE_CONTROL (1U << 2) ++#define ISNS_NODE_INITIATOR (1U << 1) ++#define ISNS_NODE_TARGET (1U << 0) ++ ++/* Attributes (6.1) */ ++#define ISNS_ATTR_DELIMITER 0 ++#define ISNS_ATTR_ENTITY_IDENTIFIER 1 ++#define ISNS_ATTR_ENTITY_PROTOCOL 2 ++#define ISNS_ATTR_MANAGEMENT_IP_ADDRESS 3 ++#define ISNS_ATTR_TIMESTAMP 4 ++#define ISNS_ATTR_PROTOCOL_VERSION_RANGE 5 ++#define ISNS_ATTR_REGISTRATION_PERIOD 6 ++#define ISNS_ATTR_ENTITY_INDEX 7 ++#define ISNS_ATTR_ENTITY_NEXT_INDEX 8 ++#define ISNS_ATTR_ISAKMP_PHASE1 11 ++#define ISNS_ATTR_CERTIFICATE 12 ++#define ISNS_ATTR_PORTAL_IP_ADDRESS 16 ++#define ISNS_ATTR_PORTAL_PORT 17 ++#define ISNS_ATTR_PORTAL_SYMBOLIC_NAME 18 ++#define ISNS_ATTR_ESI_INTERVAL 19 ++#define ISNS_ATTR_ESI_PORT 20 ++#define ISNS_ATTR_PORTAL_INDEX 22 ++#define ISNS_ATTR_SCN_PORT 23 ++#define ISNS_ATTR_PORTAL_NEXT_INDEX 24 ++#define ISNS_ATTR_PORTAL_SECURITY_BITMAP 27 ++#define ISNS_ATTR_PORTAL_ISAKMP_PHASE1 28 ++#define ISNS_ATTR_PORTAL_ISAKMP_PHASE2 29 ++#define ISNS_ATTR_PORTAL_CERTIFICATE 31 ++#define ISNS_ATTR_ISCSI_NAME 32 ++#define ISNS_ATTR_ISCSI_NODE_TYPE 33 ++#define ISNS_ATTR_ISCSI_ALIAS 34 ++#define ISNS_ATTR_ISCSI_SCN_BITMAP 35 ++#define ISNS_ATTR_ISCSI_NODE_INDEX 36 ++#define ISNS_ATTR_WWNN_TOKEN 37 ++#define ISNS_ATTR_ISCSI_NODE_NEXT_INDEX 38 ++#define ISNS_ATTR_ISCSI_AUTHMETHOD 42 ++#define ISNS_ATTR_PG_ISCSI_NAME 48 ++#define ISNS_ATTR_PG_PORTAL_IP_ADDRESS 49 ++#define ISNS_ATTR_PG_PORTAL_PORT 50 ++#define ISNS_ATTR_PG_TAG 51 ++#define ISNS_ATTR_PG_INDEX 52 ++#define ISNS_ATTR_PG_NEXT_INDEX 53 ++#define ISNS_ATTR_FC_PORT_NAME_WWPN 64 ++#define ISNS_ATTR_PORT_ID 65 ++#define ISNS_ATTR_PORT_TYPE 66 ++#define ISNS_ATTR_SYMBOLIC_PORT_NAME 67 ++#define ISNS_ATTR_FABRIC_PORT_NAME 68 ++#define ISNS_ATTR_HARD_ADDRESS 69 ++#define ISNS_ATTR_PORT_IP_ADDRESS 70 ++#define ISNS_ATTR_CLASS_OF_SERVICE 71 ++#define ISNS_ATTR_FC4_TYPES 72 ++#define ISNS_ATTR_FC4_DESCRIPOTR 73 ++#define ISNS_ATTR_FC4_FEATURES 74 ++#define ISNS_ATTR_IFCP_SCN_BITMAP 75 ++#define ISNS_ATTR_PORT_ROLE 76 ++#define ISNS_ATTR_PERMANENT_PORT_NAME 77 ++#define ISNS_ATTR_FC4_TYPE_CODE 95 ++#define ISNS_ATTR_FC_NODE_NAME_WWNN 96 ++#define ISNS_ATTR_SYMBOLIC_NODE_NAME 97 ++#define ISNS_ATTR_NODE_IP_ADDRESS 98 ++#define ISNS_ATTR_NODE_IPA 99 ++#define ISNS_ATTR_PORXY_ISCSI_NAME 101 ++#define ISNS_ATTR_SWITCH_NAME 128 ++#define ISNS_ATTR_PREFERRED_ID 129 ++#define ISNS_ATTR_ASSIGNED_ID 130 ++#define ISNS_ATTR_VIRTUAL_FABRIC_ID 131 ++#define ISNS_ATTR_ISNS_SERVER_VENDOR_OUI 256 ++#define ISNS_ATTR_DD_SET_ID 2049 ++#define ISNS_ATTR_DD_SET_SYM_NAME 2050 ++#define ISNS_ATTR_DD_SET_STATUS 2051 ++#define ISNS_ATTR_DD_SET_NEXT_ID 2052 ++#define ISNS_ATTR_DD_ID 2065 ++#define ISNS_ATTR_DD_SYMBOLIC_NAME 2066 ++#define ISNS_ATTR_DD_MEMBER_ISCSI_INDEX 2067 ++#define ISNS_ATTR_DD_MEMBER_ISCSI_NAME 2068 ++#define ISNS_ATTR_DD_MEMBER_FC_PORT_NAME 2069 ++#define ISNS_ATTR_DD_MEMBER_PORTAL_INDEX 2070 ++#define ISNS_ATTR_DD_MEMBER_IP_ADDR 2071 ++#define ISNS_ATTR_DD_MEMBER_TCP_UDP 2072 ++#define ISNS_ATTR_DD_FEATURES 2078 ++#define ISNS_ATTR_DD_ID_NEXT_ID 2079 ++ ++/* SCN flags (6.4.4) */ ++#define ISNS_SCN_FLAG_INITIATOR (1U << 24) ++#define ISNS_SCN_FLAG_TARGET (1U << 25) ++#define ISNS_SCN_FLAG_MANAGEMENT (1U << 26) ++#define ISNS_SCN_FLAG_OBJECT_REMOVE (1U << 27) ++#define ISNS_SCN_FLAG_OBJECT_ADDED (1U << 28) ++#define ISNS_SCN_FLAG_OBJECT_UPDATED (1U << 29) ++#define ISNS_SCN_FLAG_DD_REMOVED (1U << 30) ++#define ISNS_SCN_FLAG_DD_ADDED (1U << 31) ++#endif +Index: usr/netlink.c +=================================================================== +--- usr/netlink.c (revision 754) ++++ usr/netlink.c (revision 779) +@@ -52,11 +52,11 @@ + static int ctldev_handle(void); + + #define NLM_BUF_DEFAULT_MAX \ +- (NLMSG_SPACE(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + \ ++ (NLMSG_SPACE(ISCSI_DEF_MAX_RECV_SEG_LEN + \ + sizeof(struct iscsi_hdr))) + + #define PDU_SENDBUF_DEFAULT_MAX \ +- (DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH + sizeof(struct iscsi_hdr)) ++ (ISCSI_DEF_MAX_RECV_SEG_LEN + sizeof(struct iscsi_hdr)) + + #define NLM_SETPARAM_DEFAULT_MAX \ + (NI_MAXHOST + 1 + sizeof(struct iscsi_uevent)) +Index: usr/discovery.c +=================================================================== +--- usr/discovery.c (revision 754) ++++ usr/discovery.c (revision 779) +@@ -667,8 +667,8 @@ + config->send_async_text : -1; + session->conn[0].hdrdgst_en = ISCSI_DIGEST_NONE; + session->conn[0].datadgst_en = ISCSI_DIGEST_NONE; +- session->conn[0].max_recv_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; +- session->conn[0].max_xmit_dlength = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH; ++ session->conn[0].max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; ++ session->conn[0].max_xmit_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN; + + session->reopen_cnt = config->reopen_max; + +Index: usr/initiator.h +=================================================================== +--- usr/initiator.h (revision 754) ++++ usr/initiator.h (revision 779) +@@ -120,7 +120,7 @@ + struct iscsi_session *session; + iscsi_login_context_t login_context; + struct queue_task *logout_qtask; +- char data[DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH]; ++ char data[ISCSI_DEF_MAX_RECV_SEG_LEN]; + char host[NI_MAXHOST]; /* scratch */ + iscsi_conn_state_e state; + actor_t connect_timer; +@@ -186,22 +186,9 @@ + + typedef struct queue_task { + iscsi_conn_t *conn; +- union { +- /* iSCSI requests originated via IPC */ +- struct ipcreq_login { +- iscsiadm_req_t req; +- iscsiadm_rsp_t rsp; +- int mgmt_ipc_fd; +- } login; +- struct ipcreq_logout { +- iscsiadm_req_t req; +- iscsiadm_rsp_t rsp; +- int mgmt_ipc_fd; +- } logout; +- /* iSCSI requests originated via CTL */ +- struct ctlreq_recv_pdu { +- } recv_pdu; +- } u; ++ iscsiadm_req_t req; ++ iscsiadm_rsp_t rsp; ++ int mgmt_ipc_fd; + } queue_task_t; + + typedef enum iscsi_provider_status_e { +@@ -283,6 +270,9 @@ + iscsi_session_r_stage_e r_stage; + uint32_t replacement_timeout; + ++ /* sync up fields */ ++ queue_task_t *sync_qtask; ++ + /* session's processing */ + actor_t mainloop; + queue_t *queue; +@@ -368,4 +358,10 @@ + extern mgmt_ipc_err_e iscsi_sync_session(node_rec_t *rec, queue_task_t + *tsk, uint32_t sid); + ++/* isns.c */ ++extern int isns_init(void); ++extern void isns_handle(int); ++extern void isns_exit(void); ++extern int isns_dev_attr_query_task(queue_task_t *qtask); ++ + #endif /* INITIATOR_H */ +Index: usr/login.c +=================================================================== +--- usr/login.c (revision 754) ++++ usr/login.c (revision 779) +@@ -1079,7 +1079,7 @@ + if ((conn->hdrdgst_en != ISCSI_DIGEST_NONE) || + (conn->datadgst_en != ISCSI_DIGEST_NONE) || + (conn->max_recv_dlength != +- DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH)) ++ ISCSI_DEF_MAX_RECV_SEG_LEN)) + conn->next_stage = + ISCSI_OP_PARMS_NEGOTIATION_STAGE; + else +Index: usr/isns.c +=================================================================== +--- usr/isns.c (revision 0) ++++ usr/isns.c (revision 779) +@@ -0,0 +1,794 @@ ++/* ++ * iSNS functions ++ * ++ * Copyright (C) 2006 FUJITA Tomonori ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation; either version 2 of the ++ * License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA ++ * 02110-1301 USA ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "initiator.h" ++#include "idbm.h" ++#include "log.h" ++#include "util.h" ++#include "isns_proto.h" ++ ++enum isns_task_state { ++ ISNS_TASK_WAIT_CONN, ++ ISNS_TASK_SEND_PDU, ++ ISNS_TASK_RECV_PDU, ++}; ++ ++struct isns_task { ++ int state; ++ int fd; ++ int len; ++ char data[ISCSI_DEF_MAX_RECV_SEG_LEN]; ++ int transaction; ++ int done; ++ int retry; ++ queue_task_t *qtask; ++}; ++ ++static actor_t isns_actor; ++static queue_t *isns_queue = NULL; ++static struct sockaddr_storage ss; ++static uint16_t transaction; ++ ++static char isns_address[NI_MAXHOST]; ++static int isns_port = 3205, isns_listen_port, max_retry = 10000; ++ ++#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) ++ ++#define get_hdr_param(hdr, function, length, flags, transaction, sequence) \ ++{ \ ++ function = ntohs(hdr->function); \ ++ length = ntohs(hdr->length); \ ++ flags = ntohs(hdr->flags); \ ++ transaction = ntohs(hdr->transaction); \ ++ sequence = ntohs(hdr->sequence); \ ++} ++ ++/* use io.c */ ++static int set_non_blocking(int fd) ++{ ++ int res = fcntl(fd, F_GETFL); ++ ++ if (res == -1) ++ log_warning("unable to get fd flags %m"); ++ else { ++ res = fcntl(fd, F_SETFL, res | O_NONBLOCK); ++ if (res) ++ log_warning("unable to set fd flags %m"); ++ } ++ ++ return res; ++} ++ ++static void ++isns_hdr_init(struct isns_hdr *hdr, uint16_t function, uint16_t length, ++ uint16_t flags, uint16_t trans, uint16_t sequence) ++{ ++ hdr->version = htons(0x0001); ++ hdr->function = htons(function); ++ hdr->length = htons(length); ++ hdr->flags = htons(flags); ++ hdr->transaction = htons(trans); ++ hdr->sequence = htons(sequence); ++} ++ ++static int ++isns_tlv_set(struct isns_tlv **tlv, uint32_t tag, uint32_t length, void *value) ++{ ++ if (length) ++ memcpy((*tlv)->value, value, length); ++ if (length % ISNS_ALIGN) ++ length += (ISNS_ALIGN - (length % ISNS_ALIGN)); ++ ++ (*tlv)->tag = htonl(tag); ++ (*tlv)->length = htonl(length); ++ ++ length += sizeof(struct isns_tlv); ++ *tlv = (struct isns_tlv *) ((char *) *tlv + length); ++ ++ return length; ++} ++ ++static void build_dev_reg_req(struct isns_task *task) ++{ ++ struct isns_hdr *hdr = (struct isns_hdr *) task->data; ++ struct isns_tlv *tlv = (struct isns_tlv *) hdr->pdu; ++ struct sockaddr_storage lss; ++ static uint8_t ip[16]; ++ char eid[NI_MAXHOST]; ++ char *name = dconfig->initiator_name; ++ char *alias = dconfig->initiator_alias; ++ socklen_t slen = sizeof(lss); ++ int i; ++ uint16_t flags = 0, length = 0; ++ uint32_t addr; ++ uint32_t port; ++ uint32_t node = htonl(ISNS_NODE_INITIATOR); ++ uint32_t type = htonl(2); ++ ++ memset(hdr, 0, sizeof(task->data)); ++ ++ getsockname(task->fd, (struct sockaddr *) &lss, &slen); ++ getnameinfo((struct sockaddr *) &lss, sizeof(lss), eid, sizeof(eid), ++ NULL, 0, 0); ++ ++ switch (lss.ss_family) { ++ case AF_INET: ++ addr = (((struct sockaddr_in *) &lss)->sin_addr.s_addr); ++ ++ ip[10] = ip[11] = 0xff; ++ ip[15] = 0xff & (addr >> 24); ++ ip[14] = 0xff & (addr >> 16); ++ ip[13] = 0xff & (addr >> 8); ++ ip[12] = 0xff & addr; ++ port = ((struct sockaddr_in *) &lss)->sin_port; ++ break; ++ case AF_INET6: ++ for (i = 0; i < ARRAY_SIZE(ip); i++) ++ ip[i] = ((struct sockaddr_in6 *) &lss)->sin6_addr.s6_addr[i]; ++ break; ++ port = ((struct sockaddr_in6 *) &lss)->sin6_port; ++ } ++ ++ port = htonl(ntohs(port)); ++ ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER, ++ strlen(eid), eid); ++ length += isns_tlv_set(&tlv, 0, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_IDENTIFIER, ++ strlen(eid), eid); ++ ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ENTITY_PROTOCOL, ++ sizeof(type), &type); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS, ++ sizeof(ip), &ip); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_PORT, ++ sizeof(port), &port); ++ flags = ISNS_FLAG_REPLACE; ++ ++ port = htonl(isns_listen_port); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ESI_PORT, ++ sizeof(port), &port); ++ ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, ++ sizeof(node), &node); ++ if(alias) ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_ALIAS, ++ strlen(alias), alias); ++ ++ flags |= ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; ++ task->transaction = ++transaction; ++ isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_REG, length, flags, ++ task->transaction, 0); ++ ++ task->len = length + sizeof(*hdr); ++} ++ ++static int isns_connect(void) ++{ ++ int err; ++ int fd; ++ ++ fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); ++ if (fd < 0) { ++ log_error("can't create socket %m"); ++ return -errno; ++ } ++ ++ err = set_non_blocking(fd); ++ if (err) { ++ log_error("can't set non-blocking %m"); ++ close(fd); ++ return -errno; ++ } ++ ++ err = connect(fd, (struct sockaddr *) &ss, sizeof(ss)); ++ if (err && errno != EINPROGRESS) { ++ log_error("can't connect %m"); ++ close(fd); ++ return -errno; ++ } ++ return fd; ++} ++ ++static int isns_send_pdu(struct isns_task *task) ++{ ++ int err; ++ ++ err = write(task->fd, task->data + task->done, task->len - task->done); ++ if (err < 0) { ++ if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) { ++ log_error("send fail %m"); ++ return -1; ++ } ++ } else ++ task->done += err; ++ ++ return 0; ++} ++static void isns_free_task(struct isns_task *task) ++{ ++ close(task->fd); ++ free(task); ++} ++ ++static int isns_recv_pdu(struct isns_task *task) ++{ ++ struct isns_hdr *hdr = (struct isns_hdr *) task->data; ++ uint16_t function, length, flags, transaction, sequence; ++ int err, size; ++ ++ if (task->done < sizeof(*hdr)) ++ size = sizeof(*hdr) - task->done; ++ else ++ size = task->len + sizeof(*hdr) - task->done; ++ ++ err = read(task->fd, task->data + task->done, size); ++ if (err <= 0) { ++ if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) { ++ log_error("send fail %m"); ++ return -1; ++ } ++ } else { ++ task->done += err; ++ ++ if (task->done == sizeof(*hdr)) { ++ get_hdr_param(hdr, function, length, flags, transaction, ++ sequence); ++ task->len = length; ++ } ++ } ++ return 0; ++} ++ ++static void add_new_target_node(char *targetname, uint8_t *ip, int port, ++ int tag) ++{ ++ int err; ++ node_rec_t rec; ++ idbm_t *db; ++ char dst[INET6_ADDRSTRLEN]; ++ ++ memset(dst, 0, sizeof(dst)); ++ if (!memcmp(ip, dst, 10) && ip[10] == 0xff && ip[11] == 0xff) ++ inet_ntop(AF_INET, ip + 12, dst, sizeof(dst)); ++ else ++ inet_ntop(AF_INET6, ip, dst, sizeof(dst)); ++ ++ log_debug(1, "add a new target node:%s %s,%d %d", ++ targetname, dst, port, tag); ++ ++ db = idbm_init(dconfig->config_file); ++ ++ idbm_node_setup_defaults(&rec); ++ strncpy(rec.name, targetname, TARGET_NAME_MAXLEN); ++ rec.conn[0].port = port; ++ rec.tpgt = tag; ++ strncpy(rec.conn[0].address, dst, NI_MAXHOST); ++ err = idbm_new_node(db, &rec); ++ if (err) ++ log_error("Could not add new target node:%s %s,%d", ++ targetname, dst, port); ++ ++ idbm_terminate(db); ++} ++ ++static int qry_rsp_handle(struct isns_hdr *hdr) ++{ ++ struct isns_tlv *tlv; ++ uint16_t function, length, flags, transaction, sequence; ++ uint32_t port, tag, status; ++ uint8_t *addr; ++ char *name; ++ ++ get_hdr_param(hdr, function, length, flags, transaction, sequence); ++ ++ status = (uint32_t) (*hdr->pdu); ++ if (status) ++ return status; ++ ++ /* skip status */ ++ tlv = (struct isns_tlv *) ((char *) hdr->pdu + 4); ++ length -= 4; ++ ++ /* check node type in the message key*/ ++ if ((ntohl(tlv->tag) != ISNS_ATTR_ISCSI_NODE_TYPE) || ++ ntohl(*(tlv->value)) != ISNS_NODE_TARGET) ++ return EINVAL; ++ ++ /* 12 + 8 bytes */ ++ length -= (sizeof(*tlv) + 4 + 8); ++ if (length <= 0) { ++ log_error("No target found."); ++ return EINVAL; ++ } ++ ++ tlv = (struct isns_tlv *) ((char *) tlv + 20); ++ ++ name = NULL; ++ addr = NULL; ++ port = tag = 0; ++ ++ /* FIXME: this assume the exact order. */ ++ while (length) { ++ uint32_t vlen = ntohl(tlv->length); ++ ++ switch (ntohl(tlv->tag)) { ++ case ISNS_ATTR_PG_ISCSI_NAME: ++ if (name && addr) { ++ add_new_target_node(name, addr, port, tag); ++ name = NULL; ++ addr = NULL; ++ } ++ name = (char *) tlv->value; ++ break; ++ case ISNS_ATTR_ISCSI_NODE_TYPE: ++ if (ntohl(*(tlv->value)) != ISNS_NODE_TARGET) ++ name = NULL; ++ break; ++ case ISNS_ATTR_PG_PORTAL_IP_ADDRESS: ++ addr = (uint8_t *) tlv->value; ++ break; ++ case ISNS_ATTR_PG_PORTAL_PORT: ++ port = ntohl(tlv->value[0]); ++ break; ++ case ISNS_ATTR_PG_TAG: ++ tag = ntohl(tlv->value[0]); ++ break; ++ case ISNS_ATTR_ISCSI_NAME: ++ case ISNS_ATTR_PORTAL_IP_ADDRESS: ++ case ISNS_ATTR_PORTAL_PORT: ++ break; ++ default: ++ log_error("unexpected type %d", ntohl(tlv->tag)); ++ break; ++ } ++ ++ length -= (sizeof(*tlv) + vlen); ++ tlv = (struct isns_tlv *) ((char *) tlv->value + vlen); ++ } ++ ++ if (name && addr) ++ add_new_target_node(name, addr, port, tag); ++ ++ return 0; ++} ++ ++static void send_mgmt_rsp(struct isns_task *task, int err) ++{ ++ mgmt_ipc_write_rsp(task->qtask, ++ err ? MGMT_IPC_ERR_ISNS_UNAVAILABLE : MGMT_IPC_OK); ++} ++ ++static int isns_task_done(struct isns_task *task) ++{ ++ struct isns_hdr *hdr = (struct isns_hdr *) task->data; ++ uint16_t function, length, flags, transaction, sequence; ++ uint32_t status = (uint32_t) (*hdr->pdu); ++ char *payload = (char *) hdr + sizeof(*hdr); ++ int finished = 1; ++ ++ get_hdr_param(hdr, function, length, flags, transaction, ++ sequence); ++ ++ if (function & 0x8000 && status) ++ log_error("error isns response %x %x", function, status); ++ ++ switch (function) { ++ case ISNS_FUNC_DEV_ATTR_REG_RSP: ++ break; ++ case ISNS_FUNC_DEV_ATTR_QRY_RSP: ++ if (!status) ++ qry_rsp_handle((struct isns_hdr *)task->data); ++ send_mgmt_rsp(task, status); ++ break; ++ case ISNS_FUNC_ESI: ++ memmove(payload + 4, payload, length); ++ *((uint32_t *) payload) = 0; ++ ++ length += 4; ++ flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ++ ISNS_FLAG_FIRST_PDU; ++ isns_hdr_init(hdr, ISNS_FUNC_ESI_RSP, length, flags, ++ transaction, 0); ++ task->state = ISNS_TASK_SEND_PDU; ++ task->len = length + sizeof(*hdr); ++ task->done = 0; ++ ++ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); ++ actor_schedule(&isns_actor); ++ finished = 0; ++ break; ++ default: ++ log_error("unexpected function %d", function); ++ break; ++ } ++ ++ return finished; ++} ++ ++int isns_dev_attr_query_task(queue_task_t *qtask) ++{ ++ int fd; ++ struct isns_hdr *hdr; ++ struct isns_tlv *tlv; ++ char *name = dconfig->initiator_name; ++ uint16_t flags, length = 0; ++ uint32_t node = htonl(ISNS_NODE_TARGET); ++ struct isns_task *task; ++ ++ if (!strlen(isns_address)) ++ return MGMT_IPC_ERR_ISNS_UNAVAILABLE; ++ ++ fd = isns_connect(); ++ if (fd < 0) { ++ log_error("%s %m", __FUNCTION__); ++ return MGMT_IPC_ERR_ISNS_UNAVAILABLE; ++ } ++ ++ task = malloc(sizeof(*task)); ++ if (!task) { ++ log_error("%s %m", __FUNCTION__); ++ close(fd); ++ return MGMT_IPC_ERR_NOMEM; ++ } ++ memset(task, 0, sizeof(*task)); ++ ++ task->qtask = qtask; ++ task->fd = fd; ++ ++ hdr = (struct isns_hdr *) task->data; ++ tlv = (struct isns_tlv *) hdr->pdu; ++ ++ memset(hdr, 0, sizeof(task->data)); ++ ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, strlen(name), name); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, ++ sizeof(node), &node); ++ length += isns_tlv_set(&tlv, 0, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NAME, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_ISCSI_NODE_TYPE, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_IP_ADDRESS, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PORTAL_PORT, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_ISCSI_NAME, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_PORTAL_IP_ADDRESS, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_PORTAL_PORT, 0, 0); ++ length += isns_tlv_set(&tlv, ISNS_ATTR_PG_TAG, 0, 0); ++ ++ flags = ISNS_FLAG_CLIENT | ISNS_FLAG_LAST_PDU | ISNS_FLAG_FIRST_PDU; ++ task->transaction = ++transaction; ++ isns_hdr_init(hdr, ISNS_FUNC_DEV_ATTR_QRY, length, flags, ++ task->transaction, 0); ++ ++ task->len = length + sizeof(*hdr); ++ task->state = ISNS_TASK_SEND_PDU; ++ ++ qtask->rsp.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY; ++ ++ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); ++ actor_schedule(&isns_actor); ++ ++ return MGMT_IPC_OK; ++} ++ ++void isns_handle(int listen_fd) ++{ ++ struct sockaddr_storage from; ++ socklen_t slen = sizeof(from); ++ int fd; ++ struct isns_task *task; ++ ++ fd = accept(listen_fd, (struct sockaddr *) &from, &slen); ++ if (fd < 0) { ++ log_error("%s: accept error %m", __FUNCTION__); ++ return; ++ } ++ ++ task = malloc(sizeof(*task)); ++ if (!task) { ++ log_error("%s %m", __FUNCTION__); ++ close(fd); ++ return; ++ } ++ ++ memset(task, 0, sizeof(*task)); ++ task->state = ISNS_TASK_RECV_PDU; ++ task->fd = fd; ++ ++ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); ++ actor_schedule(&isns_actor); ++} ++ ++static void isns_poll(queue_item_t *item) ++{ ++ int err, finished; ++ struct pollfd pfd; ++ struct isns_task *task = item->context; ++ struct isns_hdr *hdr = (struct isns_hdr *) task->data; ++ uint16_t function = ntohs(hdr->function); ++ ++ pfd.fd = task->fd; ++ switch (task->state) { ++ case ISNS_TASK_WAIT_CONN: ++ case ISNS_TASK_SEND_PDU: ++ pfd.events = POLLOUT; ++ break; ++ case ISNS_TASK_RECV_PDU: ++ pfd.events = POLLIN; ++ } ++ ++ err = poll(&pfd, 1, 1); ++ if (err > 0) { ++ switch (task->state) { ++ case ISNS_TASK_WAIT_CONN: ++ task->state = ISNS_TASK_SEND_PDU; ++ case ISNS_TASK_SEND_PDU: ++ err = isns_send_pdu(task); ++ if (err) ++ goto abort_task; ++ else { ++ ++ if (task->done == task->len) { ++ task->state = ISNS_TASK_RECV_PDU; ++ task->done = task->len = 0; ++ ++ if (function == ISNS_FUNC_ESI_RSP) ++ goto free_task; ++ } ++ ++ queue_produce(isns_queue, EV_CONN_POLL, task, 0, ++ NULL); ++ actor_schedule(&isns_actor); ++ } ++ break; ++ case ISNS_TASK_RECV_PDU: ++ err = isns_recv_pdu(task); ++ if (err) ++ goto abort_task; ++ else { ++ if (task->done == ++ task->len + sizeof(struct isns_hdr)) { ++ finished = isns_task_done(task); ++ if (finished) ++ goto free_task; ++ } else { ++ /* need to read more */ ++ queue_produce(isns_queue, EV_CONN_POLL, ++ task, 0, NULL); ++ actor_schedule(&isns_actor); ++ } ++ } ++ } ++ } else if (!err) { ++ /* FIXME */ ++ if (task->retry++ > max_retry) { ++ log_error("abort task"); ++ goto abort_task; ++ } else { ++ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); ++ actor_schedule(&isns_actor); ++ } ++ } ++ ++ return; ++abort_task: ++ if (task->qtask) ++ send_mgmt_rsp(task, 1); ++free_task: ++ isns_free_task(task); ++} ++ ++static void isns_control(void *data) ++{ ++ int count = isns_queue->count, i; ++ int err; ++ unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX]; ++ queue_item_t *item = (queue_item_t *)(void *)item_buf; ++ ++ for (i = 0; i < count; i++) { ++ err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item); ++ if (err == QUEUE_IS_EMPTY) { ++ log_debug(4, "%d items flushed while mainloop " ++ "was processing", count - i); ++ break; ++ } ++ ++ switch (item->event_type) { ++ case EV_CONN_POLL: ++ isns_poll(item); ++ break; ++ default: ++ log_error("%d unknown event type", item->event_type); ++ break; ++ } ++ } ++} ++ ++static int isns_dev_register(void) ++{ ++ struct isns_task *task; ++ ++ task = malloc(sizeof(*task)); ++ if (!task) ++ return -ENOMEM; ++ memset(task, 0, sizeof(*task)); ++ ++ task->fd = isns_connect(); ++ if (task->fd < 0) { ++ free(task); ++ return -ENOMEM; ++ } ++ ++ task->state = ISNS_TASK_WAIT_CONN; ++ build_dev_reg_req(task); ++ queue_produce(isns_queue, EV_CONN_POLL, task, 0, NULL); ++ ++ actor_new(&isns_actor, isns_control, NULL); ++ actor_schedule(&isns_actor); ++ ++ return 0; ++} ++ ++static int isns_listen_init(int *listen_fd) ++{ ++ int fd, opt, err; ++ struct sockaddr_storage lss; ++ socklen_t slen; ++ ++ fd = socket(ss.ss_family, SOCK_STREAM, IPPROTO_TCP); ++ if (fd < 0) { ++ log_error("%s %m", __FUNCTION__); ++ return -errno; ++ } ++ ++ opt = 1; ++ if (ss.ss_family == AF_INET6) { ++ err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, ++ sizeof(opt)); ++ if (err) ++ log_error("%s %m", __FUNCTION__); ++ goto out; ++ } ++ ++ err = listen(fd, 5); ++ if (err) { ++ log_error("%s %m", __FUNCTION__); ++ goto out; ++ } ++ ++ slen = sizeof(lss); ++ err = getsockname(fd, (struct sockaddr *) &lss, &slen); ++ if (err) { ++ log_error("%s %m", __FUNCTION__); ++ goto out; ++ } ++ ++ if (lss.ss_family == AF_INET6) ++ isns_listen_port = ((struct sockaddr_in6 *) &lss)->sin6_port; ++ else ++ isns_listen_port = ((struct sockaddr_in *) &lss)->sin_port; ++ ++ isns_listen_port = ntohs(isns_listen_port); ++out: ++ if (err) { ++ close(fd); ++ return -1; ++ } else { ++ *listen_fd = fd; ++ return 0; ++ } ++} ++ ++int isns_init(void) ++{ ++ char buf[2048], port[NI_MAXSERV]; ++ int fd = -1, err; ++ FILE *f; ++ ++ f = fopen(dconfig->config_file, "r"); ++ if (!f) ++ return -EIO; ++ ++ while (fgets(buf, sizeof(buf), f)) { ++ /* FIXME */ ++ if (buf[strlen(buf) - 1] == '\n') ++ buf[strlen(buf) - 1] = '\0'; ++ if (!strncmp(buf, "isns.address = ", 15)) ++ strncpy(isns_address, buf + 15, sizeof(isns_address)); ++ else if (!strncmp(buf, "isns.port = ", 12)) ++ isns_port = atoi(buf + 12); ++ } ++ ++ fclose(f); ++ ++ if (!strlen(isns_address)) ++ return -1; ++ ++ isns_queue = queue_create(4, 4, NULL, NULL); ++ if (!isns_queue) { ++ log_error("can't create queue %m"); ++ return -ENOMEM; ++ } ++ ++ snprintf(port, sizeof(port), "%d", isns_port); ++ err = resolve_address(isns_address, port, &ss); ++ if (err) { ++ log_error("can't resolve address %m, %s", isns_address); ++ goto free_queue; ++ } ++ ++ err = isns_listen_init(&fd); ++ if (err) ++ goto free_queue; ++ ++ isns_dev_register(); ++ return fd; ++ ++free_queue: ++ queue_destroy(isns_queue); ++ return err; ++} ++ ++void isns_exit(void) ++{ ++ int err, count, i; ++ unsigned char item_buf[sizeof(queue_item_t) + EVENT_PAYLOAD_MAX]; ++ queue_item_t *item = (queue_item_t *)(void *)item_buf; ++ ++ if (!isns_queue) ++ return; ++ ++ count = isns_queue->count; ++ /* ++ * TODO: Add some code to gracefully shutdown. ++ */ ++ for (i = 0; i < count; i++) { ++ err = queue_consume(isns_queue, EVENT_PAYLOAD_MAX, item); ++ if (err == QUEUE_IS_EMPTY) { ++ log_debug(4, "%d items flushed while mainloop " ++ "was processing", count - i); ++ break; ++ } ++ ++ log_debug(4, "Dropping event type %d\n", item->event_type); ++ switch (item->event_type) { ++ case EV_CONN_POLL: ++ isns_free_task(item->context); ++ continue; ++ default: ++ log_error("%d unknown event type", item->event_type); ++ continue; ++ } ++ } ++ ++ queue_destroy(isns_queue); ++} +Index: usr/iscsid.c +=================================================================== +--- usr/iscsid.c (revision 754) ++++ usr/iscsid.c (revision 779) +@@ -154,6 +154,7 @@ + char *initiatorname_file = INITIATOR_NAME_FILE; + char *pid_file = PID_FILE; + int ch, longindex; ++ int isns_fd; + uid_t uid = 0; + gid_t gid = 0; + struct sigaction sa_old; +@@ -318,7 +319,9 @@ + } + + actor_init(); +- event_loop(ipc, control_fd, mgmt_ipc_fd); ++ isns_fd = isns_init(); ++ event_loop(ipc, control_fd, mgmt_ipc_fd, isns_fd); ++ isns_exit(); + log_debug(1, "daemon stopping"); + return 0; + } +Index: usr/iscsistart.c +=================================================================== +--- usr/iscsistart.c (revision 754) ++++ usr/iscsistart.c (revision 779) +@@ -328,7 +328,7 @@ + * Start Main Event Loop + */ + actor_init(); +- event_loop(ipc, control_fd, mgmt_ipc_fd); ++ event_loop(ipc, control_fd, mgmt_ipc_fd, -1); + ipc->ctldev_close(); + mgmt_ipc_close(mgmt_ipc_fd); + +Index: usr/iscsiadm.c +=================================================================== +--- usr/iscsiadm.c (revision 754) ++++ usr/iscsiadm.c (revision 779) +@@ -99,9 +99,9 @@ + printf("\ + iscsiadm -m discovery [ -dhV ] [ -t type -p ip:port [ -l ] ] | [ -p ip:port ] \ + [ -o operation ] [ -n name ] [ -v value ]\n\ +-iscsiadm -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port | -M sysdir ] [ -l | -u ] ] \ ++iscsiadm -m node [ -dhV ] [ -L all,manual,automatic ] [ -U all,manual,automatic ] [ -S ] [ [ -T targetname -p ip:port ] [ -l | -u ] ] \ + [ [ -o operation ] [ -n name ] [ -v value ] [ -p ip:port ] ]\n\ +-iscsiadm -m session [ -dhV ] [ -r sessionid [ -i | -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n"); ++iscsiadm -m session [ -dhV ] [ -r sessionid | sysfsdir [ -i | -R | -u | -s ] [ -o operation ] [ -n name ] [ -v value ] ]\n"); + } + exit(status == 0 ? 0 : -1); + } +@@ -256,11 +256,13 @@ + struct session_mgmt_fn *mgmt = data; + char *mode = mgmt->mode; + idbm_t *db = mgmt->db; ++ iscsi_provider_t *p; + node_rec_t rec; + int rc = 0; + + /* for now skip qlogic and other HW and offload drivers */ +- if (!get_transport_by_sid(sid)) ++ p = get_transport_by_sid(sid); ++ if (!p) + return 0; + + if (idbm_node_read(db, &rec, targetname, address, port)) { +@@ -272,6 +274,11 @@ + targetname, address, port); + return 0; + } ++ ++ /* multiple drivers could be connected to the same portal */ ++ if (strcmp(rec.transport_name, p->name)) ++ return 0; ++ + /* + * we always skip on boot because if the user killed this on + * they would not be able to do anything +@@ -726,6 +733,21 @@ + return rc; + } + ++static int isns_dev_attr_query(idbm_t *db) ++{ ++ iscsiadm_req_t req; ++ iscsiadm_rsp_t rsp; ++ int err; ++ ++ memset(&req, 0, sizeof(iscsiadm_req_t)); ++ req.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY; ++ ++ err = do_iscsid(&ipc_fd, &req, &rsp); ++ if (!err) ++ idbm_for_each_node(db, NULL, print_node_info); ++ return err; ++} ++ + static int + verify_mode_params(int argc, char **argv, char *allowed, int skip_m) + { +@@ -874,6 +896,57 @@ + return rc; + } + ++static int parse_sid(char *session) ++{ ++ struct stat statb; ++ char sys_session[64], *start, *last; ++ int sid = -1, len; ++ ++ if (stat(session, &statb)) { ++ log_debug(1, "Could not stat %s failed with %d", ++ session, errno); ++ if (index(session, '/')) { ++ log_error("%s is an invalid session path\n", ++ session); ++ exit(1); ++ } ++ return atoi(session); ++ } ++ ++ if (!S_ISDIR(statb.st_mode)) { ++ log_error("%s is not a directory", session); ++ exit(1); ++ } ++ ++ /* ++ * Given sysfs_device is a directory name of the form: ++ * ++ * /sys/devices/platform/hostH/sessionS/targetH:B:I/H:B:I:L ++ * /sys/devices/platform/hostH/sessionS/targetH:B:I ++ * /sys/devices/platform/hostH/sessionS ++ * ++ * We want to set sys_session to sessionS ++ */ ++ last = NULL; ++ start = strstr(session, "session"); ++ if (start && strncmp(start, "session", 7) == 0) { ++ len = strlen(start); ++ last = index(start, '/'); ++ /* ++ * If '/' not found last is NULL. ++ */ ++ if (last) ++ len = last - start; ++ strncpy(sys_session, start, len); ++ } else { ++ log_error("Unable to find session in %s", session); ++ exit(1); ++ } ++ ++ sscanf(sys_session, "session%d", &sid); ++ return sid; ++} ++ + int + main(int argc, char **argv) + { +@@ -926,7 +999,7 @@ + value = optarg; + break; + case 'r': +- sid = atoi(optarg); ++ sid = parse_sid(optarg); + if (sid < 0) { + log_error("invalid sid '%s'", + optarg); +@@ -1028,9 +1101,10 @@ + rc = -1; + goto out; + } else if (type == DISCOVERY_TYPE_ISNS) { +- log_error("iSNS discovery is not fully " +- "implemented yet."); +- rc = -1; ++ if ((rc = isns_dev_attr_query(db)) > 0) { ++ iscsid_handle_error(rc); ++ rc = -1; ++ } + goto out; + } else if (type < 0) { + if (ip) { +Index: etc/iscsid.conf +=================================================================== +--- etc/iscsid.conf (revision 754) ++++ etc/iscsid.conf (revision 779) +@@ -7,6 +7,13 @@ + # and man page for iscsiadm for details on the --op command. + # + ++################ ++# iSNS settings ++################ ++# Address of iSNS server ++#isns.address = 192.168.0.1 ++#isns.port = 3205 ++ + #***************** + # Startup settings + #***************** +@@ -124,8 +131,8 @@ + # in an iSCSI PDU from a target, edit the following line. + # + # The value is the number of bytes in the range of 512 to (2^24-1) and +-# the default is 65536 +-node.conn[0].iscsi.MaxRecvDataSegmentLength = 65536 ++# the default is 131072 ++node.conn[0].iscsi.MaxRecvDataSegmentLength = 131072 + + # To allow the targets to control the setting of the digest checking, + # with the initiator requesting a preference of enabling the checking, uncomment# one or both of the following lines: +@@ -151,3 +158,8 @@ + # The default is to never use DataDigests and to allow the target to control + # the setting of the HeaderDigest checking with the initiator requesting + # a preference of disabling the checking. ++ ++# ++# To enable the iSNS server ++#isns.address = 192.168.0.1 ++#isns.port = 3205 +Index: etc/initd/initd.suse +=================================================================== +--- etc/initd/initd.suse (revision 754) ++++ etc/initd/initd.suse (revision 779) +@@ -69,7 +69,7 @@ + else + echo -n "Starting iSCSI initiator service: " + modprobe iscsi_tcp +- modprobe ib_iser ++ modprobe -q ib_iser + startproc $DAEMON $ARGS + RETVAL=$? + rc_status -v +@@ -89,7 +89,7 @@ + if [ "$RETVAL" == "0" ]; then + rm -f $PID_FILE + modprobe -r iscsi_tcp +- modprobe -r ib_iser ++ modprobe -q -r ib_iser + rc_failed 0 + else + rc_failed 1 +Index: Makefile +=================================================================== +--- Makefile (revision 754) ++++ Makefile (revision 779) +@@ -15,7 +15,8 @@ + initddir = $(etcdir)/init.d + + MANPAGES = doc/iscsid.8 doc/iscsiadm.8 doc/iscsi_discovery.8 +-PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery ++PROGRAMS = usr/iscsid usr/iscsiadm utils/iscsi_discovery \ ++ utils/fwparam_ibft/fwparam_ibft utils/iscsi-iname + INSTALL = install + ETCFILES = etc/iscsid.conf + +@@ -26,6 +27,8 @@ + all: + $(MAKE) -C usr + $(MAKE) -C kernel ++ $(MAKE) -C utils ++ $(MAKE) -C utils/fwparam_ibft + @echo + @echo "Compilation complete Output file" + @echo "----------------------------------- ----------------" +@@ -34,22 +37,25 @@ + @echo "Built iSCSI over TCP kernel module: kernel/iscsi_tcp.ko" + @echo "Built iSCSI daemon: usr/iscsid" + @echo "Built management application: usr/iscsiadm" ++ @echo "Built utility: utils/fwparam_ibft/fwparam_ibft" + @echo + @echo Read README file for detailed information. + + clean: ++ $(MAKE) -C utils clean + $(MAKE) -C usr clean + $(MAKE) -C kernel clean ++ $(MAKE) -C utils/fwparam_ibft clean + + # this is for safety + # now -jXXX will still be safe + # note that make may still execute the blocks in parallel + .NOTPARALLEL: install_usr install_programs install_initd \ + install_initd_suse install_initd_redhat install_initd_debian \ +- install_etc install_doc install_kernel ++ install_etc install_doc install_kernel install_iname + + install: install_kernel install_programs install_doc install_etc \ +- install_initd ++ install_initd install_iname + + install_programs: $(PROGRAMS) + $(INSTALL) -d $(DESTDIR)$(sbindir) +@@ -93,4 +99,13 @@ + install_kernel: + $(MAKE) -C kernel install_kernel + ++install_iname: ++ if [ ! -f /etc/iscsi/initiatorname.iscsi ]; then \ ++ echo "InitiatorName=`/sbin/iscsi-iname`" > /etc/iscsi/initiatorname.iscsi ; \ ++ echo "***************************************************" ; \ ++ echo "Setting InitiatorName to `cat /etc/iscsi/initiatorname.iscsi`" ; \ ++ echo "To override edit /etc/iscsi/initiatorname.iscsi" ; \ ++ echo "***************************************************" ; \ ++ fi ++ + # vim: ft=make tw=72 sw=4 ts=4: +Index: README +=================================================================== +--- README (revision 754) ++++ README (revision 779) +@@ -4,7 +4,7 @@ + + ================================================================= + +- November 8, 2006 ++ Jan 26, 2007 + + Contents + ======== +@@ -16,11 +16,9 @@ + - 5. Open-iSCSI Configuration Utility + - 6. Configuration + - 7. Getting Started +-- 8. TBD +-- Appendix A. SendTargets snapshot. ++- 8. iSCSI System Info + + +- + 1. In This Release + ================== + +@@ -36,7 +34,6 @@ + 1.1. Features + + - highly optimized and very small-footprint data path; +- - multiple outstanding R2Ts; + - persistent configuration database; + - SendTargets discovery; + - CHAP; +@@ -151,6 +148,15 @@ + The utility presents set of operations that a user can perform + on iSCSI nodes, sessions, connections, and discovery records. + ++Open-iscsi does not use the term node as defined by the iSCSI RFC, ++where a node is a single iSCSI initiator or target. Open-iscsi uses the ++term node to refer to a portal on a target, so tools like iscsiadm ++require that --targetname and --portal argument be used when in node mode. ++ ++For session mode, a session id (sid) is used. The sid of a session can be ++found by running iscsiadm -m session -i. The session id is not currently ++persistent and is partially determined by when the session is setup. ++ + Note that some of the iSCSI Node and iSCSI Discovery operations + do not require iSCSI daemon (iscsid) loaded. + +@@ -211,7 +217,8 @@ + -h, --help display this help and exit + + +- Usage examples (using the one-letter options): ++ Usage examples using the one-letter options (see iscsiadm man page ++ for long options): + + 1) SendTargets iSCSI Discovery: + +@@ -278,12 +285,17 @@ + + 7. Getting Started + ================== +-There are three steps needed to set up a system to use iscsi storage: +-1. automate iscsi startup using the init script. +-2. discover targets. +-3. automate target logins for future system reboots. ++There are three steps needed to set up a system to use iSCSI storage: ++7.1. iSCSI startup using the init script or manual startup. ++7.2. Discover targets. ++7.3. Automate target logins for future system reboots. + +-1. automate iscsi startup using the init script ++The init scripts will start the iSCSI daemon and log into any ++connections or nodes that are set up for automatic login. If your distro ++does not have a init script, then you will have to start the daemon ++and log into the targets manually. ++ ++7.1.1 iSCSI startup using the init script + ----------------------------------------------- + + Red Hat or Fedora: +@@ -312,8 +324,11 @@ + + will usually get you started. + +-Other: +------- ++7.1.2 Manual Startup: ++--------------------- ++ ++7.1.2.1 Starting up the iSCSI daemon (iscsid) and loading modules: ++----------------------------------------------------------------- + If there is no initd script, you must start the tools by hand. First load the + iscsi modules with: + +@@ -328,26 +343,70 @@ + + ./iscsid -d8 -f & + +-and use configuration utility to add/remove/update Discovery records, +-iSCSI Node records or monitor active iSCSI sessions (see above or the +-iscsiadm man files). ++7.1.2.2 Logging into Targets: ++--------------------------- ++Use the configuration utility, iscsiadm, to add/remove/update Discovery ++records, iSCSI Node records or monitor active iSCSI sessions (see above or the ++iscsiadm man files and see section 7.2 below for how to discover targets). + +- ./iscsiadm ++ ./iscsiadm -m node + ++will print out the nodes that have been discovered as: + +-To login: ++ 10.15.85.19:3260,3 iqn.1992-08.com.netapp:sn.33615311 ++ 10.15.84.19:3260,2 iqn.1992-08.com.netapp:sn.33615311 + ++The format is: ++ ++ip:port,target_portal_group_tag targetname ++ ++where targetname is the name of the target and ip_address:port is the address ++and port of the portal. target_portal_group_tag, is the portal group tag of ++the portal, and is not used in iscsiadm commands. ++ ++To login, take the ip, port and targetname from above and run: ++ + ./iscsiadm -m node -T targetname -p ip:port -l + +-where targetname is the name of the target amd ip_address:port is the address +-and port of the portal of a discovered or manually added iSCSI Target Node +-(for iscsiadm usage examples see previous sections). ++In this example we would run + +-2. discover targets +-------------------- +-Once iscsi is up, you can perform discovery to targets using: +-iscsiadm -m discovery -t sendtargets -p 192.168.1.1:3260 ++ ./iscsiadm -m node -T iqn.1992-08.com.netapp:sn.33615311 -p 10.15.84.19:3260 -l + ++ Note: drop the portal group tag from the "iscsiadm -m node" output. ++ ++7.2. Discover Targets ++--------------------- ++Once the iSCSI service is running, you can perform discovery using ++SendTarget with: ++ ++iscsiadm -m discovery -t sendtargets -p ip:port ++ ++where "ip" is the address of the portal and port is the port. ++ ++Or you can you perform discovery using iSNS by setting the address ++of the iSNS server in iscsid.conf with the "isns.address" value and ++running: ++ ++iscsiadm -m discovery -t isns ++ ++Both commands will print out the list of all discovered targets and their ++portals: ++ ++# iscsiadm -m discovery -t st -p 10.15.85.19:3260 ++10.15.85.19:3260,3 iqn.1992-08.com.netapp:sn.33615311 ++10.15.84.19:3260,2 iqn.1992-08.com.netapp:sn.33615311 ++ ++Note: this prints out every node in the db including the ones just discovered. ++This is a bug and will change in future releases. ++ ++The format for the output is: ++ ++ip:port,target_portal_group_tag targetname ++ ++In this example, for the first target the ip address is 10.15.85.19. ++The port is 3260. The target portal group is 3, and the target name ++is iqn.1992-08.com.netapp:sn.33615311. ++ + While discovery targets are kept in the discovery db, they are + usefull only for re-discovery. The discovered targets (a.k.a. nodes) + are stored as records in the node db. +@@ -356,12 +415,16 @@ + into the discovered nodes (making LUs from those nodes available as + storage), it is better to automate the login to the nodes we need. + +-3. automate target logins for future system reboots +---------------------------------------------------- +-Note: this may only work for Red Hat, Fedora and SUSE configurations ++If you wish to log into a target manually now, see section ++"7.1.2.2 Logging in targets" above. + +-To automate login to a node, use the following with the record ID of the +-node discovered in the discovery above: ++7.3. Automate Target Logins for Future System Statups ++----------------------------------------------------- ++Note: this may only work for distros with init scripts. ++ ++To automate login to a node, use the following with the record ID ++(record ID is the targetname and portal) of the node discovered in the ++discovery above: + iscsiadm -m node -T targetname -p ip:port --op update -n node.conn[0].startup -v automatic + + Or to set the "node.conn[0].statup" attribute to "startup" as default for +@@ -369,36 +432,37 @@ + + node.conn[0].startup = automatic + +-To login to all the automated nodes, simply restart the iscsi service +-e.g /etc/init.d/open-iscsi restart ++Setting this in iscsid.conf, will not affect existing nodes. It will only ++affect nodes that are discovered after setting the value. + ++To login to all the automated nodes, simply restart the iscsi service: ++e.g /etc/init.d/open-iscsi restart. On your next startup the nodes will ++be logged into autmotically. + +-8. TBD +-====== + +-To be completed: ++8. iSCSI System Info ++==================== + +- - Kernel tracing and Troubleshooting +- - Immediate and not-so-immediate plans +- - Useful scripts +- - White paper on Open-iSCSI design ++To get information about the running sessions: including the session and ++device state, session ids (sid) for session mode, and some of the ++negioated parameters, run: + ++ iscsiadm -m session -i + +-Appendix A. SendTargets iSCSI Discovery session snapshot. +-========================================================= ++If you are looking for something shorter like just the sid to node mapping ++run: + +--bash-2.05b# ./iscsiadm -m discovery -tst -p 10.16.16.223:3260 +-[02f611] 10.16.16.223:3260,1 iqn.2002-07.com.ttechnologies.target.a +-[01acd1] 17.1.1.223:3260,1 iqn.2002-07.com.ttechnologies.target.a +--bash-2.05b# +--bash-2.05b# ./iscsiadm -m node +-[02f611] 10.16.16.223:3260,1 iqn.2002-07.com.ttechnologies.target.a +-[01acd1] 17.1.1.223:3260,1 iqn.2002-07.com.ttechnologies.target.a +--bash-2.05b# +--bash-2.05b# ./iscsiadm -m discovery -tst -p 10.16.16.227:3260 +-[02fb91] 10.16.16.227:3260,1 iqn.2001-04.com.example:storage.disk2.sys1.xyz +--bash-2.05b# +--bash-2.05b# ./iscsiadm -m node +-[02f611] 10.16.16.223:3260,1 iqn.2002-07.com.ttechnologies.target.a +-[02fb91] 10.16.16.227:3260,1 iqn.2001-04.com.example:storage.disk2.sys1.xyz +-[01acd1] 17.1.1.223:3260,1 iqn.2002-07.com.ttechnologies.target.a ++ iscsiadm -m session ++ ++This will print the list of running sessions with the format: ++ ++kernel module: [sid] ip:port,target_portal_group_tag targetname ++ ++# iscsiadm -m session ++tcp: [2] 10.15.84.19:3260,2 iqn.1992-08.com.netapp:sn.33615311 ++tcp: [3] 10.15.85.19:3260,3 iqn.1992-08.com.netapp:sn.33615311 ++ ++For example this first node is using the iscsi_tcp kernel module, has ++session id (sid) 2, is connected to a portal with address and port ++10.15.84.19:3260 in portal group 2 on the target, ++iqn.1992-08.com.netapp:sn.33615311. diff --git a/open-iscsi-connect-retry b/open-iscsi-connect-retry new file mode 100644 index 0000000..cb01714 --- /dev/null +++ b/open-iscsi-connect-retry @@ -0,0 +1,49 @@ +--- open-iscsi-2.0-707/usr/util.c 2007/03/06 08:20:18 1.9 ++++ open-iscsi-2.0-707/usr/util.c 2007/03/06 08:56:09 +@@ -79,14 +79,16 @@ + return str; + } + ++#define MAXSLEEP 128 ++ + static int iscsid_connect(void) + { +- int fd, err; ++ int fd, err, nsec; + struct sockaddr_un addr; + + fd = socket(AF_LOCAL, SOCK_STREAM, 0); + if (fd < 0) { +- log_error("can not create IPC socket!"); ++ log_error("can not create IPC socket (%d)!", errno); + return fd; + } + +@@ -95,12 +97,22 @@ + memcpy((char *) &addr.sun_path + 1, ISCSIADM_NAMESPACE, + strlen(ISCSIADM_NAMESPACE)); + +- if ((err = connect(fd, (struct sockaddr *) &addr, sizeof(addr))) < 0) { +- log_error("can not connect to iSCSI daemon!"); +- fd = err; ++ /* ++ * Trying to connect with exponential backoff ++ */ ++ for (nsec = 1; nsec <= MAXSLEEP; nsec <<= 1) { ++ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == 0) { ++ /* Connection established */ ++ return fd; ++ } ++ /* ++ * Delay before trying again ++ */ ++ if (nsec <= MAXSLEEP/2) ++ sleep(nsec); + } +- +- return fd; ++ log_error("can not connect to iSCSI daemon (%d)!", errno); ++ return -1; + } + + static int iscsid_request(int fd, iscsiadm_req_t *req) diff --git a/open-iscsi-fwparam-mask-typo b/open-iscsi-fwparam-mask-typo new file mode 100644 index 0000000..1f715b1 --- /dev/null +++ b/open-iscsi-fwparam-mask-typo @@ -0,0 +1,11 @@ +--- open-iscsi-2.0-707/utils/fwparam_ibft/fwparam_ibft.c 2007/03/14 09:48:24 1.7 ++++ open-iscsi-2.0-707/utils/fwparam_ibft/fwparam_ibft.c 2007/03/16 10:48:24 +@@ -232,7 +232,7 @@ + * bringup, this sounds less flexible than the normal + * masks used. + */ +- printf("%s%s:%d\n", prefix, "MASK", nic->subnet_mask_prefix); ++ printf("%s%s=%d\n", prefix, "MASK", nic->subnet_mask_prefix); + dump_ipaddr(prefix, "GATEWAY", nic->gateway); + dump_ipaddr(prefix, "DNSADDR1", nic->primary_dns); + dump_ipaddr(prefix, "DNSADDR2", nic->secondary_dns); diff --git a/open-iscsi-fwparam-openprom b/open-iscsi-fwparam-openprom new file mode 100644 index 0000000..1425e3e --- /dev/null +++ b/open-iscsi-fwparam-openprom @@ -0,0 +1,1524 @@ +diff --git a/utils/fwparam_ibft/Makefile b/utils/fwparam_ibft/Makefile +index ee090c1..673af53 100644 +--- a/utils/fwparam_ibft/Makefile ++++ b/utils/fwparam_ibft/Makefile +@@ -1,3 +1,29 @@ ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# ++# This program is free software; you can redistribute it and/or modify ++# it under the terms of the GNU General Public License as published by ++# the Free Software Foundation; either version 2 of the License, or ++# (at your option) any later version. ++# ++# This program is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++# GNU General Public License for more details. ++# ++# You should have received a copy of the GNU General Public License ++# along with this program; if not, write to the Free Software ++# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ++# ++# Copyright (C) IBM Corporation. 2007 ++# ++# Authors: Patrick Mansfield ++# Mike Anderson ++# Doug Maxey ++# ++ ++CC = $(CROSS_COMPILE)gcc + OPTFLAGS ?= -O2 -g + WARNFLAGS ?= -Wall -Wstrict-prototypes + CFLAGS += $(OPTFLAGS) $(WARNFLAGS) +@@ -5,7 +31,86 @@ PROGRAMS = fwparam_ibft + + all: $(PROGRAMS) + +-fwparam_ibft: fwparam_ibft.c fwparam_ibft.h +- $(CC) $(CFLAGS) $^ -o $@ +-clean: +- rm -f *.o $(PROGRAMS) ++# ++# This tool currently only provides useful output on certain IBM ++# boxes with iboot capable firmware or BIOS on System x and System p. :) ++# ++ ++OBJS := ++ ++# Allow cross compile by looking at the compiler, not the host. ++target_machine = $(ARCH) ++ ++# ++# override compiler test for powerpc by defining DEBUG_IBFT=powerpc to ++# build with native compiler. ++# ++ ++ifeq (ppc,$(findstring ppc,$(target_machine))) ++# ++# powerpc objects ++# ++OBJS += prom_lex.o prom_parse.tab.o fwparam_ppc_main.o ++GENFILES = prom_lex.c prom_parse.tab.c prom_parse.tab.h ++CLEANFILES = $(OBJS) $(GENFILES) *.output *~ ++ ++BISONFLAGS = -d ++FLEXFLAGS = -t ++# turn off #line number markers ++NOL = 0 ++ifneq (0 ,$(NOL)) ++ FLEXFLAGS += -L ++ BISONFLAGS += --no-lines ++endif # NOL ++ ++ %.c : %.l ++ flex ${FLEXFLAGS} $< | perl -pe '/define YYLMAX/ && s{8192}{2048}' >$@ ++ ++ %.tab.c %.tab.h: %.y ++ bison ${BISONFLAGS} $< ++ ++$(GENFILES): Makefile ++ ++$(OBJS): prom_parse.tab.h prom_parse.h ++########## ++ ++else ++ifeq (x86_64,$(target_machine)) ++# ++# x86_64 objects ++# ++OBJS += fwparam_ibft.o ++CLEANFILES = $(OBJS) *~ ++ ++$(OBJS): Makefile fwparam_ibft.h ++########## ++ ++ ++else ++ifeq (86,$(findstring 86,$(target_machine))) ++# ++# x86 objects ++# ++OBJS += fwparam_ibft.o ++CLEANFILES = $(OBJS) *~ ++ ++$(OBJS): Makefile fwparam_ibft.h ++########## ++ ++else ++# ++# compile a do nothing main here. ++# ++OBJS += fwparam_dummy.o ++########## ++ ++endif ++endif ++endif ++ ++$(PROGRAMS) : $(OBJS) Makefile ++ ${CC} -o $(PROGRAMS) $(OBJS) ++ ++.PHONY : clean ++clean : ++ -rm -f $(PROGRAMS) $(CLEANFILES) +diff --git a/utils/fwparam_ibft/README-ppc b/utils/fwparam_ibft/README-ppc +new file mode 100644 +index 0000000..f42d2c3 +--- /dev/null ++++ b/utils/fwparam_ibft/README-ppc +@@ -0,0 +1,16 @@ ++ ++This is the second version of fwparam_ibft. ++ ++This version of the tool enables a powerpc system that is capable of ++booting from an iSCSI device to extract the pertinent info about the ++connection. The functionality is as close an analogue as possible to ++the fwparam_ibft for IBM System x. ++ ++This version of the tool parses the firmware property ++/proc/device-tree/chosen/bootpath by default when called with the -b ++flag, and prints the interesting data in a format that can be simply ++reparsed during boot. ++ ++It can also use any file that properly describes the connection, in ++particular the /proc/device-tree/aliases/iscsi-disk? files. ++ +diff --git a/utils/fwparam_ibft/fwparam_dummy.c b/utils/fwparam_ibft/fwparam_dummy.c +new file mode 100644 +index 0000000..11a2efe +--- /dev/null ++++ b/utils/fwparam_ibft/fwparam_dummy.c +@@ -0,0 +1,23 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ * ++ * Copyright (C) IBM Corporation, 2007 ++ * ++ * Author: Doug Maxey ++ * ++ */ ++ ++int main (int argc, char **argv) {/* An exercise in minimalism. */ return 0;} +diff --git a/utils/fwparam_ibft/fwparam_ppc.c b/utils/fwparam_ibft/fwparam_ppc.c +new file mode 100644 +index 0000000..8046370 +--- /dev/null ++++ b/utils/fwparam_ibft/fwparam_ppc.c +@@ -0,0 +1,257 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ++ * USA. ++ * ++ * Copyright (C) IBM Corporation, 2007 ++ * ++ * Author: Doug Maxey ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++char *progname; ++int debug; ++char default_file_name[] = "/proc/device-tree/choices/bootpath"; ++char *filename = default_file_name; ++char *bootpath_val; ++char *mac_path; ++char *mac_val; ++int boot_selected_only; ++int bytes_read; ++char *device; ++char *iscsi; ++char *initiator_name; ++char *client_addr; ++char *subnet_mask; ++char *server_addr; ++char *server_name; ++char *port; ++char *lun; ++char *prefix; ++ ++ ++/* ++ * Prefix strings, for the "prefixN:NAME=value". ++ */ ++#define NETWORK "network" ++#define INITIATOR "iscsi-initiator" ++#define TGT "target" ++#define MAC_FILE "/mac-address" ++ ++void pr_param(const char *prefix, int instance, const char *item, char *param) ++{ ++ char *val = strchr(param, '=') + 1; ++ ++ fprintf(stdout, "%s%d:%s=%s ", prefix, instance, item, val); ++} ++ ++int locate_print_mac(void) ++{ ++ int error = 0; ++ char *dt = strdup(filename); ++ int mac_path_len = strlen(device) + strlen(MAC_FILE) + 2; ++ char *mac_file; ++ int mac_fd; ++ ++ /* ++ * Get the device-tree top, and work from there. ++ */ ++ mac_fd = strlen(filename); ++ mac_fd -= strlen("chosen/bootpath") + 1; /* back up this far */ ++ dt[mac_fd] = 0; ++ mac_path_len += mac_fd; ++ mac_file = malloc(mac_path_len); ++ if (!mac_file) { ++ error = ENOMEM; ++ fprintf(stderr, "%s: malloc %s, %s\n", progname, filename, ++ strerror(errno)); ++ goto lpm_bail; ++ } ++ ++ snprintf(mac_file, mac_path_len, "%s%s%s", dt, device, MAC_FILE); ++ mac_fd = open(mac_file, O_RDONLY); ++ if (mac_fd < 0) { ++ error = errno; ++ fprintf(stderr, "%s: open %s, %s\n", progname, mac_file, ++ strerror(errno)); ++ goto lpm_bail; ++ } ++ ++ bytes_read = read(mac_fd, mac, 6); ++ if (bytes_read != 6) { ++ error = EIO; ++ fprintf(stderr, "%s: read %s, %s\n", progname, mac_file, ++ strerror(errno)); ++ goto lpm_bail; ++ } ++ close(mac_fd); ++ ++ fprintf(stdout, "%s0:HWADDR=%02x:%02x:%02x:%02x:%02x:%02x ", prefix, ++ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); ++ ++lpm_bail: ++ return error; ++} ++ ++int print_params(void) ++{ ++ int error = 0; ++ char *pp; /* working pointer in the bootpath params. */ ++#define pick(name) (name) = strtok(NULL, ","); \ ++ if (!(name)) { error = __LINE__; goto ppb_bail; } ++ ++ device = bootpath_val; ++ pp = strchr(bootpath_val, ':'); ++ ++ if (pp) ++ /* chop the string at the first colon. */ ++ *pp++ = 0; ++ prefix = "iSCSI_INITIATOR_"; ++ ++ error = locate_print_mac(); ++ if (error) ++ goto ppb_bail; ++ ++ /* ++ * tokenize on the comma. ++ */ ++ ++ iscsi = strtok(pp, ","); ++ if (!(iscsi)) { error = __LINE__; goto ppb_bail; } ++ ++ pick(initiator_name); ++ pick(client_addr); ++ pick(subnet_mask); ++ pick(server_addr); ++ pick(server_name); ++ pick(port); ++ pick(lun); ++ ++ /* We have all the required strings. Now print them. */ ++ pr_param(prefix, 0, "NAME", initiator_name); ++ pr_param(prefix, 0, "IPADDR", client_addr); ++ pr_param(prefix, 0, "MASK", subnet_mask); ++ prefix = "target"; ++ pr_param(prefix, 0, "IPADDR", server_addr); ++ pr_param(prefix, 0, "PORT", port); ++ pr_param(prefix, 0, "LUN", lun); ++ pr_param(prefix, 0, "NAME", server_name); ++ ++ puts(""); ++ppb_bail: ++ return error; ++} ++ ++int ++main (int argc, char **argv) ++{ ++ int fd, option, ret; ++ struct stat bootpath_stat; ++ ++ progname = argv[0]; ++ ++ /* ++ * For powerpc, our operations are fundamentally different. ++ * Where the x86 method searches memory, we look in the ++ * ppc procfs device-tree to obtain the data. ++ */ ++ while (1) { ++ option = getopt(argc, argv, "f:m:s:e:vhb"); ++ if (option == -1) ++ break; ++ switch (option) { ++ case 'b': ++ boot_selected_only = 1; ++ break; ++ case 'e': ++ break; ++ case 'f': ++ filename = optarg; ++ break; ++ case 's': ++ break; ++ case 'v': ++ debug++; ++ break; ++ default: ++ fprintf(stderr, "Unknown or bad option '%c'\n", option); ++ case 'h': ++ printf("Usage: %s OPTIONS\n" ++ "-b (x86 only) print only fw boot selected sections\n" ++ "-f file_to_search (default %s)\n" ++ "-s (x86 only) offset to start search\n" ++ "-e (x86 only) length of search\n" ++ "-v verbose\n", ++ progname, default_file_name); ++ exit(1); ++ } ++ } ++ ++ if (debug) ++ fprintf(stderr, "%s: file:%s; debug:%d\n", progname, filename, debug); ++ ++ ret = stat(filename, &bootpath_stat); ++ if (ret < 0) { ++ fprintf(stderr, "%s: stat %s, %s\n", progname, filename, ++ strerror(errno)); ++ exit(errno); ++ } ++ ++ bootpath_val = malloc(bootpath_stat.st_size); ++ if (!bootpath_val) { ++ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", ++ progname, filename, strerror(ENOMEM), ENOMEM); ++ exit(ENOMEM); ++ } ++ ++ fd = open(filename, O_RDONLY); ++ if (fd < 0) { ++ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", ++ progname, filename, strerror(errno), errno); ++ exit(1); ++ } ++ ++ bytes_read = read(fd, bootpath_val, bootpath_stat.st_size); ++ close(fd); ++ if (bytes_read != bootpath_stat.st_size) { ++ ret = EIO; ++ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", ++ progname, filename, strerror(ret), ret); ++ exit(ret); ++ } ++ ++ close(fd); ++ ++ /* ++ * We should find *almost* everything we need in the bootpath, ++ * save the mac-address. ++ */ ++ if (strstr(bootpath_val, "iscsi")) ++ ret = parse_print_params(); ++ else ++ /* did not boot from iscsi! */ ++ ret = 1; ++ ++ exit(ret); ++} +diff --git a/utils/fwparam_ibft/fwparam_ppc_main.c b/utils/fwparam_ibft/fwparam_ppc_main.c +new file mode 100644 +index 0000000..a004b04 +--- /dev/null ++++ b/utils/fwparam_ibft/fwparam_ppc_main.c +@@ -0,0 +1,562 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ++ * ++ * Copyright (C) IBM Corporation. 2007 ++ * Author: Doug Maxey ++ * ++ */ ++ ++#define _XOPEN_SOURCE 500 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "iscsi_obp.h" ++#include "prom_parse.h" ++ ++void* yy_scan_string(const char *str); ++int yyparse(struct ofw_dev *ofwdev); ++ ++#define BOOTPATH "/chosen/bootpath" ++#define DT_TOP "/proc/device-tree" ++#define MAC_FILE "/mac-address" ++#define LOCAL_MAC_FILE "/local-mac-address" ++ ++char *progname; ++int debug; ++char default_devtree[] = DT_TOP; ++char *devtree = default_devtree; ++int devtree_len; ++char default_file_name[] = DT_TOP BOOTPATH; ++char *filename = default_file_name; ++char *bootpath_val; ++char *mac_path; ++char *mac_val; ++int boot_selected_only; ++int bytes_read; ++#define OFWDEV_MAX (10) ++struct ofw_dev *ofwdevs[OFWDEV_MAX]; ++int dev_count; ++char *niclist[OFWDEV_MAX]; ++int nic_count; ++ ++/* ++ * Prefix strings, for the "prefixN:NAME=value". ++ */ ++#define NETWORK "network" ++#define INITIATOR "iscsi-initiator" ++#define TARGET "target" ++ ++void pr_param(const char *prefix, const char *name, ++ struct ofw_dev *dev, enum obp_param item) ++{ ++ if (dev->param[item]) ++ fprintf(stdout, " %s%s=%s", prefix, name, dev->param[item]->val); ++} ++ ++char *find_devtree(const char *filename) ++{ ++ char *devtree = strdup(filename); ++ char *chop_at; ++ struct stat dt_stat; ++ int error; ++ ++ /* ++ * What is the path to the device-tree? The only valid ++ * directories to locate the property are under /aliases or ++ * /chosen. ++ */ ++ ++ chop_at = strstr(devtree, "/chosen"); ++ if (!chop_at) ++ chop_at = strstr(devtree, "/aliases"); ++ ++ if (!chop_at) { ++ char *vdev = malloc(strlen(filename) + strlen("/vdevice") + 1); ++ ++ /* ++ * test to see if there is /vdevice dir ++ */ ++ sprintf(vdev, "%s%s", filename, "/vdevice"); ++ error = stat(vdev, &dt_stat); ++ if (error) ++ devtree = NULL; ++ } ++ else ++ devtree[chop_at - devtree] = 0; ++ ++ if (devtree) ++ devtree_len = strlen(devtree); ++ ++ return devtree; ++} ++ ++void print_mac(const char *prefix, struct ofw_dev *ofwdev) ++{ ++ fprintf(stdout, " %sHWADDR=%02x:%02x:%02x:%02x:%02x:%02x ", prefix, ++ ofwdev->mac[0], ofwdev->mac[1], ofwdev->mac[2], ++ ofwdev->mac[3], ofwdev->mac[4], ofwdev->mac[5]); ++} ++ ++/* ++ * Take the path to the property under chosen, and swizzle to make that ++ * the base for the device path discovered. ++ */ ++int locate_mac(const char *devtree, struct ofw_dev *ofwdev) ++{ ++ int error = 0; ++ int mac_path_len = strlen(ofwdev->dev_path) + strlen(MAC_FILE) + 2; ++ char *mac_file; ++ int mac_fd; ++ ++ mac_path_len += strlen(devtree); ++ mac_file = malloc(mac_path_len); ++ if (!mac_file) { ++ error = ENOMEM; ++ fprintf(stderr, "%s: malloc %s, %s\n", progname, filename, ++ strerror(errno)); ++ goto lpm_bail; ++ } ++ ++ snprintf(mac_file, mac_path_len, "%s/%s%s", devtree, ofwdev->dev_path, MAC_FILE); ++ mac_fd = open(mac_file, O_RDONLY); ++ if (mac_fd < 0) { ++ error = errno; ++ fprintf(stderr, "%s: open %s, %s\n", progname, mac_file, ++ strerror(errno)); ++ goto lpm_bail; ++ } ++ ++ bytes_read = read(mac_fd, ofwdev->mac, 6); ++ if (bytes_read != 6) { ++ error = EIO; ++ fprintf(stderr, "%s: read %s, %s\n", progname, mac_file, ++ strerror(errno)); ++ goto lpm_bail; ++ } ++ close(mac_fd); ++ ++ ++lpm_bail: ++ return error; ++} ++ ++void print_initiator(const char *prefix, struct ofw_dev *ofwdev) ++{ ++ pr_param(prefix, "ISNS", ofwdev, OBP_PARAM_ISNS); ++ pr_param(prefix, "SLP", ofwdev, OBP_PARAM_SLP); ++ pr_param(prefix, "NAME", ofwdev, OBP_PARAM_INAME); ++} ++ ++void print_target(const char *prefix, struct ofw_dev *ofwdev) ++{ ++ pr_param(prefix, "IPADDR", ofwdev, OBP_PARAM_SIADDR); ++ pr_param(prefix, "PORT", ofwdev, OBP_PARAM_IPORT); ++ pr_param(prefix, "LUN", ofwdev, OBP_PARAM_ILUN); ++ pr_param(prefix, "NAME", ofwdev, OBP_PARAM_ITNAME); ++ pr_param(prefix, "ISID", ofwdev, OBP_PARAM_ISID); ++ ++ /* ++ * chap stuff is always associated with the target ++ */ ++ pr_param(prefix, "CHAP_NAME", ofwdev, OBP_PARAM_ICHAPID); ++ pr_param(prefix, "CHAP_NAME", ofwdev, OBP_PARAM_ICHAPPW); ++ pr_param(prefix, "CHAP_NAME_IN", ofwdev, OBP_PARAM_CHAPID); ++ pr_param(prefix, "CHAP_PASSWORD_IN", ofwdev, OBP_PARAM_CHAPPW); ++} ++ ++void print_nic(const char *prefix, struct ofw_dev *ofwdev) ++{ ++ print_mac(prefix, ofwdev); ++ ++ /* ++ * nic parameters ++ */ ++ pr_param(prefix, "IPADDR", ofwdev, OBP_PARAM_CIADDR); ++ pr_param(prefix, "MASK", ofwdev, OBP_PARAM_SUBNET_MASK); ++} ++ ++const char *obp_qual_set(struct ofw_dev *ofwdev, const char *qual) ++{ ++ if (!strcmp("bootp", qual)) ++ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_BOOTP; ++ else if (!strcmp("ipv6", qual)) ++ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_IPV6; ++ else if (!strcmp("iscsi", qual)) { ++ ofwdev->type = OFW_DT_ISCSI; ++ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_ISCSI; ++ } ++ else if (!strcmp("ping", qual)) ++ ofwdev->quals[ofwdev->qual_count++] = OBP_QUAL_PING; ++ else ++ printf("%s: %s UNKNOWN\n", __func__, qual); ++ return qual; ++} ++ ++void add_obp_parm(struct ofw_dev *ofwdev, enum obp_param parm, const char *str) ++{ ++ int psz = sizeof(struct ofw_obp_param) + strlen(str); ++ ++ ofwdev->param[parm] = malloc(psz); ++ if (ofwdev->param[parm] == NULL) { ++ printf("%s: ENOMEM!\n", __func__); ++ return; ++ } ++ memset(ofwdev->param[parm], 0, psz); ++ ofwdev->param[parm]->len = psz; ++ strcpy(ofwdev->param[parm]->val, str); ++} ++ ++void obp_parm_addr(struct ofw_dev *ofwdev, const char *parm, const char *addr) ++{ ++ if (!strcmp("ciaddr", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_CIADDR, addr); ++ else if (!strcmp("dhcp", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_DHCP, addr); ++ else if (!strcmp("giaddr", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_GIADDR, addr); ++ else if (!strcmp("isns", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_ISNS, addr); ++ else if (!strcmp("siaddr", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_SIADDR, addr); ++ else if (!strcmp("slp", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_SLP, addr); ++ else if (!strcmp("subnet-mask", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_SUBNET_MASK, addr); ++ else ++ printf("%s: %s UNKNOWN\n", __func__, parm); ++} ++ ++void obp_parm_iqn(struct ofw_dev *ofwdev, const char *parm, const char *iqn) ++{ ++ if (!strcmp("itname", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_ITNAME, iqn); ++ else if (!strcmp("iname", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_INAME, iqn); ++ else ++ printf("%s: %s UNKNOWN\n", __func__, parm); ++} ++ ++void obp_parm_hexnum(struct ofw_dev *ofwdev, const char *parm, const char *numstr) ++{ ++ if (!strcmp("bootp-retries", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_BOOTP_RETRIES, numstr); ++ else if (!strcmp("tftp-retries", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_TFTP_RETRIES, numstr); ++ else if (!strcmp("iport", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_IPORT, numstr); ++ else if (!strcmp("ilun", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_ILUN, numstr); ++ else if (!strcmp("isid", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_ISID, numstr); ++ else ++ printf("%s: %s UNKNOWN <%s>\n", __func__, parm, numstr); ++} ++ ++void obp_parm_str(struct ofw_dev *ofwdev, const char *parm, const char *str) ++{ ++ if (!strcmp("filename", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_FILENAME, str); ++ else if (!strcmp("ichapid", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_ICHAPID, str); ++ else if (!strcmp("ichappw", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_ICHAPPW, str); ++ else if (!strcmp("chapid", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_CHAPID, str); ++ else if (!strcmp("chappw", parm)) ++ add_obp_parm(ofwdev, OBP_PARAM_CHAPPW, str); ++ else ++ printf("%s: %s UNKNOWN <%s>\n", __func__, parm, str); ++} ++ ++void yyerror(struct ofw_dev *ofwdev, const char *msg) ++{ ++ fprintf(stderr, "%s: error in <%s> at l%d.c%d\n", progname, ofwdev->prop_path, ++ yylloc.last_line, yylloc.last_column); ++} ++ ++void dump_obp_params(struct ofw_dev **devs, int dev_count) ++{ ++ static const char *param_name[] = { ++ "nada", ++ "blocksize", ++ "bootp-retries", ++ "target-chapid", ++ "target-chappwd", ++ "my-addr", ++ "dhcp", ++ "filename", ++ "gateway", ++ "my-chapid", ++ "my-chappwd", ++ "target-lun", ++ "target-name", ++ "target-port", ++ "target-isid", ++ "sns-server", ++ "my-name", ++ "server-addr", ++ "slp-server-addr", ++ "subnet-mask", ++ "tftp-retries", ++ "timeout" ++ }; ++ int i, j; ++ struct ofw_dev *dev; ++ char prefix[42]; ++ ++ for (j = 0; j < dev_count; j++) { ++ sprintf(prefix, "instance%d:", j); ++ for (dev = devs[j], i = OBP_PARAM_BLKSIZE; i < OBP_PARAM_COUNT; i++) { ++ pr_param(prefix, param_name[i], dev, i); ++ putchar('\n'); ++ } ++ } ++ ++} ++ ++int parse_params(const char *buf, struct ofw_dev *ofwdev) ++{ ++ int error = 0; ++#if YYDEBUG ++ yydebug = 1; ++#endif ++ ++ ++ if (yy_scan_string(buf)) ++ error = yyparse(ofwdev); ++ ++ return error; ++} ++ ++int find_file(const char *filename) ++{ ++ int error, fd; ++ struct stat bootpath_stat; ++ ++ error = stat(filename, &bootpath_stat); ++ if (error < 0) { ++ fprintf(stderr, "%s: stat %s, %s\n", progname, filename, strerror(errno)); ++ exit(errno); ++ } ++ ++ bootpath_val = malloc(bootpath_stat.st_size); ++ if (!bootpath_val) { ++ error = ENOMEM; ++ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", ++ progname, filename, strerror(error), error); ++ exit(error); ++ } ++ ++ fd = open(filename, O_RDONLY); ++ if (fd < 0) { ++ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", ++ progname, filename, strerror(errno), errno); ++ exit(1); ++ } ++ ++ bytes_read = read(fd, bootpath_val, bootpath_stat.st_size); ++ close(fd); ++ if (bytes_read != bootpath_stat.st_size) { ++ error = EIO; ++ fprintf(stderr, "%s: Could not open %s: %s (%d)\n", ++ progname, filename, strerror(error), error); ++ exit(error); ++ } ++ ++ close(fd); ++ ++ return 1; ++} ++ ++static int find_nics(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw) ++{ ++ if (tflag == FTW_D && ++ (strstr(fpath + ftw->base, "iscsi-toe") || ++ strstr(fpath + ftw->base, "ethernet"))) { ++ ++ if (nic_count < OFWDEV_MAX) { ++ niclist[nic_count] = malloc(strlen(fpath) + 1); ++ niclist[nic_count++] = strdup(fpath + devtree_len); ++ } ++ } ++ return 0; ++} ++ ++static int find_initiator(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftw) ++{ ++ struct ofw_dev *dev; ++ ++ if (tflag == FTW_F && (strstr(fpath + ftw->base, "iscsi-"))) { ++ ++ if (dev_count < OFWDEV_MAX) { ++ ofwdevs[dev_count++] = dev = calloc(sizeof(struct ofw_dev), 1); ++ dev->prop_path = strdup(fpath + devtree_len); ++ } ++ } ++ return 0; ++} ++ ++int loop_devs(void) ++{ ++ int error; ++ int i; ++ char prefix[256]; ++ ++ error = nftw(devtree, find_nics, 20, 0); ++ if (error) ++ return error; ++ ++ snprintf(prefix, sizeof(prefix), "%s/%s", devtree, "aliases"); ++ error = nftw(prefix, find_initiator, 20, 0); ++ if (error) ++ return error; ++ ++ for (i = 0; i < dev_count; i++) { ++ snprintf(prefix, sizeof(prefix), "%s%s", devtree, ofwdevs[i]->prop_path); ++ if (find_file(prefix)) { ++ error = parse_params(bootpath_val, ofwdevs[i]); ++ if (!error) ++ error = locate_mac(devtree, ofwdevs[i]); ++ ++ if (!error) { ++ snprintf(prefix, sizeof(prefix), "%s%d:", INITIATOR, i); ++ print_initiator(prefix, ofwdevs[i]); ++ snprintf(prefix, sizeof(prefix), "%s%d:", NETWORK, i); ++ print_nic(prefix, ofwdevs[i]); ++ snprintf(prefix, sizeof(prefix), "%s%d:", TARGET, i); ++ print_target(prefix, ofwdevs[i]); ++ } ++ } ++ } ++ if (!error) ++ putchar('\n'); ++ return error; ++} ++ ++int ++main(int argc, char **argv) ++{ ++ int error, option; ++ char *prefix; ++ ++ ++ progname = argv[0]; ++ ++ /* ++ * For powerpc, our operations are fundamentally different. ++ * ++ * Where the x86 method searches memory, we look in the ++ * device-tree to obtain the data. ++ * ++ */ ++ while (1) { ++ option = getopt(argc, argv, "be:f:hm:s:e:v"); ++ if (option == -1) ++ break; ++ switch (option) { ++ case 'b': ++ boot_selected_only = 1; ++ break; ++ case 'e': ++ break; ++ case 'f': ++ filename = optarg; ++ break; ++ case 's': ++ break; ++ case 'v': ++ debug++; ++ break; ++ default: ++ fprintf(stderr, "Unknown or bad option '%c'\n", option); ++ case 'h': ++ printf("Usage: %s OPTIONS\n" ++ "-b print only fw boot selected sections\n" ++ "-f file_to_search (default %s)\n" ++ "-s (x86 only) offset to start search\n" ++ "-e (x86 only) length of search\n" ++ "-v verbose\n", ++ progname, default_file_name); ++ exit(1); ++ } ++ } ++ ++ if (debug) ++ fprintf(stderr, "%s: file:%s; debug:%d\n", progname, filename, debug); ++ ++ devtree = find_devtree(filename); ++ if (!devtree) ++ exit(2); ++ ++ ++ if (boot_selected_only) { ++ dev_count = find_file(filename); ++ if (dev_count < 1) ++ error = 3; ++ else { ++ if (debug) ++ printf("%s:\n%s\n\n", filename, bootpath_val); ++ /* ++ * We find *almost* everything we need in the ++ * bootpath, save the mac-address. ++ */ ++ ++ if (strstr(bootpath_val, "iscsi")) { ++ ofwdevs[0] = malloc(sizeof(struct ofw_dev)); ++ if (!ofwdevs[0]) ++ exit(ENOMEM); ++ ++ error = parse_params(bootpath_val, ofwdevs[0]); ++ if (!error) ++ error = locate_mac(devtree, ofwdevs[0]); ++ ++ } ++ else ++ /* yikes! we did not boot from iscsi. tsk, tsk. */ ++ error = 1; ++ ++ if (!error) { ++ prefix = "iSCSI_INITIATOR_"; ++ print_initiator(prefix, ofwdevs[0]); ++ print_nic(prefix, ofwdevs[0]); ++ prefix = "iSCSI_TARGET_"; ++ print_target(prefix, ofwdevs[0]); ++ putchar('\n'); ++ } ++ } ++ ++ } ++ else { ++ /* ++ * As we were *not* called with the -b flag, locate ++ * and loop over all the device-tree iscsi-toe and ethernet ++ * entries. ++ * ++ * ++ */ ++ error = loop_devs(); ++ } ++ ++ if (debug) ++ dump_obp_params(ofwdevs, dev_count); ++ ++ ++ exit(error); ++} +diff --git a/utils/fwparam_ibft/iscsi_obp.h b/utils/fwparam_ibft/iscsi_obp.h +new file mode 100644 +index 0000000..2f9c6b9 +--- /dev/null ++++ b/utils/fwparam_ibft/iscsi_obp.h +@@ -0,0 +1,101 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ++ * ++ * Copyright (C) IBM Corporation. 2007 ++ * Author: Doug Maxey ++ * ++ */ ++ ++#ifndef ISCSI_OBP_H_ ++#define ISCSI_OBP_H_ ++ ++enum ofw_dev_type { ++ OFW_DT_NONE, ++ OFW_DT_BLOCK, ++ OFW_DT_NETWORK, ++ OFW_DT_ISCSI, ++}; ++ ++enum obp_tftp_qual { ++ OBP_QUAL_NONE, ++ OBP_QUAL_BOOTP, ++ OBP_QUAL_IPV6, ++ OBP_QUAL_ISCSI, ++ OBP_QUAL_PING, ++ OBP_QUAL_COUNT, /* Numnber of defined OBP qualifiers */ ++}; ++ ++enum obp_param { ++ /* ++ * Defined iscsi boot parameters. ++ */ ++ OBP_PARAM_NONE, ++ OBP_PARAM_BLKSIZE, /* default is 512 */ ++ OBP_PARAM_BOOTP_RETRIES, /* default 5 */ ++ OBP_PARAM_CHAPID, /* target chap id */ ++ OBP_PARAM_CHAPPW, /* target chap password */ ++ OBP_PARAM_CIADDR, /* client (my) ip addr */ ++ OBP_PARAM_DHCP, /* dhcp server address */ ++ OBP_PARAM_FILENAME, /* boot filename */ ++ OBP_PARAM_GIADDR, /* gateway addr */ ++ OBP_PARAM_ICHAPID, /* initiator chapid */ ++ OBP_PARAM_ICHAPPW, /* initiator chap password */ ++ OBP_PARAM_ILUN, /* misnomer, really the target lun */ ++ OBP_PARAM_INAME, /* initiator iqn */ ++ OBP_PARAM_IPORT, /* initiator port, defaults to 3260 */ ++ OBP_PARAM_ISID, /* session id */ ++ OBP_PARAM_ISNS, /* sns server address */ ++ OBP_PARAM_ITNAME, /* target iqn */ ++ OBP_PARAM_SIADDR, /* iscsi server ip address. */ ++ OBP_PARAM_SLP, /* slp server address */ ++ OBP_PARAM_SUBNET_MASK, ++ OBP_PARAM_TFTP_RETRIES, /* default 5 */ ++ OBP_PARAM_TIMEOUT, /* ping timeout period. */ ++ ++ OBP_PARAM_COUNT, /* number of defined OBP_PARAMs */ ++}; ++ ++struct ofw_obp_param { ++ unsigned char len; /* length of value string. */ ++ char val[1]; /* string value from the property */ ++}; ++ ++struct ofw_dev { ++ char *prop_path; /* where we found these properties. */ ++ enum ofw_dev_type type; /* known type of boot device. */ ++ int qual_count; /* count of qualifiers. */ ++ enum obp_tftp_qual quals[OBP_QUAL_COUNT]; ++ struct ofw_obp_param *param[OBP_PARAM_COUNT]; ++ int cfg_part; /* boot partition number. */ ++ char *dev_path; /* path to this ofw device. */ ++ unsigned char mac[6]; /* The binary mac address. */ ++}; ++ ++#define OFW_DEV_INIT { \ ++ .prop_path = NULL, \ ++ .type = OFW_DT_NONE, \ ++ .param = {0}, \ ++ .dev_path = NULL, \ ++ .cfg_part = -1 \ ++ } ++ ++const char *obp_qual_set(struct ofw_dev *ofwdev, const char *qual); ++void add_obp_parm(struct ofw_dev *ofwdev, enum obp_param parm, const char *str); ++void obp_parm_addr(struct ofw_dev *ofwdev, const char *parm, const char *addr); ++void obp_parm_iqn(struct ofw_dev *ofwdev, const char *parm, const char *iqn); ++void obp_parm_hexnum(struct ofw_dev *ofwdev, const char *parm, const char *numstr); ++void obp_parm_str(struct ofw_dev *ofwdev, const char *parm, const char *str); ++ ++#endif /* ISCSI_OBP_H_ */ +diff --git a/utils/fwparam_ibft/prom_lex.l b/utils/fwparam_ibft/prom_lex.l +new file mode 100644 +index 0000000..3e17d98 +--- /dev/null ++++ b/utils/fwparam_ibft/prom_lex.l +@@ -0,0 +1,93 @@ ++/* ++ * Copyright (c) 2006 IBM Corporation. ++ * Doug Maxey ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/* definitions */ ++%option array ++ ++%{ ++#include "prom_parse.h" ++ ++#undef LEXDEBUG ++#ifdef LEXDEBUG ++#define dbg(a) dbgprint((a)) ++#else ++#define dbg(a) do {} while (0) ++#endif /* LEXDEBUG */ ++ ++#define upval(d) \ ++ dbg(#d); \ ++ yylval.str[0] = 0; \ ++ strcat(yylval.str, yytext); \ ++ yylloc.first_column = yylloc.last_column; \ ++ yylloc.last_column += yyleng; \ ++ return d ++ ++void dbgprint(const char *item) { fprintf(stderr, "%s: \"%s\" len=%d ", item, yytext, yyleng);} ++char *strcat(char *dest, const char *src); ++ ++%} ++ ++%option noyywrap ++%option never-interactive ++ ++VDEVICE vdevice ++VDEVINST gscsi ++VDEVDEV dev ++VDEVRAW rawio ++ /* CHOSEN uses only boot related paths. */ ++CHOSEN bootpath|bootargs|iscsi-bootargs|nas-bootdevice ++BUSNAME ata|i2c|ide|pci|sata|scsi|usb ++BOOTDEV cdrom|disk|ethernet|iscsi-(disk[0-9]|toe)|sd ++HEX4 [[:xdigit:]]{1,4} ++HEX16 [[:xdigit:]]{5,16} ++IPV4 [0-9]{1,3}(\.[0-9]{1,3}){3} ++IQN iqn\.[-[:alnum:]:.]{1,219} ++OBPQUAL bootp|ipv6|iscsi ++OBPPARM blksize|bootp-retries|chapid|chappw|ciaddr|dhcp|filename|giaddr|ichapid|ichappw|ilun|iname|iport|isid|isns|itname|siaddr|slp|subnet-mask|tftp-retries ++FILENAME \\[-[:alnum:]\\\.]{1,} ++ ++%% /* rules */ ++ ++{CHOSEN} { upval(CHOSEN); } ++{VDEVICE} { upval(VDEVICE); } ++{VDEVINST} { upval(VDEVINST); } ++{VDEVDEV} { upval(VDEVDEV); } ++{VDEVRAW} { upval(VDEVRAW); } ++{OBPQUAL} { upval(OBPQUAL); } ++{BUSNAME} { upval(BUSNAME); } ++{IPV4} { upval(IPV4); } ++{IQN} { upval(IQN); } ++{BOOTDEV} { upval(BOOTDEV); } ++{OBPPARM} { upval(OBPPARM); } ++{HEX4} { upval(HEX4); } ++{HEX16} { upval(HEX16); } ++{FILENAME} { upval(FILENAME); } ++[ \t\n]+ { /* eat all whitespace. */ ++ yylloc.first_column = yylloc.last_column; ++ yylloc.last_column += yyleng; ++} ++. { /* any other single char. */ ++ dbg("??"); ++ yylloc.first_column = yylloc.last_column; ++ yylloc.last_column += yyleng; ++ return *yytext; ++} ++ ++<> yyterminate(); ++%% /* user code */ +diff --git a/utils/fwparam_ibft/prom_parse.h b/utils/fwparam_ibft/prom_parse.h +new file mode 100644 +index 0000000..d1145c9 +--- /dev/null ++++ b/utils/fwparam_ibft/prom_parse.h +@@ -0,0 +1,41 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ++ * ++ * Copyright (C) IBM Corporation. 2007 ++ * Author: Doug Maxey ++ * ++ */ ++ ++#ifndef PROM_PARSE_H_ ++#define PROM_PARSE_H_ ++ ++#include ++#include ++#include "iscsi_obp.h" ++ ++struct ofw_dev; ++void yyerror(struct ofw_dev *ofwdev, const char *msg); ++extern int yyleng; ++extern int yydebug; ++#include ++extern FILE *yyin; ++extern char yytext[]; ++int yylex(void); ++ ++#define YY_NO_UNPUT 1 /* match this with %option never-interactive. */ ++#include "prom_parse.tab.h" ++ ++ ++#endif /* PROM_PARSE_H_ */ +diff --git a/utils/fwparam_ibft/prom_parse.y b/utils/fwparam_ibft/prom_parse.y +new file mode 100644 +index 0000000..000198c +--- /dev/null ++++ b/utils/fwparam_ibft/prom_parse.y +@@ -0,0 +1,258 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ++ * ++ * Copyright (C) IBM Corporation. 2007 ++ * Author: Doug Maxey ++ * ++ */ ++/* - DEFINITION section. */ ++ ++%{ ++ /* literal block. include lines, decls, defns. */ ++//#define YYDEBUG 1 ++#if YYDEBUG ++#define DPRINT(fmt,...) printf(fmt,__VA_ARGS__) ++#else ++#define DPRINT(fmt,...) do {} while(0) ++#endif ++#include "prom_parse.h" ++#include "iscsi_obp.h" ++ ++%} ++%union { ++ char str[256]; ++} ++ ++/* definitions. */ ++%token BUSNAME BOOTDEV ++%token IPV4 IQN ++%token OBPPARM OBPQUAL ++%token HEX4 HEX16 ++%token VDEVICE VDEVINST VDEVDEV VDEVRAW ++%token CHOSEN ++%token FILENAME ++ ++%type devpath busses bus bootdev ++%type disklabel diskpart ++%type vdevice vdev_parms vdev_parm ++%type obp_quals obp_qual obp_params obp_param ++%type ipaddr ipv4 ipv6 ++%type hexpart hexseq ++ ++%locations ++%parse-param {struct ofw_dev *ofwdev} ++ ++%% ++ ++devpath: '/' { ++ DPRINT("****rootonly: \"%s\"\n", "/"); ++ } ++ | '/' busses bootdev { ++ DPRINT("****devpath busses:\n/%s/%s\n", $2, $3); ++ } ++ | '/' busses bootdev disklabel { ++ ofwdev->dev_path = malloc(strlen($2) + strlen($3) + 3); ++ sprintf(ofwdev->dev_path, "/%s/%s", $2, $3); ++ DPRINT("****devpath busses bootdev disklabel:\n/%s/%s%s\n", $2, $3, $4); ++ } ++ | '/' busses bootdev obp_quals obp_params { ++ ofwdev->dev_path = malloc(strlen($2) + strlen($3) + 3); ++ sprintf(ofwdev->dev_path, "/%s/%s", $2, $3); ++ DPRINT("****busses bootdev obp_quals obp_parms:\n/%s/%s:%s%s\n", $2, $3, $4, $5); ++ } ++ | '/' busses bootdev obp_quals obp_params disklabel { ++ ofwdev->dev_path = malloc(strlen($2) + strlen($3) + 3); ++ sprintf(ofwdev->dev_path, "/%s/%s", $2, $3); ++ DPRINT("****busses bootdev obp_quals obp_parms disklabel:\n/%s:%s%s%s\n", $2, $4, $5, $6); ++ } ++ | '/' vdevice bootdev vdev_parms obp_quals obp_params disklabel { ++ DPRINT("****vdevice bootdev obp_parms disklabel:\n/%s:%s%s%s%s\n", $2, $4, $5, $6, $7); ++ } ++ ; ++ ++busses: bus { ++ strcpy($$, $1); ++ } ++ | busses '/' bus { ++ sprintf($$, "%s/%s", $1, $3); ++ } ++ ; ++ ++bus: BUSNAME { ++ strcpy($$, $1); ++ } ++ | BUSNAME '@' HEX4 { ++ sprintf($$, "%s@%s", $1, $3); ++ } ++ | BUSNAME '@' HEX4 ',' HEX4 { ++ sprintf($$, "%s@%s,%s", $1, $3, $5); ++ } ++ | BUSNAME '@' HEX16 { ++ sprintf($$, "%s@%s", $1, $3); ++ } ++ | BUSNAME ',' HEX4 '@' HEX16 { ++ sprintf($$, "%s,%s@%s", $1, $3, $5); ++ } ++ ; ++ ++ ++bootdev: '/' BOOTDEV ':' { ++ sprintf($$, "/%s", $2); ++ } ++ | '/' BOOTDEV '@' HEX4 ':' { ++ sprintf($$, "/%s@%s", $2, $4); ++ } ++ | '/' BOOTDEV '@' HEX4 ',' HEX4 ':' { ++ sprintf($$, "/%s@%s,%s", $2, $4, $6); ++ } ++ ; ++ ++vdevice: VDEVICE '/' VDEVINST { ++ sprintf($$, "%s/%s", $1, $3); ++ } ++ ; ++ ++vdev_parms: ':' vdev_parm { ++ sprintf($$, ":%s", $2); ++ } ++ | vdev_parms ',' vdev_parm { ++ sprintf($$, "%s,%s", $1, $3); ++ } ++ | vdev_parms ',' VDEVRAW { ++ sprintf($$, "%s,%s", $1, $3); ++ } ++ ; ++ ++vdev_parm: VDEVDEV '=' CHOSEN { ++ sprintf($$, "%s=%s", $1, $3); ++ } ++ ; ++ ++obp_params: ',' obp_param { ++ sprintf($$, ",%s", $2); ++ } ++ | obp_params ',' obp_param { ++ sprintf($$, "%s,%s", $1, $3); ++ } ++ | obp_params ',' disklabel { ++ sprintf($$, "%s,%s", $1, $3); ++ } ++ ; ++ ++obp_param: HEX4 { ++ sprintf($$, "%s", $1); ++ } ++ | OBPPARM '=' HEX16 { ++ /* luns > 0 are the SAM-3+ hex representation. */ ++ obp_parm_hexnum(ofwdev, $1, $3); ++ sprintf($$, "%s=%s", $1, $3); ++ } ++ | OBPPARM '=' ipaddr { ++ obp_parm_addr(ofwdev, $1, $3); ++ sprintf($$, "%s=%s", $1, $3); ++ } ++ | OBPPARM '=' IQN { ++ obp_parm_iqn(ofwdev, $1, $3); ++ sprintf($$, "%s=%s", $1, $3); ++ } ++ | OBPPARM '=' HEX4 { ++ obp_parm_hexnum(ofwdev, $1, $3); ++ sprintf($$, "%s=%s", $1, $3); ++ } ++ | OBPPARM '=' FILENAME { ++ obp_parm_str(ofwdev, $1, $3); ++ sprintf($$, "%s=%s", $1, $3); ++ } ++ ; ++ ++obp_quals: obp_qual { ++ sprintf($$, "%s", $1); ++ } ++ | obp_quals ',' obp_qual { ++ sprintf($$, "%s,%s", $1, $3); ++ } ++ ; ++ ++obp_qual: OBPQUAL { ++ sprintf($$, "%s", obp_qual_set(ofwdev, $1)); ++ } ++ | vdev_parm { ++ sprintf($$, "%s", $1); ++ } ++ ; ++ ++ipaddr: ipv4 { ++ sprintf($$, "%s", $1); ++ } ++ | ipv6 { ++ sprintf($$, "%s", $1); ++ } ++ ; ++ ++ipv4: IPV4 { ++ sprintf($$, "%s", $1); ++ } ++ ; ++ ++ipv6: hexpart { ++ sprintf($$, "%s", $1); ++ } ++ | hexpart ':' ipv4 { ++ sprintf($$, "%s:%s", $1, $3); ++ } ++ ; ++ ++hexpart: hexseq { ++ sprintf($$, "%s", $1); ++ } ++ | hexpart "::" { ++ sprintf($$, "%s::", $1); ++ } ++ | hexpart "::" hexseq { ++ sprintf($$, "%s::%s", $1, $3); ++ } ++ | "::" hexseq { ++ sprintf($$, "::%s", $2); ++ } ++ ; ++ ++hexseq: HEX4 { ++ sprintf($$, "%s", $1); ++ } ++ | hexseq ":" HEX4 { ++ sprintf($$, "%s:%s", $1, $3); ++ } ++ ; ++ ++disklabel: diskpart { ++ sprintf($$, "%s", $1); ++ } ++ | HEX4 diskpart { ++ sprintf($$, "%s%s", $1, $2); ++ } ++ | '@' HEX4 ',' HEX4 diskpart { ++ sprintf($$, "@%s,%s%s", $2, $4, $5); ++ } ++ ; ++ ++diskpart: ':' HEX4 { ++ sprintf($$, ":%s", $2); ++ } ++ | ':' HEX4 ',' FILENAME { ++ sprintf($$, ":%s,%s", $2, $4); ++ } ++ ; ++ ++%% diff --git a/open-iscsi-iscsi-iname-Makefile.patch b/open-iscsi-iscsi-iname-Makefile.patch deleted file mode 100644 index fc37fb7..0000000 --- a/open-iscsi-iscsi-iname-Makefile.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- open-iscsi-0.5-454/usr/Makefile 2006/02/09 09:41:31 1.7 -+++ open-iscsi-0.5-454/usr/Makefile 2006/02/09 09:42:46 -@@ -33,7 +33,7 @@ - OPTFLAGS ?= -O2 -fno-inline -g - WARNFLAGS ?= -Wall -Wstrict-prototypes - CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -I../include -D$(OSNAME) $(IPC_CFLAGS) --PROGRAMS = iscsid iscsiadm iscsistart -+PROGRAMS = iscsid iscsiadm iscsistart iscsi-iname - - # sources shared between iscsid and iscsiadm - COMMON_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o idbm.o -@@ -47,5 +47,8 @@ - iscsistart: $(IPC_OBJ) $(ISCSI_LIB_SRCS) $(INITIATOR_SRCS) iscsistart.o - $(CC) $^ $(DBM_LIB) -o $@ - -+iscsi-iname: md5.o iscsi-iname.o -+ $(CC) $^ -o $@ -+ - clean: - rm -f *.o $(PROGRAMS) diff --git a/open-iscsi-suse.diff b/open-iscsi-suse.diff deleted file mode 100644 index 975e3ba..0000000 --- a/open-iscsi-suse.diff +++ /dev/null @@ -1,21 +0,0 @@ -Index: etc/initd/initd.suse -=================================================================== ---- etc/initd/initd.suse (revision 713) -+++ etc/initd/initd.suse (working copy) -@@ -23,16 +23,9 @@ - # Source LSB init functions - . /etc/rc.status - --. /etc/sysconfig/open-iscsi -- - # Reset status of this service - rc_reset - --iscsi_discovery() --{ -- $ISCSIADM -m discovery --type=$ISCSI_DISCOVERY --portal=$ISCSI_PORTAL > /dev/null --} -- - iscsi_login_all_nodes() - { - $ISCSIADM -m node 2> /dev/null | while read line; do diff --git a/open-iscsi-umount-all-luns b/open-iscsi-umount-all-luns new file mode 100644 index 0000000..1874452 --- /dev/null +++ b/open-iscsi-umount-all-luns @@ -0,0 +1,61 @@ +diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse +index 6d4cbd8..ce9fa70 100644 +--- a/etc/initd/initd.suse ++++ b/etc/initd/initd.suse +@@ -77,6 +77,39 @@ iscsi_logout_all_nodes() + return ${RETVAL:-0} + } + ++iscsi_umount_all_luns() ++{ ++ local d m dev p s ++ ++ cat /proc/mounts | sed -ne '/^\/dev\/.*/p' | while read d m t o x; do ++ if [ "$m" = "/" ] ; then ++ continue; ++ fi ++ if [ -L "$d" ] ; then ++ d=$(readlink -f $d) ++ fi ++ dev=${d##/dev} ++ ++ if [ "${dev##/sd}" = "$dev" ] ; then ++ continue; ++ fi ++ p="/sys/block${dev%%[0-9]*}" ++ ++ if [ ! -d ${p} ] && [ ! -d ${p}/device ] ; then ++ continue; ++ fi ++ ++ s=$(cd -P ${p}/device && echo $PWD) ++ ++ case "$s" in ++ */session[0-9]*/*) ++ # This is an iSCSI device ++ umount "$m" ++ ;; ++ esac ++ done ++} ++ + iscsi_list_all_nodes() + { + TARGETS=$($ISCSIADM -m session | sed 's@\[[^:]*:\(.*\)\] .*@\1@g') +@@ -110,6 +143,7 @@ case "$1" in + fi + ;; + stop) ++ iscsi_umount_all_luns + if iscsi_logout_all_nodes ; then + killproc -KILL $DAEMON + RETVAL=$? +diff --git a/include/iscsi_if.h b/include/iscsi_if.h +diff --git a/kernel/iscsi_tcp.c b/kernel/iscsi_tcp.c +diff --git a/kernel/libiscsi.c b/kernel/libiscsi.c +diff --git a/kernel/libiscsi.h b/kernel/libiscsi.h +diff --git a/kernel/scsi_transport_iscsi.c b/kernel/scsi_transport_iscsi.c +diff --git a/usr/discovery.c b/usr/discovery.c +diff --git a/usr/initiator.c b/usr/initiator.c +diff --git a/usr/initiator.h b/usr/initiator.h +diff --git a/usr/iscsid.c b/usr/iscsid.c diff --git a/open-iscsi-use-dev-urandom b/open-iscsi-use-dev-urandom deleted file mode 100644 index 96ff2d1..0000000 --- a/open-iscsi-use-dev-urandom +++ /dev/null @@ -1,103 +0,0 @@ -Index: usr/auth.c -=================================================================== ---- usr/auth.c (revision 713) -+++ usr/auth.c (working copy) -@@ -28,6 +28,8 @@ - #include - #include - #include -+#include -+#include - - #include "auth.h" - #include "initiator.h" -@@ -186,20 +188,32 @@ - - long r; - unsigned n; -+ int fd; - -+ fd = open("/dev/urandom", O_RDONLY); - while (length > 0) { - -- r = rand(); -+ if (fd) -+ read(fd, &r, sizeof(long)); -+ else -+ r = rand(); - r = r ^ (r >> 8); - r = r ^ (r >> 4); - n = r & 0x7; - -- r = rand(); -+ if (fd) -+ read(fd, &r, sizeof(long)); -+ else -+ r = rand(); - r = r ^ (r >> 8); - r = r ^ (r >> 5); - n = (n << 3) | (r & 0x7); - -- r = rand(); -+ if (fd) -+ read(fd, &r, sizeof(long)); -+ else -+ r = rand(); -+ - r = r ^ (r >> 8); - r = r ^ (r >> 5); - n = (n << 2) | (r & 0x3); -@@ -207,6 +221,8 @@ - *data++ = n; - length--; - } -+ if (fd) -+ close(fd); - } - - /** -Index: usr/chap.c -=================================================================== ---- usr/chap.c (revision 713) -+++ usr/chap.c (working copy) -@@ -324,6 +324,7 @@ - char text[CHAP_CHALLENGE_MAX * 2 + 8]; - static int chap_id; - int i; -+ int fd; - - value = text_key_find(conn, "CHAP_A"); - if (!value) -@@ -353,7 +354,8 @@ - * wise, or should we rather always use the max. allowed length of - * 1024 for the (unencoded) challenge? - */ -- conn->auth.chap.challenge_size = (rand() % (CHAP_CHALLENGE_MAX / 2)) + CHAP_CHALLENGE_MAX / 2; -+ conn->auth.chap.challenge_size = (sizeof(int) % (CHAP_CHALLENGE_MAX / 2)) + -+ CHAP_CHALLENGE_MAX / 2; - - conn->auth.chap.challenge = xmalloc(conn->auth.chap.challenge_size); - if (!conn->auth.chap.challenge) -@@ -362,11 +364,21 @@ - p = text; - strcpy(p, "0x"); - p += 2; -+ -+ fd = open("/dev/urandom", O_RDONLY); -+ if (fd) { -+ read(fd, conn->auth.chap.challenge, -+ sizeof(int) * conn->auth.chap.challenge_size); -+ } -+ - for (i = 0; i < conn->auth.chap.challenge_size; i++) { -- conn->auth.chap.challenge[i] = rand(); -+ if (!fd) { -+ conn->auth.chap.challenge[i] = rand(); -+ } - sprintf(p, "%.2hhx", conn->auth.chap.challenge[i]); - p += 2; - } -+ if (fd) close(fd); - text_key_add(conn, "CHAP_C", text); - - return 0; diff --git a/open-iscsi.changes b/open-iscsi.changes index 1338b20..48e1c38 100644 --- a/open-iscsi.changes +++ b/open-iscsi.changes @@ -1,3 +1,76 @@ +------------------------------------------------------------------- +Wed Apr 4 10:51:56 CEST 2007 - hare@suse.de + +- update to official release 2.0-754 +- update to svn r779 +- Include changes from SLES10 SP1 + +------------------------------------------------------------------- +Fri Mar 30 13:50:42 CEST 2007 - hare@suse.de + +- Include fwparam_ibft program for OpenPROM (#226682) + +------------------------------------------------------------------- +Wed Mar 28 12:24:23 CEST 2007 - hare@suse.de + +- Return status '6' when root on iSCSI is not enabled (#257979) + +------------------------------------------------------------------- +Fri Mar 16 11:51:33 CET 2007 - hare@suse.de + +- Fix typo in output of fwparam_ibft (#250445) + +------------------------------------------------------------------- +Thu Mar 8 12:49:39 CET 2007 - hare@suse.de + +- connect() to the daemon might fail on busy machines (#244649) + +------------------------------------------------------------------- +Mon Mar 5 16:29:05 CET 2007 - hare@suse.de + +- Add /usr/bin/iscsi-dbconvert to convert SLES10 GA + databases to new format (#251298) + +------------------------------------------------------------------- +Fri Feb 16 20:00:45 CET 2007 - hare@suse.de + +- Fix iscsiadm to print session info correctly (#243203) + +------------------------------------------------------------------- +Thu Feb 8 16:19:08 CET 2007 - hare@suse.de + +- update to svn r768 + - Update iSNS support (#243203) +- Correct typo in /etc/init.d/boot.open-iscsi (#243203) + +------------------------------------------------------------------- +Fri Feb 2 15:23:18 CET 2007 - hare@suse.de + +- Umount all luns on shutdown (#223484) + +------------------------------------------------------------------- +Wed Jan 31 09:43:38 CET 2007 - hare@suse.de + +- Remove the start iSCSI after Xen hack (#231077) +- Fixup /etc/init.d/boot.open-iscsi script (#239819) + +------------------------------------------------------------------- +Thu Jan 18 15:07:04 CET 2007 - hare@suse.de + +- Fix compat mode to display the record id. +- Add /sbin/iscsi-gen-initiatorname to generate + a default iSCSI initiatorname. + +------------------------------------------------------------------- +Mon Jan 15 17:47:13 CET 2007 - hare@suse.de + +- Fixup %post scripts. + +------------------------------------------------------------------- +Mon Jan 15 13:10:53 CET 2007 - hare@suse.de + +- Reference symlink to /etc/iscsid.conf in rpm. + ------------------------------------------------------------------- Fri Oct 20 14:14:33 CEST 2006 - hare@suse.de @@ -15,6 +88,13 @@ Fri Jul 28 13:19:29 CEST 2006 - olh@suse.de - remove unused boot.proc from boot.open-iscsi (#181972) +------------------------------------------------------------------- +Mon Jun 19 18:22:18 CEST 2006 - hare@suse.de + +- update to svn r606 + - Include local patches + - Fix OCFS2 hangs (#185107, #180773) + ------------------------------------------------------------------- Thu Jun 1 17:06:24 CEST 2006 - hare@suse.de diff --git a/open-iscsi.spec b/open-iscsi.spec index 423b055..7831ecc 100644 --- a/open-iscsi.spec +++ b/open-iscsi.spec @@ -1,7 +1,7 @@ # -# spec file for package open-iscsi (Version 2.0.713) +# spec file for package open-iscsi (Version 2.0.779) # -# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2007 SUSE LINUX Products GmbH, Nuernberg, Germany. # This file and all modifications and additions to the pristine # package are under the same license as the package itself. # @@ -11,26 +11,29 @@ # norootforbuild Name: open-iscsi +BuildRequires: db-devel URL: http://www.open-iscsi.org -License: GNU General Public License (GPL) - all versions +License: GNU General Public License (GPL) Group: Productivity/Networking/Other Prereq: %fillup_prereq %insserv_prereq Autoreqprov: on -Version: 2.0.713 +Version: 2.0.779 Release: 1 Provides: linux-iscsi Obsoletes: linux-iscsi -%define iscsi_release 707 +%define iscsi_release 754 Summary: Linux* Open-iSCSI Software Initiator -Source: %{name}-2.0-707.tar.gz +Source: %{name}-2.0-754.tar.gz +Source2: open-iscsi.sysconfig Source3: boot.open-iscsi -Source12: iscsi-iname.c -Patch1: %{name}-713.diff -Patch2: %{name}-suse.diff -Patch3: %{name}-use-dev-urandom -Patch12: %{name}-start-iscsi-after-xen +Source11: iscsi-gen-initiatorname.sh +Patch1: %{name}-779.diff +Patch12: %{name}-connect-retry +Patch13: %{name}-fwparam-mask-typo +Patch14: %{name}-fwparam-openprom +Patch20: %{name}-start-iscsi-after-xen Patch21: %{name}-check-active-sessions-before-delete -Patch22: %{name}-iscsi-iname-Makefile.patch +Patch23: %{name}-umount-all-luns BuildRoot: %{_tmppath}/%{name}-%{version}-build %description @@ -59,16 +62,27 @@ Authors: open-iscsi@googlegroups.com %prep -%setup -n %{name}-2.0-707 -%patch1 -p0 -%patch2 -p0 -%patch3 -p0 +%setup -n %{name}-2.0-%{iscsi_release} +%patch1 +# %patch10 +%patch23 -p1 +# %patch11 -p1 %patch12 -p1 -%patch22 -p1 -cp %{S:12} usr/ +%patch13 -p1 +%patch14 -p1 %build +%ifarch %ix86 x86_64 +%define target_arch x86 +%else +%ifarch ppc ppc64 +%define target_arch ppc +%else +%define target_arch none +%endif +%endif %{__make} OPTFLAGS="${RPM_OPT_FLAGS}" -C usr +%{__make} OPTFLAGS="${RPM_OPT_FLAGS} -U_FORTIFY_SOURCE" ARCH=%target_arch -C utils %install [ "${RPM_BUILD_ROOT}" != "/" -a -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT} @@ -76,9 +90,10 @@ make DESTDIR=${RPM_BUILD_ROOT} install_programs make DESTDIR=${RPM_BUILD_ROOT} install_etc make DESTDIR=${RPM_BUILD_ROOT} install_initd_suse make DESTDIR=${RPM_BUILD_ROOT} install_doc -install -D -m 755 usr/iscsi-iname ${RPM_BUILD_ROOT}/sbin/iscsi-iname +install -D -m 755 %{S:11} ${RPM_BUILD_ROOT}/sbin/iscsi-gen-initiatorname install -D -m 755 %{S:3} ${RPM_BUILD_ROOT}/etc/init.d/boot.open-iscsi (cd ${RPM_BUILD_ROOT}/sbin; ln -sf /etc/init.d/open-iscsi rcopen-iscsi) +(cd ${RPM_BUILD_ROOT}/etc; ln -sf iscsi/iscsid.conf iscsid.conf) %clean [ "${RPM_BUILD_ROOT}" != "/" -a -d ${RPM_BUILD_ROOT} ] && rm -rf ${RPM_BUILD_ROOT} @@ -87,25 +102,10 @@ install -D -m 755 %{S:3} ${RPM_BUILD_ROOT}/etc/init.d/boot.open-iscsi %{fillup_and_insserv -Y boot.open-iscsi} if [ -f /etc/initiatorname.iscsi ] ; then mv /etc/initiatorname.iscsi /etc/iscsi + ln -sf /etc/iscsi/initiatorname.iscsi /etc/initiatorname.iscsi fi -if [ ! -f /etc/iscsi/initiatorname.iscsi ]; then - cat << EOF >> /etc/iscsi/initiatorname.iscsi -## DO NOT EDIT OR REMOVE THIS FILE! -## If you remove this file, the iSCSI daemon will not start. -## If you change the InitiatorName, existing access control lists -## may reject this initiator. The InitiatorName must be unique -## for each iSCSI initiator. Do NOT duplicate iSCSI InitiatorNames. -EOF - ISSUEDATE="1996-04" - INAME=$(/sbin/iscsi-iname -p iqn.$ISSUEDATE.de.suse:01) - printf "InitiatorName=$INAME\n" >>/etc/iscsi/initiatorname.iscsi - chmod 0600 /etc/iscsi/initiatorname.iscsi -fi -if [ -f /var/lib/iscsi/discovery ]; then - mv /var/lib/iscsi/discovery /var/lib/open-iscsi -fi -if [ -f /var/lib/iscsi/node ] ; then - mv /var/lib/iscsi/node /var/lib/open-iscsi +if [ ! -f /etc/iscsi/initiatorname.iscsi ] ; then + /sbin/iscsi-gen-initiatorname fi %postun @@ -114,14 +114,49 @@ fi %files %defattr(-,root,root) %attr(0600,root,root) %config(noreplace) /etc/iscsi/iscsid.conf +/etc/iscsid.conf %config /etc/init.d/open-iscsi %config /etc/init.d/boot.open-iscsi -%dir /etc/iscsi /sbin/* +%dir /etc/iscsi %doc COPYING README %doc %{_mandir}/man8/* -%changelog -n open-iscsi +%changelog +* Wed Apr 04 2007 - hare@suse.de +- update to official release 2.0-754 +- update to svn r779 +- Include changes from SLES10 SP1 +* Fri Mar 30 2007 - hare@suse.de +- Include fwparam_ibft program for OpenPROM (#226682) +* Wed Mar 28 2007 - hare@suse.de +- Return status '6' when root on iSCSI is not enabled (#257979) +* Fri Mar 16 2007 - hare@suse.de +- Fix typo in output of fwparam_ibft (#250445) +* Thu Mar 08 2007 - hare@suse.de +- connect() to the daemon might fail on busy machines (#244649) +* Mon Mar 05 2007 - hare@suse.de +- Add /usr/bin/iscsi-dbconvert to convert SLES10 GA + databases to new format (#251298) +* Fri Feb 16 2007 - hare@suse.de +- Fix iscsiadm to print session info correctly (#243203) +* Thu Feb 08 2007 - hare@suse.de +- update to svn r768 + - Update iSNS support (#243203) +- Correct typo in /etc/init.d/boot.open-iscsi (#243203) +* Fri Feb 02 2007 - hare@suse.de +- Umount all luns on shutdown (#223484) +* Wed Jan 31 2007 - hare@suse.de +- Remove the start iSCSI after Xen hack (#231077) +- Fixup /etc/init.d/boot.open-iscsi script (#239819) +* Thu Jan 18 2007 - hare@suse.de +- Fix compat mode to display the record id. +- Add /sbin/iscsi-gen-initiatorname to generate + a default iSCSI initiatorname. +* Mon Jan 15 2007 - hare@suse.de +- Fixup %%post scripts. +* Mon Jan 15 2007 - hare@suse.de +- Reference symlink to /etc/iscsid.conf in rpm. * Fri Oct 20 2006 - hare@suse.de - Update to svn r713 - Use /dev/urandom instead of rand() (#180837) @@ -130,6 +165,10 @@ fi - Update to official version 2.0-707 * Fri Jul 28 2006 - olh@suse.de - remove unused boot.proc from boot.open-iscsi (#181972) +* Mon Jun 19 2006 - hare@suse.de +- update to svn r606 + - Include local patches + - Fix OCFS2 hangs (#185107, #180773) * Thu Jun 01 2006 - hare@suse.de - Added new startmode 'onboot' for root on iSCSI - Added new init script boot.open-iscsi startup diff --git a/open-iscsi.sysconfig b/open-iscsi.sysconfig new file mode 100644 index 0000000..0080e95 --- /dev/null +++ b/open-iscsi.sysconfig @@ -0,0 +1,17 @@ +## Path: Network/iSCSI/Client +## Description: iSCSI Default Portal +## Type: string +## Default: "" +# +# The iSCSI Default Portal to use on startup. Use either the hostname +# or IP number of the machine providing the iSCSI Targets to use +# +ISCSI_PORTAL="" + +## Type: list(sendtargets,isns,slp) +## Default: sendtargets +# +# The iSCSI discovery method to use. Currently only 'sendtargets' is +# implemented. +# +ISCSI_DISCOVERY="sendtargets"