From: Mike Christie Subject: Update open-iscsi to latest git patches X-Git: 428e08c659f2e3792962b90f098f1bb33bfe1411 Update to latest patches from git. Signed-off-by: Hannes Reinecke diff --git a/README b/README index 1f1d9fc..43a4569 100644 --- a/README +++ b/README @@ -186,11 +186,15 @@ Usage: iscsiadm [OPTION] perform [type] discovery for target portal with ip-address [ip] and port [port]. - By default this command will remove records - for portals no longer returned. And, if a portal is - returned by the target, then the discovery command - will create a new record or modify an existing one - with values from iscsi.conf and the command line. + By default this command will searh for a discovery + record for ip:port, and if found will use the + record's settings for discovery. If a record does + not exist iscsiadm will create one. It will then + remove records for portals no longer returned. And, + if a portal is returned by the target, then the + discovery command will create a new record or modify + an existing one with values from iscsi.conf and the + command line. [op] can be passed in multiple times to this command, and it will alter the DB manipulation. @@ -200,8 +204,8 @@ Usage: iscsiadm [OPTION] not yet have records in the db. If [op] is passed in and the value is - "update", iscsiadm will update records using info - from iscsi.conf and the command line for portals + "update", iscsiadm will update node records using + info from iscsi.conf and the command line for portals that are returned during discovery and have a record in the db. diff --git a/doc/iscsiadm.8 b/doc/iscsiadm.8 index 8599efa..8131054 100644 --- a/doc/iscsiadm.8 +++ b/doc/iscsiadm.8 @@ -134,9 +134,9 @@ record. .IP \fInew\fR creates a new database record for a given \fIportal\fR (IP address and port number). In discovery mode, iscsiadm will create new records for portals returned by the target. .IP -\fIdelete\fR deletes a specified \fIrecid\fR. In discovery node, iscsiadm will delete records for portals that are no longer returned. +\fIdelete\fR deletes a specified \fIrecid\fR. In discovery node, if iscsiadm is performing discovery it will delete records for portals that are no longer returned. .IP -\fIupdate\fR will update the \fIrecid\fR with \fIname\fR to the specified \fIvalue\fR. In discovery node the \fIrecid\fR, \fIname\fR and \fIvalue\fR arguments are not needed. The update operation will operate on the portals returned by the target, and will update the record with info from the config file and command line. +\fIupdate\fR will update the \fIrecid\fR with \fIname\fR to the specified \fIvalue\fR. In discovery node, if iscsiadm is performing discovery the \fIrecid\fR, \fIname\fR and \fIvalue\fR arguments are not needed. The update operation will operate on the portals returned by the target, and will update the node records with info from the config file and command line. .IP \fIshow\fR is the default behaviour for node, discovery and iface mode. It is also used when there are no commands passed into session mode and a running diff --git a/etc/initd/initd.debian b/etc/initd/initd.debian index c0dfd1e..59bf59b 100644 --- a/etc/initd/initd.debian +++ b/etc/initd/initd.debian @@ -46,11 +46,17 @@ stoptargets() { log_daemon_msg "Disconnecting iSCSI targets" sync $ADM -m node --logoutall=all - log_end_msg 0 + RETVAL=$? + log_end_msg $RETVAL } stop() { stoptargets + if [ $RETVAL -ne 0 ]; then + log_failure_msg "Could not stop all targets, try again later" + return $RETVAL + fi + log_daemon_msg "Stopping iSCSI initiator service" start-stop-daemon --stop --quiet --pidfile $PIDFILE --exec $DAEMON rm -f $PIDFILE @@ -68,11 +74,19 @@ stop() { restart() { stop + if [ $RETVAL -ne 0 ]; then + log_failure_msg "Stopping iSCSI initiator service failed, not starting" + return $RETVAL + fi start } restarttargets() { stoptargets + if [ $RETVAL -ne 0 ]; then + log_failure_msg "Could not stop all targets, try again later" + return $RETVAL + fi starttargets } diff --git a/etc/initd/initd.redhat b/etc/initd/initd.redhat index d68f135..c767cfb 100644 --- a/etc/initd/initd.redhat +++ b/etc/initd/initd.redhat @@ -39,6 +39,11 @@ stop() echo -n $"Stopping iSCSI initiator service: " sync iscsiadm -m node --logoutall=all + RETVAL=$? + if [ $RETVAL -ne 0 ]; then + echo "Could not logout from all nodes, try again later" + return $RETVAL + fi killproc iscsid rm -f /var/run/iscsid.pid [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/open-iscsi @@ -63,6 +68,10 @@ stop() restart() { stop + if [ $RETVAL -ne 0 ]; then + echo "Stopping iSCSI initiator service failed, not starting" + return $RETVAL + fi start } @@ -76,6 +85,10 @@ case "$1" in ;; restart) stop + if [ $RETVAL -ne 0 ]; then + echo "Stopping iSCSI initiator service failed, not starting" + exit $RETVAL + fi start ;; status) diff --git a/etc/initd/initd.suse b/etc/initd/initd.suse index 23bbac0..22901af 100644 --- a/etc/initd/initd.suse +++ b/etc/initd/initd.suse @@ -5,7 +5,7 @@ ### BEGIN INIT INFO # Provides: iscsi # Required-Start: $network -# Should-Start: +# Should-Start: iscsitarget # Required-Stop: # Should-Stop: # Default-Start: 3 5 @@ -192,6 +192,11 @@ case "$1" in ;; restart) $0 stop + RETVAL=$? + if [ "$RETVAL" != "0" ]; then + echo "Stopping iSCSI initiator service failed, not starting" + exit $RETVAL + fi sleep 1 $0 start ;; diff --git a/etc/iscsid.conf b/etc/iscsid.conf index a065d53..e8b62f0 100644 --- a/etc/iscsid.conf +++ b/etc/iscsid.conf @@ -14,6 +14,22 @@ #isns.address = 192.168.0.1 #isns.port = 3205 +###################### +# iscsid daemon config +###################### +# If you want iscsid to start the first time a iscsi tool +# needs to access it, instead of starting it when the init +# scripts run, set the iscsid startup command here. This +# should normally only need to be done by distro package +# maintainers. +# +# Default for Fedora and RHEL. (uncomment to activate). +# iscsid.startup = /etc/rc.d/init.d/iscsid force-start +# +# Default for upstream open-iscsi scripts (uncomment to activate). +iscsid.startup = /sbin/iscsid + + ############################# # NIC/HBA and driver settings ############################# @@ -73,6 +89,10 @@ node.startup = manual # before failing SCSI commands back to the application when running # the Linux SCSI Layer error handler, edit the line. # The value is in seconds and the default is 120 seconds. +# Special values: +# - If the value is 0, IO will be failed immediately. +# - If the value is less than 0, IO will remain queued until the session +# is logged back in, or until the user runs the logout command. node.session.timeo.replacement_timeout = 120 # To specify the time to wait for login to complete, edit the line. @@ -101,7 +121,14 @@ node.session.err_timeo.abort_timeout = 15 # before failing the operation and trying session re-establishment # edit the line. # The value is in seconds and the default is 30 seconds. -node.session.err_timeo.lu_reset_timeout = 20 +node.session.err_timeo.lu_reset_timeout = 30 + +# To specify the time to wait for a target response +# before failing the operation and trying session re-establishment +# edit the line. +# The value is in seconds and the default is 30 seconds. +node.session.err_timeo.tgt_reset_timeout = 30 + #****** # Retry diff --git a/include/iscsi_if.h b/include/iscsi_if.h index ba2e909..be72e1a 100644 --- a/include/iscsi_if.h +++ b/include/iscsi_if.h @@ -21,8 +21,18 @@ #ifndef ISCSI_IF_H #define ISCSI_IF_H +#ifdef __KERNEL__ +#include +#include +#else +#include +#endif + #include "iscsi_proto.h" +#define ISCSI_NL_GRP_ISCSID 1 +#define ISCSI_NL_GRP_UIP 2 + #define UEVENT_BASE 10 #define KEVENT_BASE 100 #define ISCSI_ERR_BASE 1000 @@ -53,6 +63,8 @@ enum iscsi_uevent_e { ISCSI_UEVENT_CREATE_BOUND_SESSION = UEVENT_BASE + 18, ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST = UEVENT_BASE + 19, + ISCSI_UEVENT_PATH_UPDATE = UEVENT_BASE + 20, + /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, ISCSI_KEVENT_CONN_ERROR = KEVENT_BASE + 2, @@ -60,6 +72,9 @@ enum iscsi_uevent_e { ISCSI_KEVENT_DESTROY_SESSION = KEVENT_BASE + 4, ISCSI_KEVENT_UNBIND_SESSION = KEVENT_BASE + 5, ISCSI_KEVENT_CREATE_SESSION = KEVENT_BASE + 6, + + ISCSI_KEVENT_PATH_REQ = KEVENT_BASE + 7, + ISCSI_KEVENT_IF_DOWN = KEVENT_BASE + 8, }; enum iscsi_tgt_dscvr { @@ -159,6 +174,9 @@ struct iscsi_uevent { uint32_t param; /* enum iscsi_host_param */ uint32_t len; } set_host_param; + struct msg_set_path { + uint32_t host_no; + } set_path; } u; union { /* messages k -> u */ @@ -192,10 +210,39 @@ struct iscsi_uevent { struct msg_transport_connect_ret { uint64_t handle; } ep_connect_ret; + struct msg_req_path { + uint32_t host_no; + } req_path; + struct msg_notify_if_down { + uint32_t host_no; + } notify_if_down; } r; } __attribute__ ((aligned (sizeof(uint64_t)))); /* + * To keep the struct iscsi_uevent size the same for userspace code + * compatibility, the main structure for ISCSI_UEVENT_PATH_UPDATE and + * ISCSI_KEVENT_PATH_REQ is defined separately and comes after the + * struct iscsi_uevent in the NETLINK_ISCSI message. + */ +struct iscsi_path { + uint64_t handle; + uint8_t mac_addr[6]; + uint8_t mac_addr_old[6]; + uint32_t ip_addr_len; /* 4 or 16 */ + union { + struct in_addr v4_addr; + struct in6_addr v6_addr; + } src; + union { + struct in_addr v4_addr; + struct in6_addr v6_addr; + } dst; + uint16_t vlan_id; + uint16_t pmtu; +} __attribute__ ((aligned (sizeof(uint64_t)))); + +/* * Common error codes */ enum iscsi_err { @@ -220,6 +267,7 @@ enum iscsi_err { ISCSI_ERR_NO_SCSI_CMD = ISCSI_ERR_BASE + 17, ISCSI_ERR_INVALID_HOST = ISCSI_ERR_BASE + 18, ISCSI_ERR_XMIT_FAILED = ISCSI_ERR_BASE + 19, + ISCSI_ERR_TCP_CONN_CLOSE = ISCSI_ERR_BASE + 20, }; /* @@ -268,6 +316,8 @@ enum iscsi_param { ISCSI_PARAM_IFACE_NAME, ISCSI_PARAM_ISID, ISCSI_PARAM_INITIATOR_NAME, + + ISCSI_PARAM_TGT_RESET_TMO, /* must always be last */ ISCSI_PARAM_MAX, }; @@ -307,6 +357,7 @@ enum iscsi_param { #define ISCSI_IFACE_NAME (1ULL << ISCSI_PARAM_IFACE_NAME) #define ISCSI_ISID (1ULL << ISCSI_PARAM_ISID) #define ISCSI_INITIATOR_NAME (1ULL << ISCSI_PARAM_INITIATOR_NAME) +#define ISCSI_TGT_RESET_TMO (1ULL << ISCSI_PARAM_TGT_RESET_TMO) /* iSCSI HBA params */ enum iscsi_host_param { diff --git a/include/iscsi_proto.h b/include/iscsi_proto.h index d1e0589..1c69feb 100644 --- a/include/iscsi_proto.h +++ b/include/iscsi_proto.h @@ -296,6 +296,8 @@ struct iscsi_tm { #define ISCSI_TM_FUNC_TARGET_COLD_RESET 7 #define ISCSI_TM_FUNC_TASK_REASSIGN 8 +#define ISCSI_TM_FUNC_VALUE(hdr) ((hdr)->flags & ISCSI_FLAG_TM_FUNC_MASK) + /* SCSI Task Management Response Header */ struct iscsi_tm_rsp { uint8_t opcode; diff --git a/kernel/2.6.14-23_compat.patch b/kernel/2.6.14-23_compat.patch index ab233bb..78b0881 100644 --- a/kernel/2.6.14-23_compat.patch +++ b/kernel/2.6.14-23_compat.patch @@ -1,35 +1,39 @@ +From 10f26ba1e4e0058a25c87b2ad00c71f152212a3a Mon Sep 17 00:00:00 2001 +From: Rakesh Ranjan +Date: Tue, 10 Nov 2009 19:02:13 +0530 +Subject: [PATCH] libiscsi 2.6.14-23_compat port + + +Signed-off-by: Rakesh Ranjan +--- + iscsi_tcp.c | 13 +-- + libiscsi.c | 102 ++++++++++----- + libiscsi.h | 1 + + libiscsi_tcp.c | 22 ++- + open_iscsi_compat.h | 356 ++++++++++++++++++++++++++++++++++++++++++++++++ + scsi_transport_iscsi.c | 273 +++++++++++++++++++------------------ + scsi_transport_iscsi.h | 3 +- + 7 files changed, 586 insertions(+), 184 deletions(-) + create mode 100644 open_iscsi_compat.h + diff --git a/iscsi_tcp.c b/iscsi_tcp.c -index caa116c..71df5b9 100644 +index aa4abdb..2c21157 100644 --- a/iscsi_tcp.c +++ b/iscsi_tcp.c -@@ -456,11 +456,9 @@ static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task, +@@ -477,10 +477,9 @@ static int iscsi_sw_tcp_pdu_init(struct iscsi_task *task, if (!task->sc) iscsi_sw_tcp_send_linear_data_prep(conn, task->data, count); else { - struct scsi_data_buffer *sdb = scsi_out(task->sc); -- + - err = iscsi_sw_tcp_send_data_prep(conn, sdb->table.sgl, - sdb->table.nents, offset, -- count); + err = iscsi_sw_tcp_send_data_prep(conn, scsi_sglist(task->sc), -+ scsi_sg_count(task->sc), -+ offset, count); ++ scsi_sg_count(task->sc), offset, + count); } - if (err) { -@@ -793,7 +791,11 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, - shost->max_lun = iscsi_max_lun; - shost->max_id = 0; - shost->max_channel = 0; -+#ifndef SCSI_MAX_VARLEN_CDB_SIZE -+ shost->max_cmd_len = 16; -+#else - shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE; -+#endif - - if (iscsi_host_add(shost, NULL)) - goto free_host; -@@ -832,12 +834,6 @@ static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) +@@ -853,12 +852,6 @@ static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) iscsi_host_free(shost); } @@ -42,42 +46,18 @@ index caa116c..71df5b9 100644 static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev) { blk_queue_bounce_limit(sdev->request_queue, BLK_BOUNCE_ANY); -@@ -846,6 +842,9 @@ static int iscsi_sw_tcp_slave_configure(struct scsi_device *sdev) - } - - static struct scsi_host_template iscsi_sw_tcp_sht = { -+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,24) -+ .use_sg_chaining = ENABLE_SG_CHAINING, -+#endif - .module = THIS_MODULE, - .name = "iSCSI Initiator over TCP/IP", - .queuecommand = iscsi_queuecommand, -@@ -856,9 +855,8 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { +@@ -877,9 +870,7 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { .cmd_per_lun = ISCSI_DEF_CMD_PER_LUN, .eh_abort_handler = iscsi_eh_abort, .eh_device_reset_handler= iscsi_eh_device_reset, - .eh_target_reset_handler= iscsi_eh_target_reset, -+ .eh_host_reset_handler = iscsi_eh_target_reset, .use_clustering = DISABLE_CLUSTERING, - .slave_alloc = iscsi_sw_tcp_slave_alloc, .slave_configure = iscsi_sw_tcp_slave_configure, .target_alloc = iscsi_target_alloc, .proc_name = "iscsi_tcp", -diff --git a/iscsi_tcp.h b/iscsi_tcp.h -index f9a4044..ab20530 100644 ---- a/iscsi_tcp.h -+++ b/iscsi_tcp.h -@@ -22,6 +22,8 @@ - #ifndef ISCSI_SW_TCP_H - #define ISCSI_SW_TCP_H - -+#include "open_iscsi_compat.h" -+ - #include "libiscsi.h" - #include "libiscsi_tcp.h" - diff --git a/libiscsi.c b/libiscsi.c -index fe4b66e..6217f76 100644 +index 0b810b6..8eb6ec0 100644 --- a/libiscsi.c +++ b/libiscsi.c @@ -24,7 +24,10 @@ @@ -91,16 +71,16 @@ index fe4b66e..6217f76 100644 #include #include #include -@@ -60,6 +63,8 @@ MODULE_PARM_DESC(debug_libiscsi, "Turn on debugging for libiscsi module. " - __func__, ##arg); \ - } while (0); +@@ -38,6 +41,8 @@ + #include "scsi_transport_iscsi.h" + #include "libiscsi.h" +#include "open_iscsi_compat.h" + - /* Serial Number Arithmetic, 32 bits, less than, RFC1982 */ - #define SNA32_CHECK 2147483648UL - -@@ -229,7 +234,7 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task) + static int iscsi_dbg_lib_conn; + module_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int, + S_IRUGO | S_IWUSR); +@@ -255,7 +260,7 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task) sizeof(rlen_ahdr->reserved)); rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH; rlen_ahdr->reserved = 0; @@ -109,7 +89,48 @@ index fe4b66e..6217f76 100644 ISCSI_DBG_SESSION(task->conn->session, "bidi-in rlen_ahdr->read_length(%d) " -@@ -300,7 +305,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) +@@ -265,6 +270,40 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task) + return 0; + } + ++#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)) && \ ++ !defined(SLEC1) ++/** ++ * scsilun_to_int: convert a scsi_lun to an int ++ * @scsilun: struct scsi_lun to be converted. ++ * ++ * Description: ++ * Convert @scsilun from a struct scsi_lun to a four byte host byte-ordered ++ * integer, and return the result. The caller must check for ++ * truncation before using this function. ++ * ++ * Notes: ++ * The struct scsi_lun is assumed to be four levels, with each level ++ * effectively containing a SCSI byte-ordered (big endian) short; the ++ * addressing bits of each level are ignored (the highest two bits). ++ * For a description of the LUN format, post SCSI-3 see the SCSI ++ * Architecture Model, for SCSI-3 see the SCSI Controller Commands. ++ * ++ * Given a struct scsi_lun of: 0a 04 0b 03 00 00 00 00, this function returns ++ * the integer: 0x0b030a04 ++ **/ ++static int scsilun_to_int(struct scsi_lun *scsilun) ++{ ++ int i; ++ unsigned int lun; ++ ++ lun = 0; ++ for (i = 0; i < sizeof(lun); i += 2) ++ lun = lun | (((scsilun->scsi_lun[i] << 8) | ++ scsilun->scsi_lun[i + 1]) << (i * 8)); ++ return lun; ++} ++#endif ++ + /** + * iscsi_check_tmf_restrictions - check if a task is affected by TMF + * @task: iscsi task +@@ -410,7 +449,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) return rc; } if (sc->sc_data_direction == DMA_TO_DEVICE) { @@ -118,7 +139,7 @@ index fe4b66e..6217f76 100644 struct iscsi_r2t_info *r2t = &task->unsol_r2t; hdr->data_length = cpu_to_be32(out_len); -@@ -346,7 +351,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) +@@ -456,7 +495,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) } else { hdr->flags |= ISCSI_FLAG_CMD_FINAL; zero_data(hdr->dlength); @@ -127,7 +148,7 @@ index fe4b66e..6217f76 100644 if (sc->sc_data_direction == DMA_FROM_DEVICE) hdr->flags |= ISCSI_FLAG_CMD_READ; -@@ -373,7 +378,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) +@@ -485,7 +524,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", conn->id, sc, sc->cmnd[0], task->itt, scsi_bufflen(sc), @@ -136,7 +157,7 @@ index fe4b66e..6217f76 100644 session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); return 0; -@@ -510,12 +515,7 @@ static void fail_scsi_task(struct iscsi_task *task, int err) +@@ -647,12 +686,7 @@ static void fail_scsi_task(struct iscsi_task *task, int err) state = ISCSI_TASK_ABRT_TMF; sc->result = err << 16; @@ -150,7 +171,7 @@ index fe4b66e..6217f76 100644 iscsi_complete_task(task, state); } -@@ -706,7 +706,7 @@ invalid_datalen: +@@ -852,7 +886,7 @@ invalid_datalen: goto out; } @@ -159,18 +180,18 @@ index fe4b66e..6217f76 100644 if (datalen < senselen) goto invalid_datalen; -@@ -723,8 +723,8 @@ invalid_datalen: +@@ -869,8 +903,8 @@ invalid_datalen: if (scsi_bidi_cmnd(sc) && res_count > 0 && (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW || - res_count <= scsi_in(sc)->length)) - scsi_in(sc)->resid = res_count; -+ res_count <= scsi_bufflen(sc))) ++ res_count <= scsi_bufflen(sc))) + scsi_set_resid(sc, res_count); else sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; } -@@ -773,8 +773,8 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, +@@ -919,8 +953,8 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, if (res_count > 0 && (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || @@ -181,7 +202,19 @@ index fe4b66e..6217f76 100644 else sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; } -@@ -1498,12 +1498,7 @@ fault: +@@ -1716,7 +1750,11 @@ reject: + ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", + sc->cmnd[0], reason); + spin_lock(host->host_lock); ++#if (defined RHELC1) || (defined SLEC1) ++ return SCSI_MLQUEUE_DEVICE_BUSY; ++#else + return SCSI_MLQUEUE_TARGET_BUSY; ++#endif + + prepd_fault: + sc->scsi_done = NULL; +@@ -1725,12 +1763,7 @@ fault: spin_unlock(&session->lock); ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); @@ -195,24 +228,104 @@ index fe4b66e..6217f76 100644 done(sc); spin_lock(host->host_lock); return 0; +@@ -2283,13 +2316,11 @@ EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); + * This function will wait for a relogin, session termination from + * userspace, or a recovery/replacement timeout. + */ +-static int iscsi_eh_session_reset(struct scsi_cmnd *sc) ++static int iscsi_eh_session_reset(struct iscsi_cls_session *cls_session) + { +- struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + +- cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + conn = session->leadconn; + +@@ -2335,7 +2366,7 @@ failed: + return SUCCESS; + } + +-static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) ++static void iscsi_prep_tgt_reset_pdu(struct iscsi_cls_session *cls_session, struct iscsi_tm *hdr) + { + memset(hdr, 0, sizeof(*hdr)); + hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; +@@ -2345,24 +2376,22 @@ static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) + } + + /** +- * iscsi_eh_target_reset - reset target ++ * iscsi_target_reset - reset target + * @sc: scsi command + * + * This will attempt to send a warm target reset. If that fails + * then we will drop the session and attempt ERL0 recovery. + */ +-int iscsi_eh_target_reset(struct scsi_cmnd *sc) ++int iscsi_target_reset(struct iscsi_cls_session *cls_session) + { +- struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + struct iscsi_tm *hdr; + int rc = FAILED; + +- cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + +- ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc, ++ ISCSI_DBG_EH(session, "tgt Reset [cls_session %p tgt %s]\n", cls_session, + session->targetname); + + mutex_lock(&session->eh_mutex); +@@ -2381,7 +2410,7 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc) + conn->tmf_state = TMF_QUEUED; + + hdr = &conn->tmhdr; +- iscsi_prep_tgt_reset_pdu(sc, hdr); ++ iscsi_prep_tgt_reset_pdu(cls_session, hdr); + + if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, + session->tgt_reset_timeout)) { +@@ -2423,9 +2452,18 @@ done: + mutex_unlock(&session->eh_mutex); + + if (rc == FAILED) +- rc = iscsi_eh_session_reset(sc); ++ rc = iscsi_eh_session_reset(cls_session); + return rc; + } ++EXPORT_SYMBOL_GPL(iscsi_target_reset); ++ ++int iscsi_eh_target_reset(struct scsi_cmnd *sc) ++{ ++ struct iscsi_cls_session *cls_session; ++ ++ cls_session = starget_to_session(scsi_target(sc->device)); ++ return iscsi_target_reset(cls_session); ++} + EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); + + /* diff --git a/libiscsi.h b/libiscsi.h -index 1798fbe..c9174ec 100644 +index 76e80e7..41c287d 100644 --- a/libiscsi.h +++ b/libiscsi.h -@@ -32,6 +32,8 @@ - #include "iscsi_if.h" - #include "scsi_transport_iscsi.h" - -+#include "open_iscsi_compat.h" -+ - struct scsi_transport_template; - struct scsi_host_template; - struct scsi_device; +@@ -337,6 +337,7 @@ struct iscsi_host { + 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_target_reset(struct scsi_cmnd *sc); ++extern int iscsi_target_reset(struct iscsi_cls_session *cls_session); + extern int iscsi_eh_device_reset(struct scsi_cmnd *sc); + extern int iscsi_queuecommand(struct scsi_cmnd *sc, + void (*done)(struct scsi_cmnd *)); diff --git a/libiscsi_tcp.c b/libiscsi_tcp.c -index c2b535b..76ead4b 100644 +index 4051e62..a031e2e 100644 --- a/libiscsi_tcp.c +++ b/libiscsi_tcp.c -@@ -360,6 +360,16 @@ iscsi_segment_seek_sg(struct iscsi_segment *segment, +@@ -360,6 +360,17 @@ iscsi_segment_seek_sg(struct iscsi_segment *segment, struct scatterlist *sg; unsigned int i; @@ -220,16 +333,17 @@ index c2b535b..76ead4b 100644 + * older kernels could send use_sg=0 for commands like sgio + * or scsi-ml commands. + */ ++ + if (!sg_count) { + iscsi_segment_init_linear(segment, (void *)sg_list + offset, -+ size, done, hash); ++ size, done, hash); + return 0; + } + __iscsi_segment_init(segment, size, done, hash); for_each_sg(sg_list, sg, sg_count, i) { if (offset < sg->length) { -@@ -471,7 +481,7 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) +@@ -471,7 +482,7 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, struct iscsi_task *task) struct iscsi_tcp_task *tcp_task = task->dd_data; struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; int datasn = be32_to_cpu(rhdr->datasn); @@ -238,7 +352,7 @@ index c2b535b..76ead4b 100644 /* * lib iscsi will update this in the completion handling if there -@@ -565,11 +575,11 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) +@@ -565,11 +576,11 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task) r2t->data_length, session->max_burst); r2t->data_offset = be32_to_cpu(rhdr->data_offset); @@ -252,7 +366,7 @@ index c2b535b..76ead4b 100644 __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*)); return ISCSI_ERR_DATALEN; -@@ -668,7 +678,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) +@@ -668,7 +679,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) if (tcp_conn->in.datalen) { struct iscsi_tcp_task *tcp_task = task->dd_data; struct hash_desc *rx_hash = NULL; @@ -260,9 +374,9 @@ index c2b535b..76ead4b 100644 /* * Setup copy of Data-In into the Scsi_Cmnd -@@ -687,8 +696,8 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) - tcp_task->data_offset, +@@ -688,8 +698,8 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) tcp_conn->in.datalen); + task->last_xfer = jiffies; rc = iscsi_segment_seek_sg(&tcp_conn->in.segment, - sdb->table.sgl, - sdb->table.nents, @@ -271,24 +385,12 @@ index c2b535b..76ead4b 100644 tcp_task->data_offset, tcp_conn->in.datalen, iscsi_tcp_process_data_in, -diff --git a/libiscsi_tcp.h b/libiscsi_tcp.h -index 3bacef5..5ea284d 100644 ---- a/libiscsi_tcp.h -+++ b/libiscsi_tcp.h -@@ -21,6 +21,7 @@ - #ifndef LIBISCSI_TCP_H - #define LIBISCSI_TCP_H - -+#include "open_iscsi_compat.h" - #include "libiscsi.h" - - struct iscsi_tcp_conn; diff --git a/open_iscsi_compat.h b/open_iscsi_compat.h new file mode 100644 -index 0000000..763d07a +index 0000000..5c98fef --- /dev/null +++ b/open_iscsi_compat.h -@@ -0,0 +1,321 @@ +@@ -0,0 +1,356 @@ +#ifndef OPEN_ISCSI_COMPAT +#define OPEN_ISCSI_COMPAT + @@ -296,6 +398,7 @@ index 0000000..763d07a +#include +#include +#include ++#include + +#ifndef SCAN_WILD_CARD +#define SCAN_WILD_CARD ~0 @@ -319,12 +422,26 @@ index 0000000..763d07a +#define mutex_init init_MUTEX +#endif + -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,19) ++#ifdef RHEL_RELEASE_CODE ++#if (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5, 4)) ++#define RHELC1 1 ++#endif ++#endif ++ ++#ifdef SLE_VERSION_CODE ++#if (SLE_VERSION_CODE < SLE_VERSION(11, 0, 0)) ++#define SLEC1 1 ++#endif ++#endif ++ ++ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,19) +struct delayed_work { + struct work_struct work; +}; + +#define cancel_delayed_work(_dwork) cancel_delayed_work(&(_dwork)->work) ++#define INIT_DELAYED_WORK(_work,_func) INIT_WORK(&(_work)->work, _func) + +static inline void INIT_WORK_compat(struct work_struct *work, void *func) +{ @@ -333,8 +450,6 @@ index 0000000..763d07a + +#undef INIT_WORK +#define INIT_WORK(_work, _func) INIT_WORK_compat(_work, _func) -+#define INIT_DELAYED_WORK(_work,_func) INIT_WORK(&(_work)->work, _func) -+ +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12) @@ -357,7 +472,9 @@ index 0000000..763d07a + +#endif + -+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) ++#ifdef RHEL_RELEASE_CODE && \ ++ RHEL_RELEASE_CODE != RHEL_RELEASE_VERSION(4,8) + +#define gfp_t unsigned + @@ -369,11 +486,13 @@ index 0000000..763d07a +} + +#endif ++#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) + +#include "linux/crypto.h" + ++#if !defined SLEC1 +#define CRYPTO_ALG_ASYNC 0x00000080 +struct hash_desc +{ @@ -420,19 +539,21 @@ index 0000000..763d07a +{ + crypto_free_tfm(tfm); +} -+ -+int kernel_getsockname(struct socket *sock, struct sockaddr *addr, ++#endif ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) \ ++ && !(defined RHELC1) ++static inline int kernel_getsockname(struct socket *sock, struct sockaddr *addr, + int *addrlen) +{ + return sock->ops->getname(sock, addr, addrlen, 0); +} + -+int kernel_getpeername(struct socket *sock, struct sockaddr *addr, ++static inline int kernel_getpeername(struct socket *sock, struct sockaddr *addr, + int *addrlen) +{ + return sock->ops->getname(sock, addr, addrlen, 1); +} -+ ++#endif +#endif + +#ifndef bool @@ -441,7 +562,8 @@ index 0000000..763d07a + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,20) +#ifdef RHEL_RELEASE_VERSION -+#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2) ++#if RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(5,2) && \ ++ RHEL_RELEASE_CODE != RHEL_RELEASE_VERSION(4,8) +static inline int is_power_of_2(unsigned long n) +{ + return (n != 0 && ((n & (n - 1)) == 0)); @@ -518,6 +640,19 @@ index 0000000..763d07a + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) + ++#ifdef SLE_VERSION_CODE ++#if SLE_VERSION_CODE == SLE_VERSION(10,2,0) ++ ++static inline unsigned fls_long(unsigned long l) ++{ ++ if (sizeof(l) == 4) ++ return fls(l); ++ return fls64(l); ++} ++ ++#endif ++#endif ++ +static inline unsigned long rounddown_pow_of_two(unsigned long n) +{ + return 1UL << (fls_long(n) - 1); @@ -562,14 +697,12 @@ index 0000000..763d07a +#define netlink_kernel_release(_nls) \ + sock_release(_nls->sk_socket) + -+ +#endif + -+ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,13) + +#define netlink_kernel_create(net, uint, groups, input, cb_mutex, mod) \ -+ netlink_kernel_create(uint, input) ++ netlink_kernel_create(uint, groups, input, cb_mutex, mod) + +#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,21) + @@ -609,12 +742,16 @@ index 0000000..763d07a + +#endif + ++#ifndef SCSI_MAX_VARLEN_CDB_SIZE ++#define SCSI_MAX_VARLEN_CDB_SIZE 16 ++#endif ++ +#endif diff --git a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c -index c9e95e7..79436c4 100644 +index f64ffa7..d0cdeb7 100644 --- a/scsi_transport_iscsi.c +++ b/scsi_transport_iscsi.c -@@ -41,13 +41,13 @@ struct iscsi_internal { +@@ -72,13 +72,13 @@ struct iscsi_internal { struct scsi_transport_template t; struct iscsi_transport *iscsi_transport; struct list_head list; @@ -632,7 +769,7 @@ index c9e95e7..79436c4 100644 }; static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ -@@ -64,12 +64,12 @@ static DEFINE_SPINLOCK(iscsi_transport_lock); +@@ -95,12 +95,12 @@ static DEFINE_SPINLOCK(iscsi_transport_lock); #define to_iscsi_internal(tmpl) \ container_of(tmpl, struct iscsi_internal, t) @@ -649,7 +786,7 @@ index c9e95e7..79436c4 100644 kfree(priv); } -@@ -79,33 +79,31 @@ static void iscsi_transport_release(struct device *dev) +@@ -110,33 +110,31 @@ static void iscsi_transport_release(struct device *dev) */ static struct class iscsi_transport_class = { .name = "iscsi_transport", @@ -692,27 +829,155 @@ index c9e95e7..79436c4 100644 NULL, }; -@@ -113,6 +111,7 @@ static struct attribute_group iscsi_transport_group = { - .attrs = iscsi_transport_attrs, +@@ -154,27 +152,28 @@ static struct attribute_group iscsi_transport_group = { + struct device_attribute dev_attr_##_prefix##_##_name = \ + __ATTR(_name,_mode,_show,_store) + +-static void iscsi_endpoint_release(struct device *dev) ++static void iscsi_endpoint_release(struct class_device *cdev) + { +- struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); ++ struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(cdev); + kfree(ep); + } + + static struct class iscsi_endpoint_class = { + .name = "iscsi_endpoint", +- .dev_release = iscsi_endpoint_release, ++ .release = iscsi_endpoint_release, }; -+#if 0 - /* - * iSCSI endpoint attrs - */ -@@ -236,9 +235,10 @@ struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) + static ssize_t +-show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf) ++show_ep_handle(struct class_device *cdev, char *buf) + { +- struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); ++ struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(cdev); + return sprintf(buf, "%llu\n", (unsigned long long) ep->id); + } +-static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL); ++static struct class_device_attribute class_device_attr_ep_handle = ++ __ATTR(handle, S_IRUGO, show_ep_handle, NULL); + + static struct attribute *iscsi_endpoint_attrs[] = { +- &dev_attr_ep_handle.attr, ++ &class_device_attr_ep_handle.attr, + NULL, + }; + +@@ -184,26 +183,15 @@ static struct attribute_group iscsi_endpoint_group = { + + #define ISCSI_MAX_EPID -1 + +-static int iscsi_match_epid(struct device *dev, void *data) +-{ +- struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev); +- uint64_t *epid = (uint64_t *) data; +- +- return *epid == ep->id; +-} +- + struct iscsi_endpoint * + iscsi_create_endpoint(int dd_size) + { +- struct device *dev; + struct iscsi_endpoint *ep; + uint64_t id; + int err; + + for (id = 1; id < ISCSI_MAX_EPID; id++) { +- dev = class_find_device(&iscsi_endpoint_class, NULL, &id, +- iscsi_match_epid); +- if (!dev) ++ if (!iscsi_lookup_endpoint(id)) + break; + } + if (id == ISCSI_MAX_EPID) { +@@ -218,8 +206,9 @@ iscsi_create_endpoint(int dd_size) + + ep->id = id; + ep->dev.class = &iscsi_endpoint_class; +- dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id); +- err = device_register(&ep->dev); ++ snprintf(ep->dev.class_id, BUS_ID_SIZE, "ep-%llu", ++ (unsigned long long) id); ++ err = class_device_register(&ep->dev); + if (err) + goto free_ep; + +@@ -232,7 +221,7 @@ iscsi_create_endpoint(int dd_size) return ep; + + unregister_dev: +- device_unregister(&ep->dev); ++ class_device_unregister(&ep->dev); + return NULL; + + free_ep: +@@ -244,32 +233,38 @@ EXPORT_SYMBOL_GPL(iscsi_create_endpoint); + void iscsi_destroy_endpoint(struct iscsi_endpoint *ep) + { + sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group); +- device_unregister(&ep->dev); ++ class_device_unregister(&ep->dev); } - EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint); + EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint); + + struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) + { +- struct iscsi_endpoint *ep; +- struct device *dev; +- +- dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, +- iscsi_match_epid); +- if (!dev) +- return NULL; ++ struct iscsi_endpoint *ep = NULL; ++ struct class_device *cdev; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) ++ down_read(&iscsi_endpoint_class.subsys.rwsem); ++#else ++ spin_lock(&iscsi_endpoint_class.subsys.list_lock); ++#endif ++ list_for_each_entry(cdev, &iscsi_endpoint_class.children, node) { ++ ep = iscsi_dev_to_endpoint(cdev); ++ if (ep->id == handle) ++ break; ++ ep = NULL; ++ } ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) ++ up_read(&iscsi_endpoint_class.subsys.rwsem); ++#else ++ spin_unlock(&iscsi_endpoint_class.subsys.list_lock); +#endif +- ep = iscsi_dev_to_endpoint(dev); +- /* +- * we can drop this now because the interface will prevent +- * removals and lookups from racing. +- */ +- put_device(dev); + return ep; + } + EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint); + static int iscsi_setup_host(struct transport_container *tc, struct device *dev, - struct device *cdev) + struct class_device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); struct iscsi_cls_host *ihost = shost->shost_data; -@@ -545,15 +545,6 @@ static void __iscsi_unblock_session(struct work_struct *work) +@@ -570,8 +565,6 @@ static void __iscsi_unblock_session(struct work_struct *work) + struct iscsi_cls_session *session = + container_of(work, struct iscsi_cls_session, + unblock_work); +- struct Scsi_Host *shost = iscsi_session_to_shost(session); +- struct iscsi_cls_host *ihost = shost->shost_data; + unsigned long flags; + + ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n"); +@@ -585,15 +578,6 @@ static void __iscsi_unblock_session(struct work_struct *work) spin_unlock_irqrestore(&session->lock, flags); /* start IO */ scsi_target_unblock(&session->dev); @@ -725,113 +990,82 @@ index c9e95e7..79436c4 100644 - if (scsi_queue_work(shost, &session->scan_work)) - atomic_inc(&ihost->nr_scans); - } + ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n"); } - /** -@@ -698,7 +689,8 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) +@@ -749,7 +733,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) } session->target_id = id; - dev_set_name(&session->dev, "session%u", session->sid); -+ snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", -+ session->sid); ++ snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", session->sid); err = device_add(&session->dev); if (err) { iscsi_cls_session_printk(KERN_ERR, session, -@@ -870,7 +862,8 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) +@@ -929,7 +913,8 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) if (!get_device(&session->dev)) goto free_conn; - dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid); + snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u", -+ session->sid, cid); ++ session->sid, cid); conn->dev.parent = &session->dev; conn->dev.release = iscsi_conn_release; err = device_register(&conn->dev); -@@ -1309,6 +1302,8 @@ static int - iscsi_if_transport_ep(struct iscsi_transport *transport, - struct iscsi_uevent *ev, int msg_type) +@@ -1002,7 +987,15 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt) + static int + iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp) { -+ return -ENOSYS; -+#if 0 - struct iscsi_endpoint *ep; - int rc = 0; - -@@ -1340,6 +1335,8 @@ iscsi_if_transport_ep(struct iscsi_transport *transport, - break; - } - return rc; +- return nlmsg_multicast(nls, skb, 0, group, gfp); ++ int err; + -+#endif - } - - static int -@@ -1421,6 +1418,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) - ev->u.c_session.queue_depth); - break; - case ISCSI_UEVENT_CREATE_BOUND_SESSION: -+ err = -ENOSYS; -+ break; -+#if 0 - ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle); - if (!ep) { - err = -EINVAL; -@@ -1432,6 +1432,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) - ev->u.c_bound_session.cmds_max, - ev->u.c_bound_session.queue_depth); - break; -+#endif - case ISCSI_UEVENT_DESTROY_SESSION: - session = iscsi_session_lookup(ev->u.d_session.sid); - if (session) -@@ -1516,55 +1517,70 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ++ NETLINK_CB(skb).dst_group = group; ++ ++ err = netlink_broadcast(nls, skb, 0, group, gfp); ++ if (err > 0) ++ err = 0; ++ ++ return err; } - /* -- * Get message from skb. Each message is processed by iscsi_if_recv_msg. -- * Malformed skbs with wrong lengths or invalid creds are not processed. -+ * 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. + int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, +@@ -1642,51 +1635,65 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) + * Malformed skbs with wrong lengths or invalid creds are not processed. */ static void -iscsi_if_rx(struct sk_buff *skb) +iscsi_if_rx(struct sock *sk, int len) { -+ struct sk_buff *skb; -+ - mutex_lock(&rx_queue_mutex); +- mutex_lock(&rx_queue_mutex); - while (skb->len >= NLMSG_SPACE(0)) { - int err; - uint32_t rlen; - struct nlmsghdr *nlh; - struct iscsi_uevent *ev; +- uint32_t group; - - nlh = nlmsg_hdr(skb); - if (nlh->nlmsg_len < sizeof(*nlh) || - skb->len < nlh->nlmsg_len) { - break; -+ while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { -+ if (NETLINK_CREDS(skb)->uid) { -+ skb_pull(skb, skb->len); -+ goto free_skb; - } - +- } +- - ev = NLMSG_DATA(nlh); - rlen = NLMSG_ALIGN(nlh->nlmsg_len); - if (rlen > skb->len) - rlen = skb->len; -+ while (skb->len >= NLMSG_SPACE(0)) { -+ int err; -+ uint32_t rlen; -+ struct nlmsghdr *nlh; -+ struct iscsi_uevent *ev; ++ struct sk_buff *skb; -- err = iscsi_if_recv_msg(skb, nlh); +- err = iscsi_if_recv_msg(skb, nlh, &group); - if (err) { - ev->type = ISCSI_KEVENT_IF_ERROR; - ev->iferror = err; -- } ++ 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; + } - do { - /* - * special case for GET_STATS: @@ -840,12 +1074,19 @@ index c9e95e7..79436c4 100644 - * on error - fall through. - */ - if (ev->type == ISCSI_UEVENT_GET_STATS && !err) ++ ++ while (skb->len >= NLMSG_SPACE(0)) { ++ int err; ++ uint32_t rlen; ++ struct nlmsghdr *nlh; ++ struct iscsi_uevent *ev; ++ uint32_t group; ++ + nlh = nlmsg_hdr(skb); + if (nlh->nlmsg_len < sizeof(*nlh) || -+ skb->len < nlh->nlmsg_len) { ++ skb->len < nlh->nlmsg_len) { break; -- err = iscsi_if_send_reply( -- NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, +- err = iscsi_if_send_reply(group, nlh->nlmsg_seq, - nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); - } while (err < 0 && err != -ECONNREFUSED); - skb_pull(skb, rlen); @@ -856,7 +1097,7 @@ index c9e95e7..79436c4 100644 + if (rlen > skb->len) + rlen = skb->len; + -+ err = iscsi_if_recv_msg(skb, nlh); ++ err = iscsi_if_recv_msg(skb, nlh, &group); + if (err) { + ev->type = ISCSI_KEVENT_IF_ERROR; + ev->iferror = err; @@ -870,9 +1111,8 @@ index c9e95e7..79436c4 100644 + */ + 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)); ++ err = iscsi_if_send_reply(group, nlh->nlmsg_seq, ++ nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); + } while (err < 0 && err != -ECONNREFUSED); + skb_pull(skb, rlen); + } @@ -891,7 +1131,7 @@ index c9e95e7..79436c4 100644 __ATTR(_name,_mode,_show,_store) /* -@@ -1572,10 +1588,9 @@ struct device_attribute dev_attr_##_prefix##_##_name = \ +@@ -1694,10 +1701,9 @@ struct device_attribute dev_attr_##_prefix##_##_name = \ */ #define iscsi_conn_attr_show(param) \ static ssize_t \ @@ -904,7 +1144,7 @@ index c9e95e7..79436c4 100644 struct iscsi_transport *t = conn->transport; \ return t->get_conn_param(conn, param, buf); \ } -@@ -1599,16 +1614,17 @@ iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS); +@@ -1721,16 +1727,18 @@ iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS); iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO); iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO); @@ -920,13 +1160,13 @@ index c9e95e7..79436c4 100644 - struct device_attribute *attr, char *buf) \ +show_session_param_##param(struct class_device *cdev, char *buf) \ { \ -- struct iscsi_cls_session *session = \ + struct iscsi_cls_session *session = \ - iscsi_dev_to_session(dev->parent); \ -+ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \ ++ iscsi_cdev_to_session(cdev); \ struct iscsi_transport *t = session->transport; \ \ if (perm && !capable(CAP_SYS_ADMIN)) \ -@@ -1642,10 +1658,9 @@ iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0); +@@ -1765,10 +1773,9 @@ iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0); iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0) static ssize_t @@ -939,7 +1179,7 @@ index c9e95e7..79436c4 100644 return sprintf(buf, "%s\n", iscsi_session_state_name(session->state)); } static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, -@@ -1653,11 +1668,9 @@ static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, +@@ -1776,11 +1783,10 @@ static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state, #define iscsi_priv_session_attr_show(field, format) \ static ssize_t \ @@ -947,13 +1187,13 @@ index c9e95e7..79436c4 100644 - struct device_attribute *attr, char *buf) \ +show_priv_session_##field(struct class_device *cdev, char *buf) \ { \ -- struct iscsi_cls_session *session = \ + struct iscsi_cls_session *session = \ - iscsi_dev_to_session(dev->parent); \ -+ struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev);\ ++ iscsi_cdev_to_session(cdev); \ return sprintf(buf, format"\n", session->field); \ } -@@ -1672,10 +1685,9 @@ iscsi_priv_session_attr(recovery_tmo, "%d"); +@@ -1795,10 +1801,9 @@ iscsi_priv_session_attr(recovery_tmo, "%d"); */ #define iscsi_host_attr_show(param) \ static ssize_t \ @@ -966,7 +1206,7 @@ index c9e95e7..79436c4 100644 struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \ return priv->iscsi_transport->get_host_param(shost, param, buf); \ } -@@ -1692,7 +1704,7 @@ iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME); +@@ -1815,7 +1820,7 @@ iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME); #define SETUP_PRIV_SESSION_RD_ATTR(field) \ do { \ @@ -975,7 +1215,7 @@ index c9e95e7..79436c4 100644 count++; \ } while (0) -@@ -1700,7 +1712,7 @@ do { \ +@@ -1823,7 +1828,7 @@ do { \ #define SETUP_SESSION_RD_ATTR(field, param_flag) \ do { \ if (tt->param_mask & param_flag) { \ @@ -984,7 +1224,7 @@ index c9e95e7..79436c4 100644 count++; \ } \ } while (0) -@@ -1708,7 +1720,7 @@ do { \ +@@ -1831,7 +1836,7 @@ do { \ #define SETUP_CONN_RD_ATTR(field, param_flag) \ do { \ if (tt->param_mask & param_flag) { \ @@ -993,7 +1233,7 @@ index c9e95e7..79436c4 100644 count++; \ } \ } while (0) -@@ -1716,7 +1728,7 @@ do { \ +@@ -1839,7 +1844,7 @@ do { \ #define SETUP_HOST_RD_ATTR(field, param_flag) \ do { \ if (tt->host_param_mask & param_flag) { \ @@ -1002,7 +1242,7 @@ index c9e95e7..79436c4 100644 count++; \ } \ } while (0) -@@ -1808,15 +1820,15 @@ iscsi_register_transport(struct iscsi_transport *tt) +@@ -1930,15 +1935,15 @@ iscsi_register_transport(struct iscsi_transport *tt) priv->t.user_scan = iscsi_user_scan; priv->t.create_work_queue = 1; @@ -1023,19 +1263,18 @@ index c9e95e7..79436c4 100644 /* host parameters */ priv->t.host_attrs.ac.attrs = &priv->host_attrs[0]; -@@ -1895,9 +1907,8 @@ iscsi_register_transport(struct iscsi_transport *tt) +@@ -2018,8 +2023,8 @@ iscsi_register_transport(struct iscsi_transport *tt) printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name); return &priv->t; -unregister_dev: - device_unregister(&priv->dev); -- return NULL; +unregister_cdev: + class_device_unregister(&priv->cdev); + return NULL; free_priv: kfree(priv); - return NULL; -@@ -1924,8 +1935,8 @@ int iscsi_unregister_transport(struct iscsi_transport *tt) +@@ -2047,8 +2052,8 @@ int iscsi_unregister_transport(struct iscsi_transport *tt) transport_container_unregister(&priv->session_cont); transport_container_unregister(&priv->t.host_attrs); @@ -1046,57 +1285,27 @@ index c9e95e7..79436c4 100644 mutex_unlock(&rx_queue_mutex); return 0; -@@ -1945,13 +1956,14 @@ static __init int iscsi_transport_init(void) - if (err) - return err; - -+#if 0 - err = class_register(&iscsi_endpoint_class); - if (err) - goto unregister_transport_class; -- -+#endif - err = transport_class_register(&iscsi_host_class); - if (err) -- goto unregister_endpoint_class; -+ goto unregister_transport_class; - - err = transport_class_register(&iscsi_connection_class); - if (err) -@@ -1982,8 +1994,10 @@ unregister_conn_class: - transport_class_unregister(&iscsi_connection_class); - unregister_host_class: - transport_class_unregister(&iscsi_host_class); -+#if 0 - unregister_endpoint_class: - class_unregister(&iscsi_endpoint_class); -+#endif - unregister_transport_class: - class_unregister(&iscsi_transport_class); - return err; -@@ -1996,7 +2010,9 @@ static void __exit iscsi_transport_exit(void) - transport_class_unregister(&iscsi_connection_class); - transport_class_unregister(&iscsi_session_class); - transport_class_unregister(&iscsi_host_class); -+#if 0 - class_unregister(&iscsi_endpoint_class); -+#endif - class_unregister(&iscsi_transport_class); - } - diff --git a/scsi_transport_iscsi.h b/scsi_transport_iscsi.h -index 6beea23..d509d17 100644 +index ef4b697..a633ee0 100644 --- a/scsi_transport_iscsi.h +++ b/scsi_transport_iscsi.h -@@ -28,6 +28,8 @@ +@@ -27,6 +27,7 @@ + #include #include #include "iscsi_if.h" - +#include "open_iscsi_compat.h" -+ + struct scsi_transport_template; struct iscsi_transport; - struct iscsi_endpoint; +@@ -219,7 +220,7 @@ extern void iscsi_host_for_each_session(struct Scsi_Host *shost, + + struct iscsi_endpoint { + void *dd_data; /* LLD private data */ +- struct device dev; ++ struct class_device dev; + uint64_t id; + }; + -- -1.5.2.1 +1.6.0.6 diff --git a/kernel/2.6.27_compat.patch b/kernel/2.6.27_compat.patch index 5111232..46794a0 100644 --- a/kernel/2.6.27_compat.patch +++ b/kernel/2.6.27_compat.patch @@ -16,7 +16,7 @@ new file mode 100644 index 0000000..b977df5 --- /dev/null +++ b/open_iscsi_compat.h -@@ -0,0 +1,28 @@ +@@ -0,0 +1,30 @@ +#ifndef OPEN_ISCSI_COMPAT +#define OPEN_ISCSI_COMPAT + @@ -36,6 +36,7 @@ index 0000000..b977df5 +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,27) ++#if !(defined CONFIG_SUSE_KERNEL) + +#define BLK_EH_NOT_HANDLED EH_NOT_HANDLED +#define BLK_EH_RESET_TIMER EH_RESET_TIMER @@ -43,6 +44,7 @@ index 0000000..b977df5 +#define blk_eh_timer_return scsi_eh_timer_return + +#endif ++#endif + +#endif diff --git a/scsi_transport_iscsi.c b/scsi_transport_iscsi.c diff --git a/kernel/Makefile b/kernel/Makefile index a97c52f..a264aed 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -102,6 +102,9 @@ linux_2_6_29: $(unpatch_code) linux_2_6_30: $(unpatch_code) +linux_2_6_31: $(unpatch_code) + +linux_2_6_32: $(unpatch_code) do_unpatch_code: echo "Un-patching source code for use with linux-2.6.14 and up ..." diff --git a/kernel/iscsi_tcp.c b/kernel/iscsi_tcp.c index bce1594..aa4abdb 100644 --- a/kernel/iscsi_tcp.c +++ b/kernel/iscsi_tcp.c @@ -100,6 +100,27 @@ static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, return total_consumed; } +/** + * iscsi_sw_sk_state_check - check socket state + * @sk: socket + * + * If the socket is in CLOSE or CLOSE_WAIT we should + * not close the connection if there is still some + * data pending. + */ +static inline int iscsi_sw_sk_state_check(struct sock *sk) +{ + struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data; + + if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) && + !atomic_read(&sk->sk_rmem_alloc)) { + ISCSI_SW_TCP_DBG(conn, "TCP_CLOSE|TCP_CLOSE_WAIT\n"); + iscsi_conn_failure(conn, ISCSI_ERR_TCP_CONN_CLOSE); + return -ECONNRESET; + } + return 0; +} + static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag) { struct iscsi_conn *conn = sk->sk_user_data; @@ -118,6 +139,8 @@ static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag) rd_desc.count = 1; tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv); + iscsi_sw_sk_state_check(sk); + read_unlock(&sk->sk_callback_lock); /* If we had to (atomically) map a highmem page, @@ -138,13 +161,7 @@ static void iscsi_sw_tcp_state_change(struct sock *sk) 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)) { - ISCSI_SW_TCP_DBG(conn, "iscsi_tcp_state_change: " - "TCP_CLOSE|TCP_CLOSE_WAIT\n"); - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - } + iscsi_sw_sk_state_check(sk); tcp_conn = conn->dd_data; tcp_sw_conn = tcp_conn->dd_data; @@ -803,7 +820,7 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, goto free_host; cls_session = iscsi_session_setup(&iscsi_sw_tcp_transport, shost, - cmds_max, + cmds_max, 0, sizeof(struct iscsi_tcp_task) + sizeof(struct iscsi_sw_tcp_hdrbuf), initial_cmdsn, 0); @@ -895,7 +912,7 @@ static struct iscsi_transport iscsi_sw_tcp_transport = { ISCSI_USERNAME | ISCSI_PASSWORD | ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN | ISCSI_FAST_ABORT | ISCSI_ABORT_TMO | - ISCSI_LU_RESET_TMO | + ISCSI_LU_RESET_TMO | ISCSI_TGT_RESET_TMO | ISCSI_PING_TMO | ISCSI_RECV_TMO | ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME, .host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS | diff --git a/kernel/libiscsi.c b/kernel/libiscsi.c index 73c4231..0b810b6 100644 --- a/kernel/libiscsi.c +++ b/kernel/libiscsi.c @@ -109,12 +109,9 @@ inline void iscsi_conn_queue_work(struct iscsi_conn *conn) } EXPORT_SYMBOL_GPL(iscsi_conn_queue_work); -void -iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) +static void __iscsi_update_cmdsn(struct iscsi_session *session, + uint32_t exp_cmdsn, uint32_t max_cmdsn) { - uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn); - uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn); - /* * standard specifies this check for when to update expected and * max sequence numbers @@ -138,6 +135,12 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) iscsi_conn_queue_work(session->leadconn); } } + +void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) +{ + __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn), + be32_to_cpu(hdr->max_cmdsn)); +} EXPORT_SYMBOL_GPL(iscsi_update_cmdsn); /** @@ -263,6 +266,88 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task) } /** + * iscsi_check_tmf_restrictions - check if a task is affected by TMF + * @task: iscsi task + * @opcode: opcode to check for + * + * During TMF a task has to be checked if it's affected. + * All unrelated I/O can be passed through, but I/O to the + * affected LUN should be restricted. + * If 'fast_abort' is set we won't be sending any I/O to the + * affected LUN. + * Otherwise the target is waiting for all TTTs to be completed, + * so we have to send all outstanding Data-Out PDUs to the target. + */ +static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) +{ + struct iscsi_conn *conn = task->conn; + struct iscsi_tm *tmf = &conn->tmhdr; + unsigned int hdr_lun; + + if (conn->tmf_state == TMF_INITIAL) + return 0; + + if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) + return 0; + + switch (ISCSI_TM_FUNC_VALUE(tmf)) { + case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: + /* + * Allow PDUs for unrelated LUNs + */ + hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun); + if (hdr_lun != task->sc->device->lun) + return 0; + /* fall through */ + case ISCSI_TM_FUNC_TARGET_WARM_RESET: + /* + * Fail all SCSI cmd PDUs + */ + if (opcode != ISCSI_OP_SCSI_DATA_OUT) { + iscsi_conn_printk(KERN_INFO, conn, + "task [op %x/%x itt " + "0x%x/0x%x] " + "rejected.\n", + task->hdr->opcode, opcode, + task->itt, task->hdr_itt); + return -EACCES; + } + /* + * And also all data-out PDUs in response to R2T + * if fast_abort is set. + */ + if (conn->session->fast_abort) { + iscsi_conn_printk(KERN_INFO, conn, + "task [op %x/%x itt " + "0x%x/0x%x] fast abort.\n", + task->hdr->opcode, opcode, + task->itt, task->hdr_itt); + return -EACCES; + } + break; + case ISCSI_TM_FUNC_ABORT_TASK: + /* + * the caller has already checked if the task + * they want to abort was in the pending queue so if + * we are here the cmd pdu has gone out already, and + * we will only hit this for data-outs + */ + if (opcode == ISCSI_OP_SCSI_DATA_OUT && + task->hdr_itt == tmf->rtt) { + ISCSI_DBG_SESSION(conn->session, + "Preventing task %x/%x from sending " + "data-out due to abort task in " + "progress\n", task->itt, + task->hdr_itt); + return -EACCES; + } + break; + } + + return 0; +} + +/** * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu * @task: iscsi task * @@ -279,6 +364,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) itt_t itt; int rc; + rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD); + if (rc) + return rc; + if (conn->session->tt->alloc_pdu) { rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD); if (rc) @@ -301,8 +390,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) hdr->flags = ISCSI_ATTR_SIMPLE; int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun); memcpy(task->lun, hdr->lun, sizeof(task->lun)); - hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); - session->cmdsn++; hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); cmd_len = sc->cmd_len; if (cmd_len < ISCSI_CDB_SIZE) @@ -388,6 +475,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) return -EIO; task->state = ISCSI_TASK_RUNNING; + hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); + session->cmdsn++; conn->scsicmd_pdus_cnt++; ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " @@ -499,6 +588,31 @@ static void iscsi_complete_task(struct iscsi_task *task, int state) __iscsi_put_task(task); } +/** + * iscsi_complete_scsi_task - finish scsi task normally + * @task: iscsi task for scsi cmd + * @exp_cmdsn: expected cmd sn in cpu format + * @max_cmdsn: max cmd sn in cpu format + * + * This is used when drivers do not need or cannot perform + * lower level pdu processing. + * + * Called with session lock + */ +void iscsi_complete_scsi_task(struct iscsi_task *task, + uint32_t exp_cmdsn, uint32_t max_cmdsn) +{ + struct iscsi_conn *conn = task->conn; + + ISCSI_DBG_SESSION(conn->session, "[itt 0x%x]\n", task->itt); + + conn->last_recv = jiffies; + __iscsi_update_cmdsn(conn->session, exp_cmdsn, max_cmdsn); + iscsi_complete_task(task, ISCSI_TASK_COMPLETED); +} +EXPORT_SYMBOL_GPL(iscsi_complete_scsi_task); + + /* * session lock must be held and if not called for a task that is * still pending or from the xmit thread, then xmit thread must @@ -549,12 +663,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, struct iscsi_session *session = conn->session; struct iscsi_hdr *hdr = task->hdr; struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; + uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; if (conn->session->state == ISCSI_STATE_LOGGING_OUT) return -ENOTCONN; - if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) && - hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) + if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT) nop->exp_statsn = cpu_to_be32(conn->exp_statsn); /* * pre-format CmdSN for outgoing PDU. @@ -562,9 +676,12 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, nop->cmdsn = cpu_to_be32(session->cmdsn); if (hdr->itt != RESERVED_ITT) { /* - * TODO: We always use immediate, so we never hit this. + * TODO: We always use immediate for normal session pdus. * If we start to send tmfs or nops as non-immediate then * we should start checking the cmdsn numbers for mgmt tasks. + * + * During discovery sessions iscsid sends TEXT as non immediate, + * but we always only send one PDU at a time. */ if (conn->c_stage == ISCSI_CONN_STARTED && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { @@ -592,22 +709,28 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, { struct iscsi_session *session = conn->session; struct iscsi_host *ihost = shost_priv(session->host); + uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; struct iscsi_task *task; itt_t itt; if (session->state == ISCSI_STATE_TERMINATE) return NULL; - if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || - hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) + if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) { /* * Login and Text are sent serially, in * request-followed-by-response sequence. * Same task can be used. Same ITT must be used. * Note that login_task is preallocated at conn_create(). */ + if (conn->login_task->state != ISCSI_TASK_FREE) { + iscsi_conn_printk(KERN_ERR, conn, "Login/Text in " + "progress. Cannot start new task.\n"); + return NULL; + } + task = conn->login_task; - else { + } else { if (session->state != ISCSI_STATE_LOGGED_IN) return NULL; @@ -857,27 +980,102 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) } } +static int iscsi_nop_out_rsp(struct iscsi_task *task, + struct iscsi_nopin *nop, char *data, int datalen) +{ + struct iscsi_conn *conn = task->conn; + int rc = 0; + + if (conn->ping_task != task) { + /* + * If this is not in response to one of our + * nops then it must be from userspace. + */ + if (iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *)nop, + data, datalen)) + rc = ISCSI_ERR_CONN_FAILED; + } else + mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); + iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + return rc; +} + 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; + int opcode, rc = 0; 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) > datalen || + ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) { + iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected " + "pdu. Invalid data length (pdu dlength " + "%u, datalen %d\n", ntoh24(reject->dlength), + datalen); + return ISCSI_ERR_PROTO; + } + memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); + opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK; + + switch (reject->reason) { + case ISCSI_REASON_DATA_DIGEST_ERROR: + iscsi_conn_printk(KERN_ERR, conn, + "pdu (op 0x%x itt 0x%x) rejected " + "due to DataDigest error.\n", + rejected_pdu.itt, opcode); + break; + case ISCSI_REASON_IMM_CMD_REJECT: + iscsi_conn_printk(KERN_ERR, conn, + "pdu (op 0x%x itt 0x%x) rejected. Too many " + "immediate commands.\n", + rejected_pdu.itt, opcode); + /* + * We only send one TMF at a time so if the target could not + * handle it, then it should get fixed (RFC mandates that + * a target can handle one immediate TMF per conn). + * + * For nops-outs, we could have sent more than one if + * the target is sending us lots of nop-ins + */ + if (opcode != ISCSI_OP_NOOP_OUT) + return 0; - if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) { - memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); - iscsi_conn_printk(KERN_ERR, conn, - "pdu (op 0x%x) rejected " - "due to DataDigest error.\n", - rejected_pdu.opcode); + if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG)) + /* + * nop-out in response to target's nop-out rejected. + * Just resend. + */ + iscsi_send_nopout(conn, + (struct iscsi_nopin*)&rejected_pdu); + else { + struct iscsi_task *task; + /* + * Our nop as ping got dropped. We know the target + * and transport are ok so just clean up + */ + task = iscsi_itt_to_task(conn, rejected_pdu.itt); + if (!task) { + iscsi_conn_printk(KERN_ERR, conn, + "Invalid pdu reject. Could " + "not lookup rejected task.\n"); + rc = ISCSI_ERR_BAD_ITT; + } else + rc = iscsi_nop_out_rsp(task, + (struct iscsi_nopin*)&rejected_pdu, + NULL, 0); } + break; + default: + iscsi_conn_printk(KERN_ERR, conn, + "pdu (op 0x%x itt 0x%x) rejected. Reason " + "code 0x%x\n", rejected_pdu.itt, + rejected_pdu.opcode, reject->reason); + break; } - return 0; + return rc; } /** @@ -1038,15 +1236,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; - if (conn->ping_task != task) - /* - * If this is not in response to one of our - * nops then it must be from userspace. - */ - goto recv_pdu; - - mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); - iscsi_complete_task(task, ISCSI_TASK_COMPLETED); + rc = iscsi_nop_out_rsp(task, (struct iscsi_nopin*)hdr, + data, datalen); break; default: rc = ISCSI_ERR_BAD_OPCODE; @@ -1212,6 +1403,9 @@ static int iscsi_xmit_task(struct iscsi_conn *conn) struct iscsi_task *task = conn->task; int rc; + if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) + return -ENODATA; + __iscsi_get_task(task); spin_unlock_bh(&conn->session->lock); rc = conn->session->tt->xmit_task(task); @@ -1258,10 +1452,11 @@ EXPORT_SYMBOL_GPL(iscsi_requeue_task); **/ static int iscsi_data_xmit(struct iscsi_conn *conn) { + struct iscsi_task *task; int rc = 0; spin_lock_bh(&conn->session->lock); - if (unlikely(conn->suspend_tx)) { + if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n"); spin_unlock_bh(&conn->session->lock); return -ENODATA; @@ -1270,7 +1465,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn) if (conn->task) { rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; } /* @@ -1290,16 +1485,13 @@ check_mgmt: } rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; } /* process pending command queue */ while (!list_empty(&conn->cmdqueue)) { - if (conn->tmf_state == TMF_QUEUED) - break; - - conn->task = list_entry(conn->cmdqueue.next, - struct iscsi_task, running); + conn->task = list_entry(conn->cmdqueue.next, struct iscsi_task, + running); list_del_init(&conn->task->running); if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { fail_scsi_task(conn->task, DID_IMM_RETRY); @@ -1307,18 +1499,18 @@ check_mgmt: } rc = iscsi_prep_scsi_cmd_pdu(conn->task); if (rc) { - if (rc == -ENOMEM) { + if (rc == -ENOMEM || rc == -EACCES) { list_add_tail(&conn->task->running, &conn->cmdqueue); conn->task = NULL; - goto again; + goto done; } else fail_scsi_task(conn->task, DID_ABORT); continue; } rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; /* * we could continuously get new task requests so * we need to check the mgmt queue for nops that need to @@ -1329,31 +1521,30 @@ check_mgmt: } while (!list_empty(&conn->requeue)) { - if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL) - break; - /* * we always do fastlogout - conn stop code will clean up. */ if (conn->session->state == ISCSI_STATE_LOGGING_OUT) break; - conn->task = list_entry(conn->requeue.next, - struct iscsi_task, running); + task = list_entry(conn->requeue.next, struct iscsi_task, + running); + if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT)) + break; + + conn->task = task; list_del_init(&conn->task->running); conn->task->state = ISCSI_TASK_RUNNING; rc = iscsi_xmit_task(conn); if (rc) - goto again; + goto done; if (!list_empty(&conn->mgmtqueue)) goto check_mgmt; } spin_unlock_bh(&conn->session->lock); return -ENODATA; -again: - if (unlikely(conn->suspend_tx)) - rc = -ENODATA; +done: spin_unlock_bh(&conn->session->lock); return rc; } @@ -1474,6 +1665,12 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) goto fault; } + if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { + reason = FAILURE_SESSION_IN_RECOVERY; + sc->result = DID_REQUEUE; + goto fault; + } + if (iscsi_check_cmdsn_window_closed(conn)) { reason = FAILURE_WINDOW_CLOSED; goto reject; @@ -1488,7 +1685,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) if (!ihost->workq) { reason = iscsi_prep_scsi_cmd_pdu(task); if (reason) { - if (reason == -ENOMEM) { + if (reason == -ENOMEM || reason == -EACCES) { reason = FAILURE_OOM; goto prepd_reject; } else { @@ -1497,6 +1694,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)) } } if (session->tt->xmit_task(task)) { + session->cmdsn--; reason = FAILURE_SESSION_NOT_READY; goto prepd_reject; } @@ -1518,7 +1716,7 @@ reject: ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); spin_lock(host->host_lock); - return SCSI_MLQUEUE_HOST_BUSY; + return SCSI_MLQUEUE_TARGET_BUSY; prepd_fault: sc->scsi_done = NULL; @@ -1558,72 +1756,6 @@ int iscsi_target_alloc(struct scsi_target *starget) } EXPORT_SYMBOL_GPL(iscsi_target_alloc); -void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) -{ - struct iscsi_session *session = cls_session->dd_data; - - spin_lock_bh(&session->lock); - if (session->state != ISCSI_STATE_LOGGED_IN) { - session->state = ISCSI_STATE_RECOVERY_FAILED; - if (session->leadconn) - wake_up(&session->leadconn->ehwait); - } - spin_unlock_bh(&session->lock); -} -EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); - -int iscsi_eh_target_reset(struct scsi_cmnd *sc) -{ - struct iscsi_cls_session *cls_session; - struct iscsi_session *session; - struct iscsi_conn *conn; - - cls_session = starget_to_session(scsi_target(sc->device)); - session = cls_session->dd_data; - conn = session->leadconn; - - mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_TERMINATE) { -failed: - ISCSI_DBG_EH(session, - "failing target reset: Could not log back into " - "target [age %d]\n", - session->age); - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - return FAILED; - } - - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - /* - * we drop the lock here but the leadconn cannot be destoyed while - * we are in the scsi eh - */ - iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - - ISCSI_DBG_EH(session, "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); - - mutex_lock(&session->eh_mutex); - spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_LOGGED_IN) { - ISCSI_DBG_EH(session, - "target reset succeeded\n"); - } else - goto failed; - spin_unlock_bh(&session->lock); - mutex_unlock(&session->eh_mutex); - return SUCCESS; -} -EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); - static void iscsi_tmf_timedout(unsigned long data) { struct iscsi_conn *conn = (struct iscsi_conn *)data; @@ -1714,6 +1846,33 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun, } } +/** + * iscsi_suspend_queue - suspend iscsi_queuecommand + * @conn: iscsi conn to stop queueing IO on + * + * This grabs the session lock to make sure no one is in + * xmit_task/queuecommand, and then sets suspend to prevent + * new commands from being queued. This only needs to be called + * by offload drivers that need to sync a path like ep disconnect + * with the iscsi_queuecommand/xmit_task. To start IO again libiscsi + * will call iscsi_start_tx and iscsi_unblock_session when in FFP. + */ +void iscsi_suspend_queue(struct iscsi_conn *conn) +{ + spin_lock_bh(&conn->session->lock); + set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); + spin_unlock_bh(&conn->session->lock); +} +EXPORT_SYMBOL_GPL(iscsi_suspend_queue); + +/** + * iscsi_suspend_tx - suspend iscsi_data_xmit + * @conn: iscsi conn tp stop processing IO on. + * + * This function sets the suspend bit to prevent iscsi_data_xmit + * from sending new IO, and if work is queued on the xmit thread + * it will wait for it to be completed. + */ void iscsi_suspend_tx(struct iscsi_conn *conn) { struct Scsi_Host *shost = conn->session->host; @@ -1979,6 +2138,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) spin_lock_bh(&session->lock); fail_scsi_task(task, DID_ABORT); conn->tmf_state = TMF_INITIAL; + memset(hdr, 0, sizeof(*hdr)); spin_unlock_bh(&session->lock); iscsi_start_tx(conn); goto success_unlocked; @@ -1989,6 +2149,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) case TMF_NOT_FOUND: if (!sc->SCp.ptr) { conn->tmf_state = TMF_INITIAL; + memset(hdr, 0, sizeof(*hdr)); /* task completed before tmf abort response */ ISCSI_DBG_EH(session, "sc completed while abort in " "progress\n"); @@ -2083,6 +2244,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) iscsi_suspend_tx(conn); spin_lock_bh(&session->lock); + memset(hdr, 0, sizeof(*hdr)); fail_scsi_tasks(conn, sc->device->lun, DID_ERROR); conn->tmf_state = TMF_INITIAL; spin_unlock_bh(&session->lock); @@ -2100,6 +2262,172 @@ done: } EXPORT_SYMBOL_GPL(iscsi_eh_device_reset); +void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) +{ + struct iscsi_session *session = cls_session->dd_data; + + spin_lock_bh(&session->lock); + if (session->state != ISCSI_STATE_LOGGED_IN) { + session->state = ISCSI_STATE_RECOVERY_FAILED; + if (session->leadconn) + wake_up(&session->leadconn->ehwait); + } + spin_unlock_bh(&session->lock); +} +EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); + +/** + * iscsi_eh_session_reset - drop session and attempt relogin + * @sc: scsi command + * + * This function will wait for a relogin, session termination from + * userspace, or a recovery/replacement timeout. + */ +static int iscsi_eh_session_reset(struct scsi_cmnd *sc) +{ + struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + + cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + conn = session->leadconn; + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->lock); + if (session->state == ISCSI_STATE_TERMINATE) { +failed: + ISCSI_DBG_EH(session, + "failing session reset: Could not log back into " + "%s, %s [age %d]\n", session->targetname, + conn->persistent_address, session->age); + spin_unlock_bh(&session->lock); + mutex_unlock(&session->eh_mutex); + return FAILED; + } + + spin_unlock_bh(&session->lock); + mutex_unlock(&session->eh_mutex); + /* + * we drop the lock here but the leadconn cannot be destoyed while + * we are in the scsi eh + */ + iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + + ISCSI_DBG_EH(session, "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); + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->lock); + if (session->state == ISCSI_STATE_LOGGED_IN) { + ISCSI_DBG_EH(session, + "session reset succeeded for %s,%s\n", + session->targetname, conn->persistent_address); + } else + goto failed; + spin_unlock_bh(&session->lock); + mutex_unlock(&session->eh_mutex); + return SUCCESS; +} + +static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) +{ + memset(hdr, 0, sizeof(*hdr)); + hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; + hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK; + hdr->flags |= ISCSI_FLAG_CMD_FINAL; + hdr->rtt = RESERVED_ITT; +} + +/** + * iscsi_eh_target_reset - reset target + * @sc: scsi command + * + * This will attempt to send a warm target reset. If that fails + * then we will drop the session and attempt ERL0 recovery. + */ +int iscsi_eh_target_reset(struct scsi_cmnd *sc) +{ + struct iscsi_cls_session *cls_session; + struct iscsi_session *session; + struct iscsi_conn *conn; + struct iscsi_tm *hdr; + int rc = FAILED; + + cls_session = starget_to_session(scsi_target(sc->device)); + session = cls_session->dd_data; + + ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc, + session->targetname); + + mutex_lock(&session->eh_mutex); + spin_lock_bh(&session->lock); + /* + * Just check if we are not logged in. We cannot check for + * the phase because the reset could come from a ioctl. + */ + if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) + goto unlock; + conn = session->leadconn; + + /* only have one tmf outstanding at a time */ + if (conn->tmf_state != TMF_INITIAL) + goto unlock; + conn->tmf_state = TMF_QUEUED; + + hdr = &conn->tmhdr; + iscsi_prep_tgt_reset_pdu(sc, hdr); + + if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, + session->tgt_reset_timeout)) { + rc = FAILED; + goto unlock; + } + + switch (conn->tmf_state) { + case TMF_SUCCESS: + break; + case TMF_TIMEDOUT: + spin_unlock_bh(&session->lock); + iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); + goto done; + default: + conn->tmf_state = TMF_INITIAL; + goto unlock; + } + + rc = SUCCESS; + spin_unlock_bh(&session->lock); + + iscsi_suspend_tx(conn); + + spin_lock_bh(&session->lock); + memset(hdr, 0, sizeof(*hdr)); + fail_scsi_tasks(conn, -1, DID_ERROR); + conn->tmf_state = TMF_INITIAL; + spin_unlock_bh(&session->lock); + + iscsi_start_tx(conn); + goto done; + +unlock: + spin_unlock_bh(&session->lock); +done: + ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname, + rc == SUCCESS ? "SUCCESS" : "FAILED"); + mutex_unlock(&session->eh_mutex); + + if (rc == FAILED) + rc = iscsi_eh_session_reset(sc); + return rc; +} +EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); + /* * Pre-allocate a pool of @max items of @item_size. By default, the pool * should be accessed via kfifo_{get,put} on q->queue. @@ -2307,7 +2635,7 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) */ struct iscsi_cls_session * iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, - uint16_t cmds_max, int cmd_task_size, + uint16_t cmds_max, int dd_size, int cmd_task_size, uint32_t initial_cmdsn, unsigned int id) { struct iscsi_host *ihost = shost_priv(shost); @@ -2357,7 +2685,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX; cls_session = iscsi_alloc_session(shost, iscsit, - sizeof(struct iscsi_session)); + sizeof(struct iscsi_session) + + dd_size); if (!cls_session) goto dec_session_count; session = cls_session->dd_data; @@ -2365,6 +2694,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->host = shost; session->state = ISCSI_STATE_FREE; session->fast_abort = 1; + session->tgt_reset_timeout = 30; session->lu_reset_timeout = 15; session->abort_timeout = 10; session->scsi_cmds_max = scsi_cmds; @@ -2374,6 +2704,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, session->max_cmdsn = initial_cmdsn + 1; session->max_r2t = 1; session->tt = iscsit; + session->dd_data = cls_session->dd_data + sizeof(*session); mutex_init(&session->eh_mutex); spin_lock_init(&session->lock); @@ -2725,6 +3056,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session, spin_lock_bh(&session->lock); fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED); fail_mgmt_tasks(session, conn); + memset(&conn->tmhdr, 0, sizeof(conn->tmhdr)); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); } @@ -2801,6 +3133,9 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, case ISCSI_PARAM_LU_RESET_TMO: sscanf(buf, "%d", &session->lu_reset_timeout); break; + case ISCSI_PARAM_TGT_RESET_TMO: + sscanf(buf, "%d", &session->tgt_reset_timeout); + break; case ISCSI_PARAM_PING_TMO: sscanf(buf, "%d", &conn->ping_timeout); break; @@ -2900,6 +3235,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, case ISCSI_PARAM_LU_RESET_TMO: len = sprintf(buf, "%d\n", session->lu_reset_timeout); break; + case ISCSI_PARAM_TGT_RESET_TMO: + len = sprintf(buf, "%d\n", session->tgt_reset_timeout); + break; case ISCSI_PARAM_INITIAL_R2T_EN: len = sprintf(buf, "%d\n", session->initial_r2t_en); break; diff --git a/kernel/libiscsi.h b/kernel/libiscsi.h index 04463c1..76e80e7 100644 --- a/kernel/libiscsi.h +++ b/kernel/libiscsi.h @@ -267,6 +267,7 @@ struct iscsi_session { /* configuration */ int abort_timeout; int lu_reset_timeout; + int tgt_reset_timeout; int initial_r2t_en; unsigned max_r2t; int imm_data_en; @@ -303,6 +304,7 @@ struct iscsi_session { int cmds_max; /* size of cmds array */ struct iscsi_task **cmds; /* Original Cmds arr */ struct iscsi_pool cmdpool; /* PDU's pool */ + void *dd_data; /* LLD private data */ }; enum { @@ -363,7 +365,7 @@ extern int iscsi_target_alloc(struct scsi_target *starget); */ extern struct iscsi_cls_session * iscsi_session_setup(struct iscsi_transport *, struct Scsi_Host *shost, - uint16_t, int, uint32_t, unsigned int); + uint16_t, int, int, uint32_t, unsigned int); extern void iscsi_session_teardown(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, @@ -390,6 +392,7 @@ extern void iscsi_session_failure(struct iscsi_session *session, extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf); extern void iscsi_suspend_tx(struct iscsi_conn *conn); +extern void iscsi_suspend_queue(struct iscsi_conn *conn); extern void iscsi_conn_queue_work(struct iscsi_conn *conn); #define iscsi_conn_printk(prefix, _c, fmt, a...) \ @@ -415,6 +418,8 @@ extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t); extern void iscsi_requeue_task(struct iscsi_task *task); extern void iscsi_put_task(struct iscsi_task *task); extern void __iscsi_get_task(struct iscsi_task *task); +extern void iscsi_complete_scsi_task(struct iscsi_task *task, + uint32_t exp_cmdsn, uint32_t max_cmdsn); /* * generic helpers diff --git a/kernel/scsi_transport_iscsi.c b/kernel/scsi_transport_iscsi.c index 5434f5b..f64ffa7 100644 --- a/kernel/scsi_transport_iscsi.c +++ b/kernel/scsi_transport_iscsi.c @@ -30,14 +30,45 @@ #include "scsi_transport_iscsi.h" #include "iscsi_if.h" -#define ISCSI_SESSION_ATTRS 21 +#define ISCSI_SESSION_ATTRS 22 #define ISCSI_CONN_ATTRS 13 #define ISCSI_HOST_ATTRS 4 #define ISCSI_TRANSPORT_VERSION "2.0-871" +static int dbg_session; +module_param_named(debug_session, dbg_session, int, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug_session, + "Turn on debugging for sessions in scsi_transport_iscsi " + "module. Set to 1 to turn on, and zero to turn off. Default " + "is off."); + +static int dbg_conn; +module_param_named(debug_conn, dbg_conn, int, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug_conn, + "Turn on debugging for connections in scsi_transport_iscsi " + "module. Set to 1 to turn on, and zero to turn off. Default " + "is off."); + +#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...) \ + do { \ + if (dbg_session) \ + iscsi_cls_session_printk(KERN_INFO, _session, \ + "%s: " dbg_fmt, \ + __func__, ##arg); \ + } while (0); + +#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...) \ + do { \ + if (dbg_conn) \ + iscsi_cls_conn_printk(KERN_INFO, _conn, \ + "%s: " dbg_fmt, \ + __func__, ##arg); \ + } while (0); + struct iscsi_internal { - int daemon_pid; struct scsi_transport_template t; struct iscsi_transport *iscsi_transport; struct list_head list; @@ -378,6 +409,7 @@ static void iscsi_session_release(struct device *dev) shost = iscsi_session_to_shost(session); scsi_host_put(shost); + ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n"); kfree(session); } @@ -442,6 +474,9 @@ static int iscsi_user_scan_session(struct device *dev, void *data) return 0; session = iscsi_dev_to_session(dev); + + ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n"); + shost = iscsi_session_to_shost(session); ihost = shost->shost_data; @@ -449,8 +484,7 @@ static int iscsi_user_scan_session(struct device *dev, void *data) spin_lock_irqsave(&session->lock, flags); if (session->state != ISCSI_SESSION_LOGGED_IN) { spin_unlock_irqrestore(&session->lock, flags); - mutex_unlock(&ihost->mutex); - return 0; + goto user_scan_exit; } id = session->target_id; spin_unlock_irqrestore(&session->lock, flags); @@ -463,7 +497,10 @@ static int iscsi_user_scan_session(struct device *dev, void *data) scsi_scan_target(&session->dev, 0, id, scan_data->lun, 1); } + +user_scan_exit: mutex_unlock(&ihost->mutex); + ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n"); return 0; } @@ -523,7 +560,9 @@ static void session_recovery_timedout(struct work_struct *work) if (session->transport->session_recovery_timedout) session->transport->session_recovery_timedout(session); + ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n"); scsi_target_unblock(&session->dev); + ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n"); } static void __iscsi_unblock_session(struct work_struct *work) @@ -535,6 +574,7 @@ static void __iscsi_unblock_session(struct work_struct *work) struct iscsi_cls_host *ihost = shost->shost_data; unsigned long flags; + ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n"); /* * The recovery and unblock work get run from the same workqueue, * so try to cancel it if it was going to run after this unblock. @@ -554,6 +594,7 @@ static void __iscsi_unblock_session(struct work_struct *work) if (scsi_queue_work(shost, &session->scan_work)) atomic_inc(&ihost->nr_scans); } + ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n"); } /** @@ -580,12 +621,16 @@ static void __iscsi_block_session(struct work_struct *work) block_work); unsigned long flags; + ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n"); spin_lock_irqsave(&session->lock, flags); session->state = ISCSI_SESSION_FAILED; spin_unlock_irqrestore(&session->lock, flags); scsi_target_block(&session->dev); - queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work, - session->recovery_tmo * HZ); + ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n"); + if (session->recovery_tmo >= 0) + queue_delayed_work(iscsi_eh_timer_workq, + &session->recovery_work, + session->recovery_tmo * HZ); } void iscsi_block_session(struct iscsi_cls_session *session) @@ -603,6 +648,8 @@ static void __iscsi_unbind_session(struct work_struct *work) struct iscsi_cls_host *ihost = shost->shost_data; unsigned long flags; + ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n"); + /* Prevent new scans and make sure scanning is not in progress */ mutex_lock(&ihost->mutex); spin_lock_irqsave(&session->lock, flags); @@ -617,6 +664,7 @@ static void __iscsi_unbind_session(struct work_struct *work) scsi_remove_target(&session->dev); iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); + ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n"); } struct iscsi_cls_session * @@ -648,6 +696,8 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, device_initialize(&session->dev); if (dd_size) session->dd_data = &session[1]; + + ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n"); return session; } EXPORT_SYMBOL_GPL(iscsi_alloc_session); @@ -693,6 +743,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) "Too many iscsi targets. Max " "number of targets is %d.\n", ISCSI_MAX_TARGET - 1); + err = -EOVERFLOW; goto release_host; } } @@ -712,6 +763,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) spin_unlock_irqrestore(&sesslock, flags); iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION); + ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n"); return 0; release_host: @@ -752,6 +804,7 @@ static void iscsi_conn_release(struct device *dev) struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); struct device *parent = conn->dev.parent; + ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n"); kfree(conn); put_device(parent); } @@ -774,6 +827,8 @@ void iscsi_remove_session(struct iscsi_cls_session *session) unsigned long flags; int err; + ISCSI_DBG_TRANS_SESSION(session, "Removing session\n"); + spin_lock_irqsave(&sesslock, flags); list_del(&session->sess_list); spin_unlock_irqrestore(&sesslock, flags); @@ -807,12 +862,15 @@ void iscsi_remove_session(struct iscsi_cls_session *session) "for session. Error %d.\n", err); transport_unregister_device(&session->dev); + + ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n"); device_del(&session->dev); } EXPORT_SYMBOL_GPL(iscsi_remove_session); void iscsi_free_session(struct iscsi_cls_session *session) { + ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n"); iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION); put_device(&session->dev); } @@ -828,6 +886,7 @@ EXPORT_SYMBOL_GPL(iscsi_free_session); int iscsi_destroy_session(struct iscsi_cls_session *session) { iscsi_remove_session(session); + ISCSI_DBG_TRANS_SESSION(session, "Completing session destruction\n"); iscsi_free_session(session); return 0; } @@ -885,6 +944,8 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) list_add(&conn->conn_list, &connlist); conn->active = 1; spin_unlock_irqrestore(&connlock, flags); + + ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n"); return conn; release_parent_ref: @@ -912,6 +973,7 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn) spin_unlock_irqrestore(&connlock, flags); transport_unregister_device(&conn->dev); + ISCSI_DBG_TRANS_CONN(conn, "Completing conn destruction\n"); device_unregister(&conn->dev); return 0; } @@ -938,23 +1000,9 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt) } static int -iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp) -{ - return netlink_broadcast(nls, skb, 0, 1, gfp); -} - -static int -iscsi_unicast_skb(struct sk_buff *skb, int pid) +iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp) { - 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; + return nlmsg_multicast(nls, skb, 0, group, gfp); } int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, @@ -980,7 +1028,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, return -ENOMEM; } - nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); + nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); ev = NLMSG_DATA(nlh); memset(ev, 0, sizeof(*ev)); ev->transport_handle = iscsi_handle(conn->transport); @@ -991,10 +1039,45 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, memcpy(pdu, hdr, sizeof(struct iscsi_hdr)); memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size); - return iscsi_unicast_skb(skb, priv->daemon_pid); + return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC); } EXPORT_SYMBOL_GPL(iscsi_recv_pdu); +int iscsi_offload_mesg(struct Scsi_Host *shost, + struct iscsi_transport *transport, uint32_t type, + char *data, uint16_t data_size) +{ + struct nlmsghdr *nlh; + struct sk_buff *skb; + struct iscsi_uevent *ev; + int len = NLMSG_SPACE(sizeof(*ev) + data_size); + + skb = alloc_skb(len, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "can not deliver iscsi offload message:OOM\n"); + return -ENOMEM; + } + + nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); + ev = NLMSG_DATA(nlh); + memset(ev, 0, sizeof(*ev)); + ev->type = type; + ev->transport_handle = iscsi_handle(transport); + switch (type) { + case ISCSI_KEVENT_PATH_REQ: + ev->r.req_path.host_no = shost->host_no; + break; + case ISCSI_KEVENT_IF_DOWN: + ev->r.notify_if_down.host_no = shost->host_no; + break; + } + + memcpy((char *)ev + sizeof(*ev), data, data_size); + + return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC); +} +EXPORT_SYMBOL_GPL(iscsi_offload_mesg); + void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) { struct nlmsghdr *nlh; @@ -1014,7 +1097,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) return; } - nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); + nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); ev = NLMSG_DATA(nlh); ev->transport_handle = iscsi_handle(conn->transport); ev->type = ISCSI_KEVENT_CONN_ERROR; @@ -1022,7 +1105,7 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) ev->r.connerror.cid = conn->cid; ev->r.connerror.sid = iscsi_conn_get_sid(conn); - iscsi_broadcast_skb(skb, GFP_ATOMIC); + iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC); iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n", error); @@ -1030,8 +1113,8 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) EXPORT_SYMBOL_GPL(iscsi_conn_error_event); static int -iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, - void *payload, int size) +iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi, + void *payload, int size) { struct sk_buff *skb; struct nlmsghdr *nlh; @@ -1045,10 +1128,10 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, return -ENOMEM; } - nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0); + nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0); nlh->nlmsg_flags = flags; memcpy(NLMSG_DATA(nlh), payload, size); - return iscsi_unicast_skb(skb, pid); + return iscsi_multicast_skb(skb, group, GFP_ATOMIC); } static int @@ -1085,7 +1168,7 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) return -ENOMEM; } - nlhstat = __nlmsg_put(skbstat, priv->daemon_pid, 0, 0, + nlhstat = __nlmsg_put(skbstat, 0, 0, 0, (len - sizeof(*nlhstat)), 0); evstat = NLMSG_DATA(nlhstat); memset(evstat, 0, sizeof(*evstat)); @@ -1109,7 +1192,8 @@ iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh) skb_trim(skbstat, NLMSG_ALIGN(actual_size)); nlhstat->nlmsg_len = actual_size; - err = iscsi_unicast_skb(skbstat, priv->daemon_pid); + err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID, + GFP_ATOMIC); } while (err < 0 && err != -ECONNREFUSED); return err; @@ -1143,7 +1227,7 @@ int iscsi_session_event(struct iscsi_cls_session *session, return -ENOMEM; } - nlh = __nlmsg_put(skb, priv->daemon_pid, 0, 0, (len - sizeof(*nlh)), 0); + nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0); ev = NLMSG_DATA(nlh); ev->transport_handle = iscsi_handle(session->transport); @@ -1172,12 +1256,15 @@ int iscsi_session_event(struct iscsi_cls_session *session, * 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); + rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL); if (rc == -ESRCH) iscsi_cls_session_printk(KERN_ERR, session, "Cannot notify userspace of session " "event %u. Check iscsi daemon\n", event); + + ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n", + event, rc); return rc; } EXPORT_SYMBOL_GPL(iscsi_session_event); @@ -1199,6 +1286,8 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep, shost = iscsi_session_to_shost(session); ev->r.c_session_ret.host_no = shost->host_no; ev->r.c_session_ret.sid = session->sid; + ISCSI_DBG_TRANS_SESSION(session, + "Completed creating transport session\n"); return 0; } @@ -1224,6 +1313,8 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) ev->r.c_conn_ret.sid = session->sid; ev->r.c_conn_ret.cid = conn->cid; + + ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n"); return 0; } @@ -1236,8 +1327,10 @@ iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev if (!conn) return -EINVAL; + ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n"); if (transport->destroy_conn) transport->destroy_conn(conn); + return 0; } @@ -1257,8 +1350,7 @@ iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) switch (ev->u.set_param.param) { case ISCSI_PARAM_SESS_RECOVERY_TMO: sscanf(data, "%d", &value); - if (value != 0) - session->recovery_tmo = value; + session->recovery_tmo = value; break; default: err = transport->set_param(conn, ev->u.set_param.param, @@ -1393,7 +1485,31 @@ iscsi_set_host_param(struct iscsi_transport *transport, } static int -iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev) +{ + struct Scsi_Host *shost; + struct iscsi_path *params; + int err; + + if (!transport->set_path) + return -ENOSYS; + + shost = scsi_host_lookup(ev->u.set_path.host_no); + if (!shost) { + printk(KERN_ERR "set path could not find host no %u\n", + ev->u.set_path.host_no); + return -ENODEV; + } + + params = (struct iscsi_path *)((char *)ev + sizeof(*ev)); + err = transport->set_path(shost, params); + + scsi_host_put(shost); + return err; +} + +static int +iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) { int err = 0; struct iscsi_uevent *ev = NLMSG_DATA(nlh); @@ -1403,6 +1519,11 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) struct iscsi_cls_conn *conn; struct iscsi_endpoint *ep = NULL; + if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE) + *group = ISCSI_NL_GRP_UIP; + else + *group = ISCSI_NL_GRP_ISCSID; + priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle)); if (!priv) return -EINVAL; @@ -1411,8 +1532,6 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) 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, ep, ev, @@ -1506,6 +1625,9 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) case ISCSI_UEVENT_SET_HOST_PARAM: err = iscsi_set_host_param(transport, ev); break; + case ISCSI_UEVENT_PATH_UPDATE: + err = iscsi_set_path(transport, ev); + break; default: err = -ENOSYS; break; @@ -1528,6 +1650,7 @@ iscsi_if_rx(struct sk_buff *skb) uint32_t rlen; struct nlmsghdr *nlh; struct iscsi_uevent *ev; + uint32_t group; nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) || @@ -1540,7 +1663,7 @@ iscsi_if_rx(struct sk_buff *skb) if (rlen > skb->len) rlen = skb->len; - err = iscsi_if_recv_msg(skb, nlh); + err = iscsi_if_recv_msg(skb, nlh, &group); if (err) { ev->type = ISCSI_KEVENT_IF_ERROR; ev->iferror = err; @@ -1554,8 +1677,7 @@ iscsi_if_rx(struct sk_buff *skb) */ if (ev->type == ISCSI_UEVENT_GET_STATS && !err) break; - err = iscsi_if_send_reply( - NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, + err = iscsi_if_send_reply(group, nlh->nlmsg_seq, nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); } while (err < 0 && err != -ECONNREFUSED); skb_pull(skb, rlen); @@ -1638,6 +1760,7 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1); iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0); iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0); iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0); +iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0); iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0); iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0) @@ -1803,7 +1926,6 @@ iscsi_register_transport(struct iscsi_transport *tt) 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->t.create_work_queue = 1; @@ -1880,6 +2002,7 @@ iscsi_register_transport(struct iscsi_transport *tt) SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT); SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO); SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO); + SETUP_SESSION_RD_ATTR(tgt_reset_tmo,ISCSI_TGT_RESET_TMO); SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME); SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME); SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo); diff --git a/kernel/scsi_transport_iscsi.h b/kernel/scsi_transport_iscsi.h index 6beea23..ef4b697 100644 --- a/kernel/scsi_transport_iscsi.h +++ b/kernel/scsi_transport_iscsi.h @@ -133,6 +133,7 @@ struct iscsi_transport { void (*ep_disconnect) (struct iscsi_endpoint *ep); int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type, uint32_t enable, struct sockaddr *dst_addr); + int (*set_path) (struct Scsi_Host *shost, struct iscsi_path *params); }; /* @@ -149,6 +150,10 @@ extern void iscsi_conn_error_event(struct iscsi_cls_conn *conn, extern int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size); +extern int iscsi_offload_mesg(struct Scsi_Host *shost, + struct iscsi_transport *transport, uint32_t type, + char *data, uint16_t data_size); + struct iscsi_cls_conn { struct list_head conn_list; /* item in connlist */ void *dd_data; /* LLD private data */ diff --git a/usr/Makefile b/usr/Makefile index 04f7707..4d3c71f 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -41,7 +41,7 @@ ISCSI_LIB_SRCS = util.o io.o auth.o login.o log.o md5.o sha1.o iface.o idbm.o sy COMMON_SRCS = $(ISCSI_LIB_SRCS) # core initiator files INITIATOR_SRCS = initiator.o scsi.o actor.o event_poll.o mgmt_ipc.o isns.o \ - cxgb3i.o transport.o + cxgb3i.o be2iscsi.o transport.o # fw boot files FW_BOOT_SRCS = $(wildcard ../utils/fwparam_ibft/*.o) diff --git a/usr/actor.c b/usr/actor.c index b487632..00f5c58 100644 --- a/usr/actor.c +++ b/usr/actor.c @@ -90,7 +90,7 @@ actor_delete(actor_t *thread) } static void -actor_schedule_private(actor_t *thread, uint32_t ttschedule) +actor_schedule_private(actor_t *thread, uint32_t ttschedule, int head) { uint64_t delay_time, current_time; actor_t *next_thread; @@ -115,10 +115,20 @@ actor_schedule_private(actor_t *thread, uint32_t ttschedule) if (delay_time == 0) { if (poll_in_progress) { thread->state = ACTOR_POLL_WAITING; - list_add_tail(&thread->list, &poll_list); + if (head) + list_add(&thread->list, + &poll_list); + else + list_add_tail(&thread->list, + &poll_list); } else { thread->state = ACTOR_SCHEDULED; - list_add_tail(&thread->list, &actor_list); + if (head) + list_add(&thread->list, + &actor_list); + else + list_add_tail(&thread->list, + &actor_list); } } else { thread->state = ACTOR_WAITING; @@ -159,9 +169,15 @@ done: } void +actor_schedule_head(actor_t *thread) +{ + actor_schedule_private(thread, 0, 1); +} + +void actor_schedule(actor_t *thread) { - actor_schedule_private(thread, 0); + actor_schedule_private(thread, 0, 0); } void @@ -169,7 +185,7 @@ actor_timer(actor_t *thread, uint32_t timeout, void (*callback)(void *), void *data) { actor_new(thread, callback, data); - actor_schedule_private(thread, timeout); + actor_schedule_private(thread, timeout, 0); } int @@ -178,7 +194,7 @@ actor_timer_mod(actor_t *thread, uint32_t timeout, void *data) if (thread->state == ACTOR_WAITING) { list_del_init(&thread->list); thread->data = data; - actor_schedule_private(thread, timeout); + actor_schedule_private(thread, timeout, 0); return 1; } return 0; diff --git a/usr/actor.h b/usr/actor.h index 7a71d42..704224d 100644 --- a/usr/actor.h +++ b/usr/actor.h @@ -44,6 +44,7 @@ typedef struct actor { extern void actor_new(actor_t *thread, void (*callback)(void *), void * data); extern void actor_delete(actor_t *thread); +extern void actor_schedule_head(actor_t *thread); extern void actor_schedule(actor_t *thread); extern void actor_timer(actor_t *thread, uint32_t timeout, void (*callback)(void *), void *data); diff --git a/usr/be2iscsi.c b/usr/be2iscsi.c new file mode 100644 index 0000000..b81c58d --- /dev/null +++ b/usr/be2iscsi.c @@ -0,0 +1,31 @@ +/* + * be2iscsi helpers + * + * Copyright (C) 2006 Mike Christie + * Copyright (C) 2006 Red Hat, Inc. All rights reserved. + * + * 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. + */ +#include "initiator.h" + +void be2iscsi_create_conn(struct iscsi_conn *conn) +{ + if (conn->max_recv_dlength > 65536) + conn->max_recv_dlength = 65536; + + if (conn->session->first_burst > 8192) + conn->session->first_burst = 8192; + + if (conn->session->max_burst > 262144) + conn->session->max_burst = 262144; + + conn->session->erl = 0; +} diff --git a/usr/be2iscsi.h b/usr/be2iscsi.h new file mode 100644 index 0000000..9e5c727 --- /dev/null +++ b/usr/be2iscsi.h @@ -0,0 +1,8 @@ +#ifndef BE2ISCSI_TRANSPORT +#define BE2ISCSI_TRANSPORT + +struct iscsi_conn; + +extern void be2iscsi_create_conn(struct iscsi_conn *conn); + +#endif diff --git a/usr/config.h b/usr/config.h index 354cd30..47b8d9b 100644 --- a/usr/config.h +++ b/usr/config.h @@ -86,6 +86,7 @@ struct iscsi_error_timeout_config { int abort_timeout; int host_reset_timeout; int lu_reset_timeout; + int tgt_reset_timeout; }; /* all TCP options go in this structure. diff --git a/usr/discovery.c b/usr/discovery.c index 3c9b64b..5c7ea66 100644 --- a/usr/discovery.c +++ b/usr/discovery.c @@ -130,7 +130,7 @@ int discovery_offload_sendtargets(int host_no, int do_login, * and get back the results. We should do this since it would * allows us to then process the results like software iscsi. */ - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 1); if (rc) { log_error("Could not offload sendtargets to %s.\n", drec->address); @@ -564,7 +564,7 @@ static int request_initiator_name(void) memset(&req, 0, sizeof(req)); req.command = MGMT_IPC_CONFIG_INAME; - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 1); if (rc) return EIO; @@ -574,7 +574,7 @@ static int request_initiator_name(void) memset(&req, 0, sizeof(req)); req.command = MGMT_IPC_CONFIG_IALIAS; - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 0); if (rc) /* alias is optional so return ok */ return 0; diff --git a/usr/idbm.c b/usr/idbm.c index 30c2102..c6cd7fd 100644 --- a/usr/idbm.c +++ b/usr/idbm.c @@ -127,67 +127,6 @@ static struct idbm *db; _n++; \ } while(0) -/* - * from linux kernel - */ -static char *strstrip(char *s) -{ - size_t size; - char *end; - - size = strlen(s); - if (!size) - return s; - - end = s + size - 1; - while (end >= s && isspace(*end)) - end--; - *(end + 1) = '\0'; - - while (*s && isspace(*s)) - s++; - - return s; -} - -static char *get_global_string_param(char *pathname, const char *key) -{ - FILE *f = NULL; - int len; - char *line, buffer[1024]; - char *name = NULL; - - if (!pathname) { - log_error("No pathname to load %s from", key); - return NULL; - } - - len = strlen(key); - if ((f = fopen(pathname, "r"))) { - while ((line = fgets(buffer, sizeof (buffer), f))) { - - line = strstrip(line); - - if (strncmp(line, key, len) == 0) { - char *end = line + len; - - /* - * make sure ther is something after the - * key. - */ - if (strlen(end)) - name = strdup(line + len); - } - } - fclose(f); - if (name) - log_debug(5, "%s=%s", key, name); - } else - log_error("can't open %s configuration file %s", key, pathname); - - return name; -} - char *get_iscsi_initiatorname(char *pathname) { char *name; @@ -324,6 +263,9 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri) __recinfo_int(SESSION_LU_RESET_TMO, ri, r, session.err_timeo.lu_reset_timeout, IDBM_SHOW, num, 1); + __recinfo_int(SESSION_TGT_RESET_TMO, ri, r, + session.err_timeo.tgt_reset_timeout, + IDBM_SHOW, num, 1); __recinfo_int(SESSION_HOST_RESET_TMO, ri, r, session.err_timeo.host_reset_timeout, IDBM_SHOW, num, 1); @@ -1580,17 +1522,15 @@ free_portal: return rc; } -int -idbm_add_discovery(discovery_rec_t *newrec, int overwrite) +int idbm_add_discovery(discovery_rec_t *newrec) { discovery_rec_t rec; int rc; if (!idbm_discovery_read(&rec, newrec->address, newrec->port)) { - if (!overwrite) - return 0; - log_debug(7, "overwriting existing record"); + log_debug(7, "disc rec already exists"); + return 0; } else log_debug(7, "adding new DB record"); @@ -2158,6 +2098,35 @@ free_info: return rc; } +int idbm_discovery_set_param(void *data, discovery_rec_t *rec) +{ + struct db_set_param *param = data; + recinfo_t *info; + int rc = 0; + + info = idbm_recinfo_alloc(MAX_KEYS); + if (!info) + return ENOMEM; + + idbm_recinfo_discovery((discovery_rec_t *)rec, info); + + rc = idbm_verify_param(info, param->name); + if (rc) + goto free_info; + + rc = idbm_rec_update_param(info, param->name, param->value, 0); + if (rc) + goto free_info; + + rc = idbm_discovery_write((discovery_rec_t *)rec); + if (rc) + goto free_info; + +free_info: + free(info); + return rc; +} + int idbm_init(idbm_get_config_file_fn *fn) { /* make sure root db dir is there */ diff --git a/usr/idbm.h b/usr/idbm.h index ed8badd..8fdd863 100644 --- a/usr/idbm.h +++ b/usr/idbm.h @@ -130,7 +130,7 @@ extern int idbm_bind_ifaces_to_nodes(idbm_disc_nodes_fn *disc_node_fn, extern int idbm_add_nodes(node_rec_t *newrec, discovery_rec_t *drec, struct list_head *ifaces, int overwrite); -extern int idbm_add_discovery(discovery_rec_t *newrec, int overwrite); +extern int idbm_add_discovery(discovery_rec_t *newrec); extern void idbm_sendtargets_defaults(struct iscsi_sendtargets_config *cfg); extern void idbm_slp_defaults(struct iscsi_slp_config *cfg); extern int idbm_discovery_read(discovery_rec_t *rec, char *addr, @@ -139,6 +139,7 @@ extern int idbm_rec_read(node_rec_t *out_rec, char *target_name, int tpgt, char *addr, int port, struct iface_rec *iface); extern int idbm_node_set_param(void *data, node_rec_t *rec); +extern int idbm_discovery_set_param(void *data, discovery_rec_t *rec); /* lower level idbm functions for use by iface.c */ extern void idbm_recinfo_config(recinfo_t *info, FILE *f); diff --git a/usr/idbm_fields.h b/usr/idbm_fields.h index aae36f7..ead8a65 100644 --- a/usr/idbm_fields.h +++ b/usr/idbm_fields.h @@ -31,6 +31,7 @@ #define SESSION_REPLACEMENT_TMO "node.session.timeo.replacement_timeout" #define SESSION_ABORT_TMO "node.session.err_timeo.abort_timeout" #define SESSION_LU_RESET_TMO "node.session.err_timeo.lu_reset_timeout" +#define SESSION_TGT_RESET_TMO "node.session.err_timeo.tgt_reset_timeout" #define SESSION_HOST_RESET_TMO "node.session.err_timeo.host_reset_timeout" #define SESSION_FAST_ABORT "node.session.iscsi.FastAbort" #define SESSION_INITIAL_R2T "node.session.iscsi.InitialR2T" diff --git a/usr/initiator.c b/usr/initiator.c index d7ff50f..7158a38 100644 --- a/usr/initiator.c +++ b/usr/initiator.c @@ -543,14 +543,10 @@ __session_create(node_rec_t *rec, struct iscsi_transport *t) /* session's eh parameters */ session->replacement_timeout = rec->session.timeo.replacement_timeout; - if (session->replacement_timeout == 0) { - log_error("Cannot set replacement_timeout to zero. Setting " - "120 seconds\n"); - session->replacement_timeout = DEF_REPLACEMENT_TIMEO; - } session->fast_abort = rec->session.iscsi.FastAbort; session->abort_timeout = rec->session.err_timeo.abort_timeout; session->lu_reset_timeout = rec->session.err_timeo.lu_reset_timeout; + session->tgt_reset_timeout = rec->session.err_timeo.tgt_reset_timeout; session->host_reset_timeout = rec->session.err_timeo.host_reset_timeout; /* OUI and uniqifying number */ @@ -1191,7 +1187,7 @@ mgmt_ipc_err_e iscsi_host_set_param(int host_no, int param, char *value) return MGMT_IPC_OK; } -#define MAX_SESSION_PARAMS 31 +#define MAX_SESSION_PARAMS 32 #define MAX_HOST_PARAMS 3 static void @@ -1366,6 +1362,11 @@ setup_full_feature_phase(iscsi_conn_t *conn) .type = ISCSI_INT, .conn_only = 0, }, { + .param = ISCSI_PARAM_TGT_RESET_TMO, + .value = &session->tgt_reset_timeout, + .type = ISCSI_INT, + .conn_only = 0, + }, { .param = ISCSI_PARAM_PING_TMO, .value = &conn->noop_out_timeout, .type = ISCSI_INT, @@ -1909,7 +1910,7 @@ static void session_conn_poll(void *data) /* do not allocate new connection in case of reopen */ if (session->id == -1) { if (conn->id == 0 && session_ipc_create(session)) { - log_error("can't create session (%d)", errno); + log_error("Can't create session."); err = MGMT_IPC_ERR_INTERNAL; goto cleanup; } @@ -1918,8 +1919,7 @@ static void session_conn_poll(void *data) if (ipc->create_conn(session->t->handle, session->id, conn->id, &conn->id)) { - log_error("can't create connection (%d)", - errno); + log_error("Can't create connection."); err = MGMT_IPC_ERR_INTERNAL; goto cleanup; } @@ -1981,6 +1981,8 @@ void iscsi_sched_conn_context(struct iscsi_conn_context *conn_context, struct iscsi_conn *conn, unsigned long tmo, int event) { + enum iscsi_err error; + log_debug(7, "sched conn context %p event %d, tmo %lu", &conn_context->actor, event, tmo); @@ -1992,9 +1994,19 @@ void iscsi_sched_conn_context(struct iscsi_conn_context *conn_context, actor_schedule(&conn_context->actor); break; case EV_CONN_ERROR: + error = *(enum iscsi_err *)conn_context->data; + actor_new(&conn_context->actor, session_conn_error, conn_context); - actor_schedule(&conn_context->actor); + /* + * We handle invalid host, by killing the session. + * It must go at the head of the queue, so we do not + * initiate error handling or logout or some other op. + */ + if (error == ISCSI_ERR_INVALID_HOST) + actor_schedule_head(&conn_context->actor); + else + actor_schedule(&conn_context->actor); break; case EV_CONN_POLL: actor_new(&conn_context->actor, session_conn_poll, @@ -2075,10 +2087,16 @@ static int iface_set_param(struct iscsi_transport *t, struct iface_rec *iface, iface->name, iface->netdev, iface->ipaddress, iface->hwaddress, iface->transport_name); - /* if we need to set the ip addr then set all the iface net settings */ - if (!iface_is_bound_by_ipaddr(iface)) + if (!t->template->set_host_ip) return 0; + /* if we need to set the ip addr then set all the iface net settings */ + if (!iface_is_bound_by_ipaddr(iface)) { + log_warning("Please set the iface.ipaddress for iface %s, " + "then retry the login command.\n", iface->name); + return EINVAL; + } + /* this assumes that the netdev or hw address is going to be set */ hostno = iscsi_sysfs_get_host_no_from_hwinfo(iface, &rc); if (rc) diff --git a/usr/initiator.h b/usr/initiator.h index 5bcf4d1..7f030bb 100644 --- a/usr/initiator.h +++ b/usr/initiator.h @@ -252,6 +252,7 @@ typedef struct iscsi_session { uint32_t replacement_timeout; int host_reset_timeout; + int tgt_reset_timeout; int lu_reset_timeout; int abort_timeout; diff --git a/usr/iscsi_settings.h b/usr/iscsi_settings.h index 32d0e85..3d923c8 100644 --- a/usr/iscsi_settings.h +++ b/usr/iscsi_settings.h @@ -11,6 +11,7 @@ #define DEF_ABORT_TIMEO 15 #define DEF_LU_RESET_TIMEO 30 +#define DEF_TGT_RESET_TIMEO 30 #define DEF_HOST_RESET_TIMEO 60 /* q depths */ diff --git a/usr/iscsiadm.c b/usr/iscsiadm.c index 4cd02fe..d4f7925 100644 --- a/usr/iscsiadm.c +++ b/usr/iscsiadm.c @@ -198,7 +198,7 @@ static void kill_iscsid(int priority) memset(&req, 0, sizeof(req)); req.command = MGMT_IPC_IMMEDIATE_STOP; - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 0); if (rc) { iscsid_handle_error(rc); log_error("Could not stop iscsid. Trying sending iscsid " @@ -794,7 +794,7 @@ static char *get_config_file(void) memset(&req, 0, sizeof(req)); req.command = MGMT_IPC_CONFIG_FILE; - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 1); if (rc) return NULL; @@ -844,7 +844,7 @@ session_stats(void *data, struct session_info *info) req.command = MGMT_IPC_SESSION_STATS; req.u.session.sid = info->sid; - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 1); if (rc) return EIO; @@ -1183,7 +1183,7 @@ do_software_sendtargets(discovery_rec_t *drec, struct list_head *ifaces, * DB lined up, but for now just put all the targets found from * a discovery portal in one place */ - rc = idbm_add_discovery(drec, op & OP_UPDATE); + rc = idbm_add_discovery(drec); if (rc) { log_error("Could not add new discovery record."); return rc; @@ -1277,7 +1277,7 @@ static int isns_dev_attr_query(discovery_rec_t *drec, memset(&req, 0, sizeof(iscsiadm_req_t)); req.command = MGMT_IPC_ISNS_DEV_ATTR_QUERY; - err = do_iscsid(&req, &rsp); + err = do_iscsid(&req, &rsp, 1); if (err) { iscsid_handle_error(err); return EIO; @@ -1912,7 +1912,7 @@ main(int argc, char **argv) name, value); break; case MODE_DISCOVERY: - if ((rc = verify_mode_params(argc, argv, "SIPdmtplo", 0))) { + if ((rc = verify_mode_params(argc, argv, "SIPdmntplov", 0))) { log_error("discovery mode: option '-%c' is not " "allowed/supported", rc); rc = -1; @@ -1927,9 +1927,11 @@ main(int argc, char **argv) goto out; } - idbm_sendtargets_defaults(&drec.u.sendtargets); - strlcpy(drec.address, ip, sizeof(drec.address)); - drec.port = port; + if (idbm_discovery_read(&drec, ip, port)) { + idbm_sendtargets_defaults(&drec.u.sendtargets); + strlcpy(drec.address, ip, sizeof(drec.address)); + drec.port = port; + } if (do_sendtargets(&drec, &ifaces, info_level, do_login, op)) { @@ -1991,6 +1993,20 @@ main(int argc, char **argv) "record!"); rc = -1; } + } else if (op == OP_UPDATE) { + struct db_set_param set_param; + + if (!name || !value) { + log_error("Update requires " + "name and value"); + rc = -1; + goto out; + } + set_param.name = name; + set_param.value = value; + if (idbm_discovery_set_param(&set_param, + &drec)) + rc = -1; } else { log_error("operation is not supported."); rc = -1; diff --git a/usr/iscsid.c b/usr/iscsid.c index ea29bb2..47a7599 100644 --- a/usr/iscsid.c +++ b/usr/iscsid.c @@ -190,6 +190,11 @@ static int sync_session(void *data, struct session_info *info) t = iscsi_sysfs_get_transport_by_sid(info->sid); if (!t) return 0; + if (set_transport_template(t)) { + log_error("Could not find userspace transport template for %s", + t->name); + return 0; + } /* * Just rescan the device in case this is the first startup. @@ -210,6 +215,13 @@ static int sync_session(void *data, struct session_info *info) } memset(&rec, 0, sizeof(node_rec_t)); + /* + * We might get the local ip address for software. We do not + * want to try and bind a session by ip though. + */ + if (!t->template->set_host_ip) + memset(info->iface.ipaddress, 0, sizeof(info->iface.ipaddress)); + if (idbm_rec_read(&rec, info->targetname, info->tpgt, info->persistent_address, info->persistent_port, &info->iface)) { @@ -254,7 +266,7 @@ static int sync_session(void *data, struct session_info *info) memcpy(&req.u.session.rec, &rec, sizeof(node_rec_t)); retry: - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 0); if (rc == MGMT_IPC_ERR_ISCSID_NOTCONN && retries < 30) { retries++; sleep(1); diff --git a/usr/iscsistart.c b/usr/iscsistart.c index 90f4e24..8482ad5 100644 --- a/usr/iscsistart.c +++ b/usr/iscsistart.c @@ -115,7 +115,7 @@ static int stop_event_loop(void) memset(&req, 0, sizeof(req)); req.command = MGMT_IPC_IMMEDIATE_STOP; - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 0); if (rc) { iscsid_handle_error(rc); log_error("Could not stop event_loop\n"); @@ -146,7 +146,7 @@ static int login_session(void) memcpy(&req.u.session.rec, &config_rec, sizeof(node_rec_t)); retry: - rc = do_iscsid(&req, &rsp); + rc = do_iscsid(&req, &rsp, 0); /* * handle race where iscsid proc is starting up while we are * trying to connect. diff --git a/usr/netlink.c b/usr/netlink.c index 83d162a..293d5fd 100644 --- a/usr/netlink.c +++ b/usr/netlink.c @@ -255,7 +255,7 @@ kwritev(enum iscsi_uevent_e type, struct iovec *iovp, int count) static int __kipc_call(void *iov_base, int iov_len) { - int rc; + int rc, iferr; struct iovec iov; struct iscsi_uevent *ev = iov_base; enum iscsi_uevent_e type = ev->type; @@ -280,18 +280,22 @@ __kipc_call(void *iov_base, int iov_len) sizeof(*ev), 0)) < 0) { return rc; } - if (ev->iferror == -ENOSYS) + /* + * iferror is u32, but the kernel returns + * negative errno values for errors. + */ + iferr = ev->iferror; + + if (iferr == -ENOSYS) /* not fatal so let caller handle log */ - log_debug(1, "Recieved iferror %d: %s", - ev->iferror, - strerror(ev->iferror)); - else if (ev->iferror < 0) - log_error("Received iferror %d: %s", - ev->iferror, - strerror(ev->iferror)); + log_debug(1, "Recieved iferror %d: %s.", + iferr, strerror(-iferr)); + else if (iferr < 0) + log_error("Received iferror %d: %s.", + iferr, strerror(-iferr)); else - log_error("Received iferror %d", - ev->iferror); + log_error("Received iferror %d.", + iferr); return ev->iferror; } /* diff --git a/usr/session_info.c b/usr/session_info.c index 631e006..1124a06 100644 --- a/usr/session_info.c +++ b/usr/session_info.c @@ -107,7 +107,7 @@ static int print_iscsi_state(int sid, char *prefix) req.command = MGMT_IPC_SESSION_INFO; req.u.session.sid = sid; - err = do_iscsid(&req, &rsp); + err = do_iscsid(&req, &rsp, 1); /* * for drivers like qla4xxx, iscsid does not display * anything here since it does not know about it. diff --git a/usr/transport.c b/usr/transport.c index 76e7aef..ebdfb1b 100644 --- a/usr/transport.c +++ b/usr/transport.c @@ -26,6 +26,7 @@ #include "util.h" #include "iscsi_sysfs.h" #include "cxgb3i.h" +#include "be2iscsi.h" struct iscsi_transport_template iscsi_tcp = { .name = "tcp", @@ -44,6 +45,7 @@ struct iscsi_transport_template iscsi_iser = { struct iscsi_transport_template cxgb3i = { .name = "cxgb3i", + .set_host_ip = 1, .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, .ep_disconnect = ktransport_ep_disconnect, @@ -52,6 +54,16 @@ struct iscsi_transport_template cxgb3i = { struct iscsi_transport_template bnx2i = { .name = "bnx2i", + .set_host_ip = 1, + .ep_connect = ktransport_ep_connect, + .ep_poll = ktransport_ep_poll, + .ep_disconnect = ktransport_ep_disconnect, +}; + +struct iscsi_transport_template be2iscsi = { + .name = "be2iscsi", + .set_host_ip = 1, + .create_conn = be2iscsi_create_conn, .ep_connect = ktransport_ep_connect, .ep_poll = ktransport_ep_poll, .ep_disconnect = ktransport_ep_disconnect, @@ -67,6 +79,7 @@ static struct iscsi_transport_template *iscsi_transport_templates[] = { &cxgb3i, &bnx2i, &qla4xxx, + &be2iscsi, NULL }; diff --git a/usr/transport.h b/usr/transport.h index fe9578d..5ceedb3 100644 --- a/usr/transport.h +++ b/usr/transport.h @@ -26,6 +26,11 @@ struct iscsi_conn; struct iscsi_transport_template { const char *name; uint8_t rdma; + /* + * Drivers should set this if they require iscsid to set + * the host's ip address. + */ + uint8_t set_host_ip; int (*ep_connect) (struct iscsi_conn *conn, int non_blocking); int (*ep_poll) (struct iscsi_conn *conn, int timeout_ms); void (*ep_disconnect) (struct iscsi_conn *conn); diff --git a/usr/util.c b/usr/util.c index 5b31e89..e72ed15 100644 --- a/usr/util.c +++ b/usr/util.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -119,9 +120,88 @@ int increase_max_files(void) return 0; } +/* + * from linux kernel + */ +char *strstrip(char *s) +{ + size_t size; + char *end; + + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + while (end >= s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + while (*s && isspace(*s)) + s++; + + return s; +} + +char *get_global_string_param(char *pathname, const char *key) +{ + FILE *f = NULL; + int len; + char *line, buffer[1024]; + char *name = NULL; + + if (!pathname) { + log_error("No pathname to load %s from", key); + return NULL; + } + + len = strlen(key); + if ((f = fopen(pathname, "r"))) { + while ((line = fgets(buffer, sizeof (buffer), f))) { + + line = strstrip(line); + + if (strncmp(line, key, len) == 0) { + char *end = line + len; + + /* + * make sure there is something after the + * key. + */ + if (strlen(end)) + name = strdup(line + len); + } + } + fclose(f); + if (name) + log_debug(5, "%s=%s", key, name); + } else + log_error("can't open %s configuration file %s", key, pathname); + + return name; +} + +/* TODO: move iscsid client helpers to file */ +static void iscsid_startup(void) +{ + char *startup_cmd; + + startup_cmd = get_global_string_param(CONFIG_FILE, "iscsid.startup = "); + if (!startup_cmd) { + log_error("iscsid is not running. Could not start it up " + "automatically using the startup command in " + "/etc/iscsi/iscsid.start. Please check that the " + "file exists or that your init scripts have " + "started iscsid."); + return; + } + + system(startup_cmd); +} + #define MAXSLEEP 128 -static mgmt_ipc_err_e iscsid_connect(int *fd) +static mgmt_ipc_err_e iscsid_connect(int *fd, int start_iscsid) { int nsec; struct sockaddr_un addr; @@ -146,8 +226,12 @@ static mgmt_ipc_err_e iscsid_connect(int *fd) /* If iscsid isn't there, there's no sense * in retrying. */ - if (errno == ECONNREFUSED) - break; + if (errno == ECONNREFUSED) { + if (start_iscsid && nsec == 1) + iscsid_startup(); + else + break; + } /* * Delay before trying again @@ -159,11 +243,11 @@ static mgmt_ipc_err_e iscsid_connect(int *fd) return MGMT_IPC_ERR_ISCSID_NOTCONN; } -mgmt_ipc_err_e iscsid_request(int *fd, iscsiadm_req_t *req) +mgmt_ipc_err_e iscsid_request(int *fd, iscsiadm_req_t *req, int start_iscsid) { int err; - err = iscsid_connect(fd); + err = iscsid_connect(fd, start_iscsid); if (err) return err; @@ -193,12 +277,13 @@ mgmt_ipc_err_e iscsid_response(int fd, iscsiadm_cmd_e cmd, iscsiadm_rsp_t *rsp) return iscsi_err; } -mgmt_ipc_err_e do_iscsid(iscsiadm_req_t *req, iscsiadm_rsp_t *rsp) +mgmt_ipc_err_e do_iscsid(iscsiadm_req_t *req, iscsiadm_rsp_t *rsp, + int start_iscsid) { int fd; mgmt_ipc_err_e err; - err = iscsid_request(&fd, req); + err = iscsid_request(&fd, req, start_iscsid); if (err) return err; @@ -221,7 +306,7 @@ int iscsid_req_by_rec_async(iscsiadm_cmd_e cmd, node_rec_t *rec, int *fd) req.command = cmd; memcpy(&req.u.session.rec, rec, sizeof(node_rec_t)); - return iscsid_request(fd, &req); + return iscsid_request(fd, &req, 1); } int iscsid_req_by_rec(iscsiadm_cmd_e cmd, node_rec_t *rec) @@ -242,7 +327,7 @@ int iscsid_req_by_sid_async(iscsiadm_cmd_e cmd, int sid, int *fd) req.command = cmd; req.u.session.sid = sid; - return iscsid_request(fd, &req); + return iscsid_request(fd, &req, 1); } int iscsid_req_by_sid(iscsiadm_cmd_e cmd, int sid) @@ -276,6 +361,7 @@ void idbm_node_setup_defaults(node_rec_t *rec) rec->session.auth.password_in_length = 0; rec->session.err_timeo.abort_timeout = DEF_ABORT_TIMEO; rec->session.err_timeo.lu_reset_timeout = DEF_LU_RESET_TIMEO; + rec->session.err_timeo.tgt_reset_timeout = DEF_TGT_RESET_TIMEO; rec->session.err_timeo.host_reset_timeout = DEF_HOST_RESET_TIMEO; rec->session.timeo.replacement_timeout = DEF_REPLACEMENT_TIMEO; rec->session.iscsi.InitialR2T = 0; diff --git a/usr/util.h b/usr/util.h index b88fd65..1d94496 100644 --- a/usr/util.h +++ b/usr/util.h @@ -13,9 +13,11 @@ extern int oom_adjust(void); extern void daemon_init(void); extern int increase_max_files(void); -extern int do_iscsid(struct iscsiadm_req *req, struct iscsiadm_rsp *rsp); +extern int do_iscsid(struct iscsiadm_req *req, struct iscsiadm_rsp *rsp, + int iscsid_start); extern void iscsid_handle_error(int err); -extern int iscsid_request(int *fd, struct iscsiadm_req *req); +extern int iscsid_request(int *fd, struct iscsiadm_req *req, + int iscsid_start); extern int iscsid_response(int fd, int cmd, struct iscsiadm_rsp *rsp); extern int iscsid_req_wait(int cmd, int fd); extern int iscsid_req_by_rec_async(int cmd, struct node_rec *rec, int *fd); @@ -31,4 +33,7 @@ extern int __iscsi_match_session(struct node_rec *rec, char *targetname, char *address, int port, struct iface_rec *iface); +extern char *strstrip(char *s); +extern char *get_global_string_param(char *pathname, const char *key); + #endif diff --git a/utils/iscsi-iname.c b/utils/iscsi-iname.c index 7cf0e7a..6347edc 100644 --- a/utils/iscsi-iname.c +++ b/utils/iscsi-iname.c @@ -62,7 +62,8 @@ main(int argc, char *argv[]) prefix = argv[1]; if (( strcmp(prefix, "-h") == 0 ) || ( strcmp(prefix, "--help") == 0 )) { - printf("\nDisplays the iSCSI initiator name\n"); + printf("\nGenerates a unique iSCSI node name " + "on every invocation.\n"); exit(0); } else if ( strcmp(prefix, "-p") == 0 ) { prefix = argv[2];