summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;