summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2007-08-27 02:41:25 -0500
committerMike Christie <michaelc@cs.wisc.edu>2007-08-27 19:37:54 -0500
commit13b02660d0866725d10aa3bba03b949df23fef20 (patch)
treece6ecc82d309838701e9aaa5ebb721bb49010f4c
parenta3e4c5202918409d54a893216a1d6555dc38cfe9 (diff)
downloadopen-iscsi-13b02660d0866725d10aa3bba03b949df23fef20.tar.gz
mv nop handling to kernel
During root boot and shutdown the target could send us nops. At this time iscsid cannot be running, so the target will drop the session and the boot or shutdown will hang. This patch moves the nop handling to the kernel.
-rw-r--r--etc/iscsid.conf19
-rw-r--r--include/iscsi_if.h11
-rw-r--r--kernel/iscsi_tcp.c4
-rw-r--r--kernel/libiscsi.c331
-rw-r--r--kernel/libiscsi.h8
-rw-r--r--kernel/scsi_transport_iscsi.c4
-rw-r--r--usr/config.h5
-rw-r--r--usr/discovery.c7
-rw-r--r--usr/idbm.c24
-rw-r--r--usr/initiator.c58
-rw-r--r--usr/initiator.h7
-rw-r--r--usr/iscsi_settings.h4
-rw-r--r--usr/netlink.c1
-rw-r--r--usr/util.c9
14 files changed, 361 insertions, 131 deletions
diff --git a/etc/iscsid.conf b/etc/iscsid.conf
index e950e3f..ea05c89 100644
--- a/etc/iscsid.conf
+++ b/etc/iscsid.conf
@@ -83,18 +83,14 @@ node.conn[0].timeo.login_timeout = 15
# The value is in seconds and the default is 15 seconds.
node.conn[0].timeo.logout_timeout = 15
-# To specify the intervale to send iSCSI Nop-outs as pings
-# to the target edit this the line.
-# The value is in seconds and the default is 10 seconds.
-node.conn[0].timeo.noop_out_interval = 10
+# Time interval to wait for on connection before sending a ping.
+node.conn[0].timeo.noop_out_interval = 5
# To specify the time to wait for a Nop-out response before failing
# the connection, edit this line. Failing the connection will
# cause IO to be failed back to the SCSI layer. If using dm-multipath
# this will cause the IO to be failed to the multipath layer.
-# The value is in seconds and the default is 15 seconds.
-node.conn[0].timeo.noop_out_timeout = 15
-
+node.conn[0].timeo.noop_out_timeout = 5
#******
# Retry
@@ -212,11 +208,10 @@ discovery.sendtargets.iscsi.MaxRecvDataSegmentLength = 32768
# Some targets like IET prefer after an initiator has sent a task
# management function like an ABORT TASK or LOGICAL UNIT RESET, that
# it does not respond to PDUs like R2Ts. To enable this behavior uncomment
-# the following line (The default behavior is No):
-# node.session.iscsi.FastAbort = Yes
+# the following line (The default behavior is Yes):
+node.session.iscsi.FastAbort = Yes
# Some targets like Equalogic prefer that after an initiator has sent
# a task management function like an ABORT TASK or LOGICAL UNIT RESET, that
-# it continue to respond to R2Ts. To enable this uncomment this line (This
-# is the default behavior):
-node.session.iscsi.FastAbort = No
+# it continue to respond to R2Ts. To enable this uncomment this line
+# node.session.iscsi.FastAbort = No
diff --git a/include/iscsi_if.h b/include/iscsi_if.h
index 7820294..dff4f29 100644
--- a/include/iscsi_if.h
+++ b/include/iscsi_if.h
@@ -244,6 +244,12 @@ enum iscsi_param {
ISCSI_PARAM_PASSWORD_IN,
ISCSI_PARAM_FAST_ABORT,
+ ISCSI_PARAM_ABORT_TMO,
+ ISCSI_PARAM_LU_RESET_TMO,
+ ISCSI_PARAM_HOST_RESET_TMO,
+
+ ISCSI_PARAM_PING_TMO,
+ ISCSI_PARAM_RECV_TMO,
/* must always be last */
ISCSI_PARAM_MAX,
};
@@ -275,6 +281,11 @@ enum iscsi_param {
#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)
#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN)
#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT)
+#define ISCSI_ABORT_TMO (1 << ISCSI_PARAM_ABORT_TMO)
+#define ISCSI_LU_RESET_TMO (1 << ISCSI_PARAM_LU_RESET_TMO)
+#define ISCSI_HOST_RESET_TMO (1 << ISCSI_PARAM_HOST_RESET_TMO)
+#define ISCSI_PING_TMO (1 << ISCSI_PARAM_PING_TMO)
+#define ISCSI_RECV_TMO (1 << ISCSI_PARAM_RECV_TMO)
/* iSCSI HBA params */
enum iscsi_host_param {
diff --git a/kernel/iscsi_tcp.c b/kernel/iscsi_tcp.c
index 2b84855..a51c564 100644
--- a/kernel/iscsi_tcp.c
+++ b/kernel/iscsi_tcp.c
@@ -2301,7 +2301,9 @@ static struct iscsi_transport iscsi_tcp_transport = {
ISCSI_TARGET_NAME | ISCSI_TPGT |
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
- ISCSI_FAST_ABORT,
+ ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
+ ISCSI_LU_RESET_TMO |
+ ISCSI_PING_TMO | ISCSI_RECV_TMO,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
ISCSI_HOST_INITIATOR_NAME |
ISCSI_HOST_NETDEV_NAME,
diff --git a/kernel/libiscsi.c b/kernel/libiscsi.c
index 7f94987..5f695b6 100644
--- a/kernel/libiscsi.c
+++ b/kernel/libiscsi.c
@@ -283,11 +283,70 @@ void iscsi_free_mgmt_task(struct iscsi_conn *conn,
list_del_init(&mtask->running);
if (conn->login_mtask == mtask)
return;
+
+ if (conn->ping_mtask == mtask)
+ conn->ping_mtask = NULL;
__kfifo_put(conn->session->mgmtpool.queue,
(void*)&mtask, sizeof(void*));
}
EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
+static struct iscsi_mgmt_task *
+__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size)
+{
+ struct iscsi_session *session = conn->session;
+ struct iscsi_mgmt_task *mtask;
+
+ 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))
+ /*
+ * Login and Text are sent serially, in
+ * request-followed-by-response sequence.
+ * Same mtask can be used. Same ITT must be used.
+ * Note that login_mtask is preallocated at conn_create().
+ */
+ mtask = conn->login_mtask;
+ else {
+ BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
+ BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
+
+ if (!__kfifo_get(session->mgmtpool.queue,
+ (void*)&mtask, sizeof(void*)))
+ return NULL;
+ }
+
+ if (data_size) {
+ memcpy(mtask->data, data, data_size);
+ mtask->data_count = data_size;
+ } else
+ mtask->data_count = 0;
+
+ memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
+ INIT_LIST_HEAD(&mtask->running);
+ list_add_tail(&mtask->running, &conn->mgmtqueue);
+ return mtask;
+}
+
+int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
+ char *data, uint32_t data_size)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+ int err = 0;
+
+ spin_lock_bh(&session->lock);
+ if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
+ err = -EPERM;
+ spin_unlock_bh(&session->lock);
+ scsi_queue_work(session->host, &conn->xmitwork);
+ return err;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+
/**
* iscsi_cmd_rsp - SCSI Command Response processing
* @conn: iscsi connection
@@ -380,6 +439,39 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
wake_up(&conn->ehwait);
}
+static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
+{
+ struct iscsi_nopout hdr;
+ struct iscsi_mgmt_task *mtask;
+
+ if (!rhdr && conn->ping_mtask)
+ return;
+
+ memset(&hdr, 0, sizeof(struct iscsi_nopout));
+ hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+ hdr.flags = ISCSI_FLAG_CMD_FINAL;
+
+ if (rhdr) {
+ memcpy(hdr.lun, rhdr->lun, 8);
+ hdr.ttt = rhdr->ttt;
+ hdr.itt = RESERVED_ITT;
+ } else
+ hdr.ttt = RESERVED_ITT;
+
+ mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+ if (!mtask) {
+ printk(KERN_ERR "Could not send nopout\n");
+ return;
+ }
+
+ /* only track our nops */
+ if (!rhdr) {
+ conn->ping_mtask = mtask;
+ conn->last_ping = jiffies;
+ }
+ scsi_queue_work(conn->session->host, &conn->xmitwork);
+}
+
static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, int datalen)
{
@@ -424,6 +516,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
struct iscsi_mgmt_task *mtask;
uint32_t itt;
+ conn->last_recv = jiffies;
if (hdr->itt != RESERVED_ITT)
itt = get_itt(hdr->itt);
else
@@ -491,14 +584,22 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
iscsi_free_mgmt_task(conn, mtask);
break;
case ISCSI_OP_NOOP_IN:
- if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) {
+ if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) ||
+ datalen) {
rc = ISCSI_ERR_PROTO;
break;
}
conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
- if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
- rc = ISCSI_ERR_CONN_FAILED;
+ if (conn->ping_mtask != mtask) {
+ /*
+ * If this is not in response to one of our
+ * nops then it must be from userspace.
+ */
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
+ datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+ }
iscsi_free_mgmt_task(conn, mtask);
break;
default:
@@ -518,8 +619,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
break;
- if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0))
- rc = ISCSI_ERR_CONN_FAILED;
+ iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr);
break;
case ISCSI_OP_REJECT:
rc = iscsi_handle_reject(conn, hdr, data, datalen);
@@ -971,62 +1071,6 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
}
EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
-static struct iscsi_mgmt_task *
-__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
- char *data, uint32_t data_size)
-{
- struct iscsi_session *session = conn->session;
- struct iscsi_mgmt_task *mtask;
-
- 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))
- /*
- * Login and Text are sent serially, in
- * request-followed-by-response sequence.
- * Same mtask can be used. Same ITT must be used.
- * Note that login_mtask is preallocated at conn_create().
- */
- mtask = conn->login_mtask;
- else {
- BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
- BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
-
- if (!__kfifo_get(session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*)))
- return NULL;
- }
-
- if (data_size) {
- memcpy(mtask->data, data, data_size);
- mtask->data_count = data_size;
- } else
- mtask->data_count = 0;
-
- memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
- INIT_LIST_HEAD(&mtask->running);
- list_add_tail(&mtask->running, &conn->mgmtqueue);
- return mtask;
-}
-
-int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
- char *data, uint32_t data_size)
-{
- struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
- int err = 0;
-
- spin_lock_bh(&session->lock);
- if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
- err = -EPERM;
- spin_unlock_bh(&session->lock);
- scsi_queue_work(session->host, &conn->xmitwork);
- return err;
-}
-EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
-
void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
{
struct iscsi_session *session = class_to_transport_session(cls_session);
@@ -1102,7 +1146,8 @@ static void iscsi_tmf_timedout(unsigned long data)
}
static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
- struct iscsi_tm *hdr, int age)
+ struct iscsi_tm *hdr, int age,
+ int timeout)
{
struct iscsi_session *session = conn->session;
struct iscsi_mgmt_task *mtask;
@@ -1117,7 +1162,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
return -EPERM;
}
conn->tmfcmd_pdus_cnt++;
- conn->tmf_timer.expires = 30 * HZ + jiffies;
+ conn->tmf_timer.expires = timeout * HZ + jiffies;
conn->tmf_timer.function = iscsi_tmf_timedout;
conn->tmf_timer.data = (unsigned long)conn;
add_timer(&conn->tmf_timer);
@@ -1201,6 +1246,106 @@ static void iscsi_start_tx(struct iscsi_conn *conn)
scsi_queue_work(conn->session->host, &conn->xmitwork);
}
+static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
+{
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+ enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
+
+ cls_session = starget_to_session(scsi_target(scmd->device));
+ session = class_to_transport_session(cls_session);
+
+ debug_scsi("scsi cmd %p timedout\n", scmd);
+
+ spin_lock(&session->lock);
+ if (session->state != ISCSI_STATE_LOGGED_IN) {
+ /*
+ * We are probably in the middle of iscsi recovery so let
+ * that complete and handle the error.
+ */
+ rc = EH_RESET_TIMER;
+ goto done;
+ }
+
+ conn = session->leadconn;
+ if (!conn) {
+ /* In the middle of shuting down */
+ rc = EH_RESET_TIMER;
+ goto done;
+ }
+
+ if (!conn->recv_timeout && !conn->ping_timeout)
+ goto done;
+ /*
+ * if the ping timedout then we are in the middle of cleaning up
+ * and can let the iscsi eh handle it
+ */
+ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+ (conn->ping_timeout * HZ), jiffies))
+ rc = EH_RESET_TIMER;
+ /*
+ * if we are about to check the transport then give the command
+ * more time
+ */
+ if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
+ jiffies))
+ rc = EH_RESET_TIMER;
+ /* if in the middle of checking the transport then give us more time */
+ if (conn->ping_mtask)
+ rc = EH_RESET_TIMER;
+done:
+ spin_unlock(&session->lock);
+ debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh");
+ return rc;
+}
+
+static void iscsi_check_transport_timeouts(unsigned long data)
+{
+ struct iscsi_conn *conn = (struct iscsi_conn *)data;
+ struct iscsi_session *session = conn->session;
+ unsigned long timeout, next_timeout = 0, last_recv;
+
+ spin_lock(&session->lock);
+ if (session->state != ISCSI_STATE_LOGGED_IN)
+ goto done;
+
+ timeout = conn->recv_timeout;
+ if (!timeout)
+ goto done;
+
+ timeout *= HZ;
+ last_recv = conn->last_recv;
+ if (time_before_eq(last_recv + timeout + (conn->ping_timeout * HZ),
+ jiffies)) {
+ printk(KERN_ERR "ping timeout of %d secs expired, "
+ "last rx %lu, last ping %lu, now %lu\n",
+ conn->ping_timeout, last_recv,
+ conn->last_ping, jiffies);
+ spin_unlock(&session->lock);
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ return;
+ }
+
+ if (time_before_eq(last_recv + timeout, jiffies)) {
+ if (time_before_eq(conn->last_ping, last_recv)) {
+ /* send a ping to try to provoke some traffic */
+ debug_scsi("Sending nopout as ping on conn %p\n", conn);
+ iscsi_send_nopout(conn, NULL);
+ }
+ next_timeout = last_recv + timeout + (conn->ping_timeout * HZ);
+ } else {
+ next_timeout = last_recv + timeout;
+ }
+
+ if (next_timeout) {
+ debug_scsi("Setting next tmo %lu\n", next_timeout);
+ mod_timer(&conn->transport_timer, next_timeout);
+ }
+done:
+ spin_unlock(&session->lock);
+}
+
static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask,
struct iscsi_tm *hdr)
{
@@ -1272,7 +1417,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
hdr = &conn->tmhdr;
iscsi_prep_abort_task_pdu(ctask, hdr);
- if (iscsi_exec_task_mgmt_fn(conn, hdr, age)) {
+ if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
rc = FAILED;
goto failed;
}
@@ -1333,7 +1478,7 @@ static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
- hdr->rtt = ISCSI_RESERVED_TAG;
+ hdr->rtt = RESERVED_ITT;
}
int iscsi_eh_device_reset(struct scsi_cmnd *sc)
@@ -1364,7 +1509,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
hdr = &conn->tmhdr;
iscsi_prep_lun_reset_pdu(sc, hdr);
- if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age)) {
+ if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+ session->lu_reset_timeout)) {
rc = FAILED;
goto unlock;
}
@@ -1535,12 +1681,14 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
shost->max_cmd_len = iscsit->max_cmd_len;
shost->transportt = scsit;
shost->transportt->create_work_queue = 1;
+ shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
*hostno = shost->host_no;
session = iscsi_hostdata(shost->hostdata);
memset(session, 0, sizeof(struct iscsi_session));
session->host = shost;
session->state = ISCSI_STATE_FREE;
+ session->fast_abort = 1;
session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
session->cmds_max = cmds_max;
session->queued_cmdsn = session->cmdsn = initial_cmdsn;
@@ -1671,6 +1819,11 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
conn->id = conn_idx;
conn->exp_statsn = 0;
conn->tmf_state = TMF_INITIAL;
+
+ init_timer(&conn->transport_timer);
+ conn->transport_timer.data = (unsigned long)conn;
+ conn->transport_timer.function = iscsi_check_transport_timeouts;
+
INIT_LIST_HEAD(&conn->run_list);
INIT_LIST_HEAD(&conn->mgmt_run_list);
INIT_LIST_HEAD(&conn->mgmtqueue);
@@ -1720,6 +1873,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
struct iscsi_session *session = conn->session;
unsigned long flags;
+ del_timer_sync(&conn->transport_timer);
+
spin_lock_bh(&session->lock);
conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
if (session->leadconn == conn) {
@@ -1786,11 +1941,29 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
return -EINVAL;
}
+ if (conn->ping_timeout && !conn->recv_timeout) {
+ printk(KERN_ERR "iscsi: invalid recv timeout of zero "
+ "Using 5 seconds\n.");
+ conn->recv_timeout = 5;
+ }
+
+ if (conn->recv_timeout && !conn->ping_timeout) {
+ printk(KERN_ERR "iscsi: invalid ping timeout of zero "
+ "Using 5 seconds.\n");
+ conn->ping_timeout = 5;
+ }
+
spin_lock_bh(&session->lock);
conn->c_stage = ISCSI_CONN_STARTED;
session->state = ISCSI_STATE_LOGGED_IN;
session->queued_cmdsn = session->cmdsn;
+ conn->last_recv = jiffies;
+ conn->last_ping = jiffies;
+ if (conn->recv_timeout && conn->ping_timeout)
+ mod_timer(&conn->transport_timer,
+ jiffies + (conn->recv_timeout * HZ));
+
switch(conn->stop_stage) {
case STOP_CONN_RECOVER:
/*
@@ -1842,6 +2015,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
{
int old_stop_stage;
+ del_timer_sync(&conn->transport_timer);
+
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
if (conn->stop_stage == STOP_CONN_TERM) {
@@ -1956,6 +2131,18 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
case ISCSI_PARAM_FAST_ABORT:
sscanf(buf, "%d", &session->fast_abort);
break;
+ case ISCSI_PARAM_ABORT_TMO:
+ sscanf(buf, "%d", &session->abort_timeout);
+ break;
+ case ISCSI_PARAM_LU_RESET_TMO:
+ sscanf(buf, "%d", &session->lu_reset_timeout);
+ break;
+ case ISCSI_PARAM_PING_TMO:
+ sscanf(buf, "%d", &conn->ping_timeout);
+ break;
+ case ISCSI_PARAM_RECV_TMO:
+ sscanf(buf, "%d", &conn->recv_timeout);
+ break;
case ISCSI_PARAM_MAX_RECV_DLENGTH:
sscanf(buf, "%d", &conn->max_recv_dlength);
break;
@@ -2073,6 +2260,12 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
case ISCSI_PARAM_FAST_ABORT:
len = sprintf(buf, "%d\n", session->fast_abort);
break;
+ case ISCSI_PARAM_ABORT_TMO:
+ len = sprintf(buf, "%d\n", session->abort_timeout);
+ break;
+ case ISCSI_PARAM_LU_RESET_TMO:
+ len = sprintf(buf, "%d\n", session->lu_reset_timeout);
+ break;
case ISCSI_PARAM_INITIAL_R2T_EN:
len = sprintf(buf, "%d\n", session->initial_r2t_en);
break;
@@ -2130,6 +2323,12 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
int len;
switch(param) {
+ case ISCSI_PARAM_PING_TMO:
+ len = sprintf(buf, "%u\n", conn->ping_timeout);
+ break;
+ case ISCSI_PARAM_RECV_TMO:
+ len = sprintf(buf, "%u\n", conn->recv_timeout);
+ break;
case ISCSI_PARAM_MAX_RECV_DLENGTH:
len = sprintf(buf, "%u\n", conn->max_recv_dlength);
break;
diff --git a/kernel/libiscsi.h b/kernel/libiscsi.h
index b4a082f..d02c55f 100644
--- a/kernel/libiscsi.h
+++ b/kernel/libiscsi.h
@@ -133,6 +133,12 @@ struct iscsi_conn {
* conn_stop() flag: stop to recover, stop to terminate
*/
int stop_stage;
+ struct timer_list transport_timer;
+ unsigned long last_recv;
+ unsigned long last_ping;
+ int ping_timeout;
+ int recv_timeout;
+ struct iscsi_mgmt_task *ping_mtask;
/* iSCSI connection-wide sequencing */
uint32_t exp_statsn;
@@ -223,6 +229,8 @@ struct iscsi_session {
uint32_t queued_cmdsn;
/* configuration */
+ int abort_timeout;
+ int lu_reset_timeout;
int initial_r2t_en;
unsigned max_r2t;
int imm_data_en;
diff --git a/kernel/scsi_transport_iscsi.c b/kernel/scsi_transport_iscsi.c
index 078ea9b..56ba5c2 100644
--- a/kernel/scsi_transport_iscsi.c
+++ b/kernel/scsi_transport_iscsi.c
@@ -1211,6 +1211,8 @@ iscsi_conn_attr(port, ISCSI_PARAM_CONN_PORT);
iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN);
iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS);
+iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
+iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
#define iscsi_cdev_to_session(_cdev) \
iscsi_dev_to_session(_cdev->dev)
@@ -1446,6 +1448,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
SETUP_CONN_RD_ATTR(exp_statsn, ISCSI_EXP_STATSN);
SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS);
SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT);
+ SETUP_CONN_RD_ATTR(ping_tmo, ISCSI_PING_TMO);
+ SETUP_CONN_RD_ATTR(recv_tmo, ISCSI_RECV_TMO);
BUG_ON(count > ISCSI_CONN_ATTRS);
priv->conn_attrs[count] = NULL;
diff --git a/usr/config.h b/usr/config.h
index 864aee7..e767d6d 100644
--- a/usr/config.h
+++ b/usr/config.h
@@ -66,8 +66,6 @@ struct iscsi_connection_timeout_config {
int logout_timeout;
int auth_timeout;
int active_timeout;
- int idle_timeout;
- int ping_timeout;
int noop_out_interval;
int noop_out_timeout;
};
@@ -86,7 +84,8 @@ struct iscsi_session_timeout_config {
*/
struct iscsi_error_timeout_config {
int abort_timeout;
- int reset_timeout;
+ int host_reset_timeout;
+ int lu_reset_timeout;
};
/* all TCP options go in this structure.
diff --git a/usr/discovery.c b/usr/discovery.c
index a10b0ea..6e010bd 100644
--- a/usr/discovery.c
+++ b/usr/discovery.c
@@ -548,7 +548,6 @@ init_new_session(struct iscsi_sendtargets_config *config)
session->conn[0].login_timeout = config->conn_timeo.login_timeout;
session->conn[0].auth_timeout = config->conn_timeo.auth_timeout;
session->conn[0].active_timeout = config->conn_timeo.active_timeout;
- session->conn[0].idle_timeout = config->conn_timeo.idle_timeout;
session->conn[0].hdrdgst_en = ISCSI_DIGEST_NONE;
session->conn[0].datadgst_en = ISCSI_DIGEST_NONE;
@@ -879,11 +878,9 @@ int discovery_sendtargets(idbm_t *db, discovery_rec_t *drec,
goto free_sendtargets;
}
- log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d, "
- "active %d, idle %d, ping %d",
+ log_debug(4, "discovery timeouts: login %d, reopen_cnt %d, auth %d.",
session->conn[0].login_timeout, session->reopen_cnt,
- session->conn[0].auth_timeout, session->conn[0].active_timeout,
- session->conn[0].idle_timeout, session->conn[0].ping_timeout);
+ session->conn[0].auth_timeout);
/* setup authentication variables for the session*/
rc = setup_authentication(session, drec, config);
diff --git a/usr/idbm.c b/usr/idbm.c
index ed9dc1e..4d148ba 100644
--- a/usr/idbm.c
+++ b/usr/idbm.c
@@ -230,11 +230,8 @@ idbm_recinfo_discovery(discovery_rec_t *r, recinfo_t *ri)
u.sendtargets.conn_timeo.auth_timeout,
IDBM_SHOW, num);
__recinfo_int("discovery.sendtargets.timeo.active_timeout",ri,r,
- u.sendtargets.conn_timeo.active_timeout,
- IDBM_SHOW, num);
- __recinfo_int("discovery.sendtargets.timeo.idle_timeout", ri, r,
- u.sendtargets.conn_timeo.idle_timeout,
- IDBM_SHOW, num);
+ u.sendtargets.conn_timeo.active_timeout,
+ IDBM_SHOW, num);
__recinfo_int("discovery.sendtargets.iscsi.MaxRecvDataSegmentLength",
ri, r, u.sendtargets.iscsi.MaxRecvDataSegmentLength,
IDBM_SHOW, num);
@@ -298,8 +295,11 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
__recinfo_int("node.session.err_timeo.abort_timeout", ri, r,
session.err_timeo.abort_timeout,
IDBM_SHOW, num);
- __recinfo_int("node.session.err_timeo.reset_timeout", ri, r,
- session.err_timeo.reset_timeout,
+ __recinfo_int("node.session.err_timeo.lu_reset_timeout", ri, r,
+ session.err_timeo.lu_reset_timeout,
+ IDBM_SHOW, num);
+ __recinfo_int("node.session.err_timeo.host_reset_timeout", ri, r,
+ session.err_timeo.host_reset_timeout,
IDBM_SHOW, num);
__recinfo_int_o2("node.session.iscsi.FastAbort", ri, r,
session.iscsi.FastAbort, IDBM_SHOW, "No", "Yes", num);
@@ -349,15 +349,6 @@ idbm_recinfo_node(node_rec_t *r, recinfo_t *ri)
sprintf(key, "node.conn[%d].timeo.auth_timeout", i);
__recinfo_int(key, ri, r, conn[i].timeo.auth_timeout,
IDBM_SHOW, num);
- sprintf(key, "node.conn[%d].timeo.active_timeout", i);
- __recinfo_int(key, ri, r, conn[i].timeo.active_timeout,
- IDBM_SHOW, num);
- sprintf(key, "node.conn[%d].timeo.idle_timeout", i);
- __recinfo_int(key, ri, r, conn[i].timeo.idle_timeout,
- IDBM_SHOW, num);
- sprintf(key, "node.conn[%d].timeo.ping_timeout", i);
- __recinfo_int(key, ri, r, conn[i].timeo.ping_timeout,
- IDBM_SHOW, num);
sprintf(key, "node.conn[%d].timeo.noop_out_interval", i);
__recinfo_int(key, ri, r, conn[i].timeo.noop_out_interval,
@@ -474,7 +465,6 @@ idbm_discovery_setup_defaults(discovery_rec_t *rec, discovery_type_e type)
rec->u.sendtargets.conn_timeo.login_timeout=15;
rec->u.sendtargets.conn_timeo.auth_timeout = 45;
rec->u.sendtargets.conn_timeo.active_timeout=30;
- rec->u.sendtargets.conn_timeo.idle_timeout = 60;
rec->u.sendtargets.iscsi.MaxRecvDataSegmentLength =
DEF_INI_DISC_MAX_RECV_SEG_LEN;
} else if (type == DISCOVERY_TYPE_SLP) {
diff --git a/usr/initiator.c b/usr/initiator.c
index 70ac050..84a0f86 100644
--- a/usr/initiator.c
+++ b/usr/initiator.c
@@ -431,6 +431,8 @@ __session_conn_create(iscsi_session_t *session, int cid)
conn->login_timeout = DEF_LOGIN_TIMEO;
}
+ conn->auth_timeout = conn_rec->timeo.auth_timeout;
+
/* noop-out setting */
conn->noop_out_interval = conn_rec->timeo.noop_out_interval;
conn->noop_out_timeout = conn_rec->timeo.noop_out_timeout;
@@ -445,18 +447,9 @@ __session_conn_create(iscsi_session_t *session, int cid)
log_error("Invalid timeo.noop_out_interval. Must be greater "
"than zero. Using default %d.\n",
DEF_NOOP_OUT_INTERVAL);
- conn->noop_out_timeout = DEF_NOOP_OUT_INTERVAL;
+ conn->noop_out_interval = DEF_NOOP_OUT_INTERVAL;
}
- /*
- * currently not used (leftover from linux-iscsi which we
- * may do one day)
- */
- conn->auth_timeout = conn_rec->timeo.auth_timeout;
- conn->active_timeout = conn_rec->timeo.active_timeout;
- conn->idle_timeout = conn_rec->timeo.idle_timeout;
- conn->ping_timeout = conn_rec->timeo.ping_timeout;
-
iscsi_copy_operational_params(conn);
/* TCP options */
@@ -1084,7 +1077,7 @@ mgmt_ipc_err_e iscsi_host_set_param(int host_no, int param, char *value)
return MGMT_IPC_OK;
}
-#define MAX_SESSION_PARAMS 25
+#define MAX_SESSION_PARAMS 30
#define MAX_HOST_PARAMS 3
static void
@@ -1245,13 +1238,32 @@ setup_full_feature_phase(iscsi_conn_t *conn)
.value = &session->fast_abort,
.type = ISCSI_INT,
.conn_only = 0,
+ }, {
+ .param = ISCSI_PARAM_ABORT_TMO,
+ .value = &session->abort_timeout,
+ .type = ISCSI_INT,
+ .conn_only = 0,
+ }, {
+ .param = ISCSI_PARAM_LU_RESET_TMO,
+ .value = &session->lu_reset_timeout,
+ .type = ISCSI_INT,
+ .conn_only = 0,
+ }, {
+ .param = ISCSI_PARAM_HOST_RESET_TMO,
+ .value = &session->host_reset_timeout,
+ .type = ISCSI_INT,
+ .conn_only = 0,
+ }, {
+ .param = ISCSI_PARAM_PING_TMO,
+ .value = &conn->noop_out_timeout,
+ .type = ISCSI_INT,
+ .conn_only = 1,
+ }, {
+ .param = ISCSI_PARAM_RECV_TMO,
+ .value = &conn->noop_out_interval,
+ .type = ISCSI_INT,
+ .conn_only = 1,
},
- /*
- * FIXME: set these timeouts via set_param() API
- *
- * rec->session.timeo
- * rec->session.err_timeo
- */
};
/* almost! entered full-feature phase */
@@ -1304,6 +1316,11 @@ setup_full_feature_phase(iscsi_conn_t *conn)
return;
}
+ /* older kernels may not support nop handling in kernel */
+ if (rc == -ENOSYS &&
+ conntbl[i].param == ISCSI_PARAM_PING_TMO)
+ conn->userspace_nop = 1;
+
print_param_value(conntbl[i].param, conntbl[i].value,
conntbl[i].type);
}
@@ -1358,7 +1375,7 @@ setup_full_feature_phase(iscsi_conn_t *conn)
session->r_stage = R_STAGE_NO_CHANGE;
/* noop_out */
- if (conn->noop_out_interval) {
+ if (conn->userspace_nop && conn->noop_out_interval) {
actor_timer(&conn->nop_out_timer, conn->noop_out_interval*1000,
conn_send_nop_out, conn);
log_debug(3, "noop out timer %p start\n",
@@ -1455,6 +1472,11 @@ static void iscsi_logout(void *data)
static void iscsi_recv_nop_in(iscsi_conn_t *conn, struct iscsi_hdr *hdr)
{
+ if (!conn->userspace_nop) {
+ log_error("Got nop in, but kernel supports nop handling.");
+ return;
+ }
+
if (hdr->ttt == ISCSI_RESERVED_TAG) {
/* noop out rsp */
actor_delete(&conn->nop_out_timer);
diff --git a/usr/initiator.h b/usr/initiator.h
index 9bfc43d..968ee13 100644
--- a/usr/initiator.h
+++ b/usr/initiator.h
@@ -107,6 +107,7 @@ typedef struct iscsi_conn {
char data[ISCSI_DEF_MAX_RECV_SEG_LEN];
char host[NI_MAXHOST]; /* scratch */
iscsi_conn_state_e state;
+ int userspace_nop;
actor_t login_timer;
actor_t nop_out_timer;
@@ -139,8 +140,6 @@ typedef struct iscsi_conn {
int logout_timeout;
int auth_timeout;
int active_timeout;
- int idle_timeout;
- int ping_timeout;
int noop_out_interval;
int noop_out_timeout;
@@ -233,7 +232,9 @@ typedef struct iscsi_session {
iscsi_session_r_stage_e r_stage;
uint32_t replacement_timeout;
- int unbinding;
+ int host_reset_timeout;
+ int lu_reset_timeout;
+ int abort_timeout;
/* sync up fields */
queue_task_t *sync_qtask;
diff --git a/usr/iscsi_settings.h b/usr/iscsi_settings.h
index 4bb7064..107a4f7 100644
--- a/usr/iscsi_settings.h
+++ b/usr/iscsi_settings.h
@@ -9,6 +9,10 @@
#define DEF_NOOP_OUT_TIMEO 15
#define DEF_REPLACEMENT_TIMEO 120
+#define DEF_ABORT_TIMEO 15
+#define DEF_LU_RESET_TIMEO 30
+#define DEF_HOST_RESET_TIMEO 60
+
/* q depths */
#define CMDS_MAX 128
#define QUEUE_DEPTH 32
diff --git a/usr/netlink.c b/usr/netlink.c
index 5eabccf..9f21bc9 100644
--- a/usr/netlink.c
+++ b/usr/netlink.c
@@ -868,7 +868,6 @@ static int ctldev_handle(void)
{
int rc, ev_size;
struct iscsi_uevent *ev;
- struct iscsi_transport *t;
iscsi_session_t *session = NULL;
iscsi_conn_t *conn = NULL;
char nlm_ev[NLMSG_SPACE(sizeof(struct iscsi_uevent))];
diff --git a/usr/util.c b/usr/util.c
index 29811e3..ff98b0a 100644
--- a/usr/util.c
+++ b/usr/util.c
@@ -190,8 +190,9 @@ void idbm_node_setup_defaults(node_rec_t *rec)
rec->session.auth.authmethod = 0;
rec->session.auth.password_length = 0;
rec->session.auth.password_in_length = 0;
- rec->session.err_timeo.abort_timeout = 10;
- rec->session.err_timeo.reset_timeout = 30;
+ rec->session.err_timeo.abort_timeout = DEF_ABORT_TIMEO;
+ rec->session.err_timeo.lu_reset_timeout = DEF_LU_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;
rec->session.iscsi.ImmediateData = 1;
@@ -202,6 +203,7 @@ void idbm_node_setup_defaults(node_rec_t *rec)
rec->session.iscsi.MaxConnections = 1;
rec->session.iscsi.MaxOutstandingR2T = 1;
rec->session.iscsi.ERL = 0;
+ rec->session.iscsi.FastAbort = 1;
for (i=0; i<ISCSI_CONN_MAX; i++) {
rec->conn[i].startup = 0;
@@ -211,9 +213,6 @@ void idbm_node_setup_defaults(node_rec_t *rec)
rec->conn[i].timeo.login_timeout= DEF_LOGIN_TIMEO;
rec->conn[i].timeo.logout_timeout= DEF_LOGOUT_TIMEO;
rec->conn[i].timeo.auth_timeout = 45;
- rec->conn[i].timeo.active_timeout=5;
- rec->conn[i].timeo.idle_timeout = 60;
- rec->conn[i].timeo.ping_timeout = 5;
rec->conn[i].timeo.noop_out_interval = DEF_NOOP_OUT_INTERVAL;
rec->conn[i].timeo.noop_out_timeout = DEF_NOOP_OUT_TIMEO;