aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/iscsi_tcp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/iscsi_tcp.c')
-rw-r--r--drivers/scsi/iscsi_tcp.c659
1 files changed, 395 insertions, 264 deletions
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 6e510f3cfbf67..aca3e23b1b9eb 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -82,6 +82,9 @@ module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
/* global data */
static kmem_cache_t *taskcache;
+#define session_to_cls(_sess) \
+ hostdata_session(_sess->host->hostdata)
+
static inline void
iscsi_buf_init_virt(struct iscsi_buf *ibuf, char *vbuf, int size)
{
@@ -230,17 +233,19 @@ iscsi_hdr_extract(struct iscsi_conn *conn)
return 0;
}
-static inline void
-iscsi_ctask_cleanup(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+/*
+ * must be called with session lock
+ */
+static void
+__iscsi_ctask_cleanup(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
{
- struct scsi_cmnd *sc = ctask->sc;
struct iscsi_session *session = conn->session;
+ struct scsi_cmnd *sc;
- spin_lock(&session->lock);
- if (unlikely(!sc)) {
- spin_unlock(&session->lock);
+ sc = ctask->sc;
+ if (unlikely(!sc))
return;
- }
+
if (sc->sc_data_direction == DMA_TO_DEVICE) {
struct iscsi_data_task *dtask, *n;
/* WRITE: cleanup Data-Out's if any */
@@ -252,7 +257,20 @@ iscsi_ctask_cleanup(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
ctask->xmstate = XMSTATE_IDLE;
ctask->r2t = NULL;
ctask->sc = NULL;
+ list_del(&ctask->running);
+
__kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
+
+ sc->scsi_done(sc);
+}
+
+static void
+iscsi_ctask_cleanup(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_session *session = conn->session;
+
+ spin_lock(&session->lock);
+ __iscsi_ctask_cleanup(conn, ctask);
spin_unlock(&session->lock);
}
@@ -311,7 +329,6 @@ out:
(long)sc, sc->result, ctask->itt);
conn->scsirsp_pdus_cnt++;
iscsi_ctask_cleanup(conn, ctask);
- sc->scsi_done(sc);
return rc;
}
@@ -395,6 +412,7 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
dtask = mempool_alloc(ctask->datapool, GFP_ATOMIC);
BUG_ON(!dtask);
+ INIT_LIST_HEAD(&dtask->item);
hdr = &dtask->hdr;
memset(hdr, 0, sizeof(struct iscsi_data));
hdr->ttt = r2t->ttt;
@@ -710,8 +728,6 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
conn->tmfrsp_pdus_cnt++;
spin_lock(&session->lock);
if (conn->tmabort_state == TMABORT_INITIAL) {
- __kfifo_put(session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*));
conn->tmabort_state =
((struct iscsi_tm_rsp *)hdr)->
response == ISCSI_TMF_RSP_COMPLETE ?
@@ -986,7 +1002,6 @@ done:
(long)sc, sc->result, ctask->itt);
conn->scsirsp_pdus_cnt++;
iscsi_ctask_cleanup(conn, ctask);
- sc->scsi_done(sc);
}
return rc;
@@ -1460,6 +1475,7 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
dtask = mempool_alloc(ctask->datapool, GFP_ATOMIC);
BUG_ON(!dtask);
+ INIT_LIST_HEAD(&dtask->item);
hdr = &dtask->hdr;
memset(hdr, 0, sizeof(struct iscsi_data));
hdr->ttt = r2t->ttt;
@@ -1506,6 +1522,7 @@ iscsi_unsolicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
dtask = mempool_alloc(ctask->datapool, GFP_ATOMIC);
BUG_ON(!dtask);
+ INIT_LIST_HEAD(&dtask->item);
hdr = &dtask->hdr;
memset(hdr, 0, sizeof(struct iscsi_data));
hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
@@ -1550,6 +1567,7 @@ iscsi_cmd_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
BUG_ON(__kfifo_len(ctask->r2tqueue));
+ INIT_LIST_HEAD(&ctask->running);
ctask->sc = sc;
ctask->conn = conn;
ctask->hdr.opcode = ISCSI_OP_SCSI_CMD;
@@ -1680,7 +1698,7 @@ iscsi_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
if (mtask->data_count)
mtask->xmstate |= XMSTATE_IMM_DATA;
if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE &&
- conn->stop_stage != STOP_CONN_RECOVER &&
+ conn->stop_stage != STOP_CONN_RECOVER &&
conn->hdrdgst_en)
iscsi_hdr_digest(conn, &mtask->headbuf,
(u8*)mtask->hdrext);
@@ -2226,6 +2244,7 @@ iscsi_data_xmit(struct iscsi_conn *conn)
/* process command queue */
while (__kfifo_get(conn->xmitqueue, (void*)&conn->ctask,
sizeof(void*))) {
+ list_add_tail(&conn->ctask->running, &conn->run_list);
if (iscsi_ctask_xmit(conn, conn->ctask))
goto again;
}
@@ -2277,11 +2296,14 @@ iscsi_xmitworker(void *data)
mutex_unlock(&conn->xmitmutex);
}
-#define FAILURE_BAD_HOST 1
-#define FAILURE_SESSION_FAILED 2
-#define FAILURE_SESSION_FREED 3
-#define FAILURE_WINDOW_CLOSED 4
-#define FAILURE_SESSION_TERMINATE 5
+enum {
+ FAILURE_BAD_HOST = 1,
+ FAILURE_SESSION_FAILED,
+ FAILURE_SESSION_FREED,
+ FAILURE_WINDOW_CLOSED,
+ FAILURE_SESSION_TERMINATE,
+ FAILURE_SESSION_RECOVERY_TIMEOUT,
+};
static int
iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
@@ -2297,12 +2319,14 @@ iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
host = sc->device->host;
session = iscsi_hostdata(host->hostdata);
- BUG_ON(host != session->host);
spin_lock(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN) {
- if (session->state == ISCSI_STATE_FAILED) {
+ if (session->recovery_failed) {
+ reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+ goto fault;
+ } else if (session->state == ISCSI_STATE_FAILED) {
reason = FAILURE_SESSION_FAILED;
goto reject;
} else if (session->state == ISCSI_STATE_TERMINATE) {
@@ -2350,11 +2374,6 @@ fault:
spin_unlock(&session->lock);
printk(KERN_ERR "iscsi_tcp: cmd 0x%x is not queued (%d)\n",
sc->cmnd[0], reason);
- sc->sense_buffer[0] = 0x70;
- sc->sense_buffer[2] = NOT_READY;
- sc->sense_buffer[7] = 0x6;
- sc->sense_buffer[12] = 0x08;
- sc->sense_buffer[13] = 0x00;
sc->result = (DID_NO_CONNECT << 16);
sc->resid = sc->request_bufflen;
sc->scsi_done(sc);
@@ -2445,6 +2464,7 @@ iscsi_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
conn->id = conn_idx;
conn->exp_statsn = 0;
conn->tmabort_state = TMABORT_INITIAL;
+ INIT_LIST_HEAD(&conn->run_list);
/* initial operational parameters */
conn->hdr_size = sizeof(struct iscsi_hdr);
@@ -2701,6 +2721,22 @@ iscsi_conn_bind(struct iscsi_cls_session *cls_session,
return 0;
}
+static void
+iscsi_session_recovery_timedout(struct iscsi_cls_session *csession)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(csession);
+ struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct iscsi_conn *conn = session->leadconn;
+
+ spin_lock_bh(&session->lock);
+ if (session->state != ISCSI_STATE_LOGGED_IN) {
+ session->recovery_failed = 1;
+ if (conn)
+ wake_up(&conn->ehwait);
+ }
+ spin_unlock_bh(&session->lock);
+}
+
static int
iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
{
@@ -2716,7 +2752,6 @@ iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
}
sk = conn->sock->sk;
-
write_lock_bh(&sk->sk_callback_lock);
spin_lock_bh(&session->lock);
conn->c_stage = ISCSI_CONN_STARTED;
@@ -2732,8 +2767,13 @@ iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
conn->stop_stage = 0;
conn->tmabort_state = TMABORT_INITIAL;
session->age++;
+ session->recovery_failed = 0;
+ spin_unlock_bh(&session->lock);
+ write_unlock_bh(&sk->sk_callback_lock);
+
+ iscsi_unblock_session(session_to_cls(session));
wake_up(&conn->ehwait);
- break;
+ return 0;
case STOP_CONN_TERM:
session->conn_cnt++;
conn->stop_stage = 0;
@@ -2752,105 +2792,199 @@ iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
return 0;
}
-static void
-iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+static int
+iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
{
- struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_conn *conn = ctask->conn;
struct iscsi_session *session = conn->session;
+ struct iscsi_mgmt_task *mtask;
+
+ if (!ctask->mtask)
+ return -EINVAL;
+
+ while (__kfifo_get(conn->immqueue, (void*)&mtask, sizeof(void*))) {
+ if (mtask == ctask->mtask)
+ break;
+ __kfifo_put(conn->immqueue, (void*)&mtask, sizeof(void*));
+ }
+
+ __kfifo_put(session->mgmtpool.queue, (void*)&ctask->mtask,
+ sizeof(void*));
+ ctask->mtask = NULL;
+ return 0;
+}
+
+static void
+fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask, int err)
+{
+ struct iscsi_r2t_info *r2t;
+ struct scsi_cmnd *sc;
+
+ /* flush ctask's r2t queues */
+ while (__kfifo_get(ctask->r2tqueue, (void*)&r2t, sizeof(void*)))
+ __kfifo_put(ctask->r2tpool.queue, (void*)&r2t, sizeof(void*));
+
+ iscsi_ctask_mtask_cleanup(ctask);
+
+ sc = ctask->sc;
+ if (!sc)
+ return;
+
+ sc->result = err;
+ sc->resid = sc->request_bufflen;
+
+ __iscsi_ctask_cleanup(conn, ctask);
+}
+
+/* Fail commands. Mutex and session lock held */
+static void
+fail_all_commands(struct iscsi_session *session, struct iscsi_conn *conn)
+{
+ struct iscsi_cmd_task *ctask, *tmp;
+
+ /* flush pending */
+ while (__kfifo_get(conn->xmitqueue, (void*)&ctask, sizeof(void*))) {
+ debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc,
+ ctask->itt);
+ fail_command(conn, ctask, DID_BUS_BUSY << 16);
+ }
+
+ /* fail running */
+ list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) {
+ debug_scsi("failing in progress sc %p itt 0x%x\n",
+ ctask->sc, ctask->itt);
+ fail_command(conn, ctask, DID_BUS_BUSY << 16);
+ }
+
+ conn->ctask = NULL;
+}
+
+static void
+flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
+{
+ struct iscsi_mgmt_task *mtask;
+
+ /* TODO: handle running pdus */
+ while (__kfifo_get(conn->immqueue, (void*)&mtask, sizeof(void*)) ||
+ __kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) {
+ if (mtask == conn->login_mtask)
+ continue;
+ __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
+ sizeof(void*));
+ }
+
+ if (conn->mtask && conn->mtask != conn->login_mtask)
+ __kfifo_put(session->mgmtpool.queue, (void*)&conn->mtask,
+ sizeof(void*));
+ conn->mtask = NULL;
+}
+
+static void
+iscsi_suspend_conn_rx(struct iscsi_conn *conn)
+{
struct sock *sk;
- unsigned long flags;
BUG_ON(!conn->sock);
sk = conn->sock->sk;
write_lock_bh(&sk->sk_callback_lock);
set_bit(SUSPEND_BIT, &conn->suspend_rx);
write_unlock_bh(&sk->sk_callback_lock);
+}
- mutex_lock(&conn->xmitmutex);
-
- spin_lock_irqsave(session->host->host_lock, flags);
- spin_lock(&session->lock);
+static void
+iscsi_start_session_recovery(struct iscsi_session *session,
+ struct iscsi_conn *conn, int flag)
+{
+ spin_lock_bh(&session->lock);
+ if (conn->stop_stage == STOP_CONN_RECOVER ||
+ conn->stop_stage == STOP_CONN_TERM) {
+ spin_unlock_bh(&session->lock);
+ return;
+ }
conn->stop_stage = flag;
+ spin_unlock_bh(&session->lock);
+
+ iscsi_suspend_conn_rx(conn);
+
+ mutex_lock(&conn->xmitmutex);
+ spin_lock_bh(&session->lock);
conn->c_stage = ISCSI_CONN_STOPPED;
set_bit(SUSPEND_BIT, &conn->suspend_tx);
- if (flag != STOP_CONN_SUSPEND)
- session->conn_cnt--;
-
+ session->conn_cnt--;
if (session->conn_cnt == 0 || session->leadconn == conn)
session->state = ISCSI_STATE_FAILED;
- spin_unlock(&session->lock);
- spin_unlock_irqrestore(session->host->host_lock, flags);
-
- if (flag == STOP_CONN_TERM || flag == STOP_CONN_RECOVER) {
- struct iscsi_cmd_task *ctask;
- struct iscsi_mgmt_task *mtask;
+ spin_unlock_bh(&session->lock);
- /*
- * Socket must go now.
- */
- sock_hold(conn->sock->sk);
- iscsi_conn_restore_callbacks(conn);
- sock_put(conn->sock->sk);
+ /*
+ * Socket must go now.
+ */
+ sock_hold(conn->sock->sk);
+ iscsi_conn_restore_callbacks(conn);
+ sock_put(conn->sock->sk);
- /*
- * flush xmit queues.
- */
- spin_lock_bh(&session->lock);
- while (__kfifo_get(conn->writequeue, (void*)&ctask,
- sizeof(void*)) ||
- __kfifo_get(conn->xmitqueue, (void*)&ctask,
- sizeof(void*))) {
- struct iscsi_r2t_info *r2t;
-
- /*
- * flush ctask's r2t queues
- */
- while (__kfifo_get(ctask->r2tqueue, (void*)&r2t,
- sizeof(void*)))
- __kfifo_put(ctask->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
+ /*
+ * flush queues.
+ */
+ spin_lock_bh(&session->lock);
+ fail_all_commands(session, conn);
+ flush_control_queues(session, conn);
+ spin_unlock_bh(&session->lock);
- spin_unlock_bh(&session->lock);
- local_bh_disable();
- iscsi_ctask_cleanup(conn, ctask);
- local_bh_enable();
- spin_lock_bh(&session->lock);
- }
- conn->ctask = NULL;
- while (__kfifo_get(conn->immqueue, (void*)&mtask,
- sizeof(void*)) ||
- __kfifo_get(conn->mgmtqueue, (void*)&mtask,
- sizeof(void*))) {
- __kfifo_put(session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*));
- }
- conn->mtask = NULL;
- spin_unlock_bh(&session->lock);
+ /*
+ * release socket only after we stopped data_xmit()
+ * activity and flushed all outstandings
+ */
+ sock_release(conn->sock);
+ conn->sock = NULL;
- /*
- * release socket only after we stopped data_xmit()
- * activity and flushed all outstandings
- */
- sock_release(conn->sock);
- conn->sock = NULL;
+ /*
+ * for connection level recovery we should not calculate
+ * header digest. conn->hdr_size used for optimization
+ * in hdr_extract() and will be re-negotiated at
+ * set_param() time.
+ */
+ if (flag == STOP_CONN_RECOVER) {
+ conn->hdr_size = sizeof(struct iscsi_hdr);
+ conn->hdrdgst_en = 0;
+ conn->datadgst_en = 0;
- /*
- * for connection level recovery we should not calculate
- * header digest. conn->hdr_size used for optimization
- * in hdr_extract() and will be re-negotiated at
- * set_param() time.
- */
- if (flag == STOP_CONN_RECOVER) {
- conn->hdr_size = sizeof(struct iscsi_hdr);
- conn->hdrdgst_en = 0;
- conn->datadgst_en = 0;
- }
+ if (session->state == ISCSI_STATE_FAILED)
+ iscsi_block_session(session_to_cls(session));
}
mutex_unlock(&conn->xmitmutex);
}
+static void
+iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+
+ switch (flag) {
+ case STOP_CONN_RECOVER:
+ case STOP_CONN_TERM:
+ iscsi_start_session_recovery(session, conn, flag);
+ return;
+ case STOP_CONN_SUSPEND:
+ iscsi_suspend_conn_rx(conn);
+
+ mutex_lock(&conn->xmitmutex);
+ spin_lock_bh(&session->lock);
+
+ conn->stop_stage = flag;
+ conn->c_stage = ISCSI_CONN_STOPPED;
+ set_bit(SUSPEND_BIT, &conn->suspend_tx);
+
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&conn->xmitmutex);
+ break;
+ default:
+ printk(KERN_ERR "invalid stop flag %d\n", flag);
+ }
+}
+
static int
iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, uint32_t data_size)
@@ -2940,23 +3074,54 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
static int
iscsi_eh_host_reset(struct scsi_cmnd *sc)
{
- struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
- struct iscsi_conn *conn = ctask->conn;
- struct iscsi_session *session = conn->session;
+ struct Scsi_Host *host = sc->device->host;
+ struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+ struct iscsi_conn *conn = session->leadconn;
+ int fail_session = 0;
spin_lock_bh(&session->lock);
if (session->state == ISCSI_STATE_TERMINATE) {
+failed:
debug_scsi("failing host reset: session terminated "
"[CID %d age %d]", conn->id, session->age);
spin_unlock_bh(&session->lock);
return FAILED;
}
+
+ if (sc->SCp.phase == session->age) {
+ debug_scsi("failing connection CID %d due to SCSI host reset",
+ conn->id);
+ fail_session = 1;
+ }
spin_unlock_bh(&session->lock);
- debug_scsi("failing connection CID %d due to SCSI host reset "
- "[itt 0x%x age %d]", conn->id, ctask->itt,
- session->age);
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ /*
+ * we drop the lock here but the leadconn cannot be destoyed while
+ * we are in the scsi eh
+ */
+ if (fail_session) {
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ /*
+ * if userspace cannot respond then we must kick this off
+ * here for it
+ */
+ iscsi_start_session_recovery(session, conn, STOP_CONN_RECOVER);
+ }
+
+ debug_scsi("iscsi_eh_host_reset wait for relogin\n");
+ wait_event_interruptible(conn->ehwait,
+ session->state == ISCSI_STATE_TERMINATE ||
+ session->state == ISCSI_STATE_LOGGED_IN ||
+ session->recovery_failed);
+ if (signal_pending(current))
+ flush_signals(current);
+
+ spin_lock_bh(&session->lock);
+ if (session->state == ISCSI_STATE_LOGGED_IN)
+ printk(KERN_INFO "host reset succeeded\n");
+ else
+ goto failed;
+ spin_unlock_bh(&session->lock);
return SUCCESS;
}
@@ -2970,8 +3135,6 @@ iscsi_tmabort_timedout(unsigned long data)
spin_lock(&session->lock);
if (conn->tmabort_state == TMABORT_INITIAL) {
- __kfifo_put(session->mgmtpool.queue,
- (void*)&ctask->mtask, sizeof(void*));
conn->tmabort_state = TMABORT_TIMEDOUT;
debug_scsi("tmabort timedout [sc %lx itt 0x%x]\n",
(long)ctask->sc, ctask->itt);
@@ -2981,186 +3144,163 @@ iscsi_tmabort_timedout(unsigned long data)
spin_unlock(&session->lock);
}
+/* must be called with the mutex lock */
static int
-iscsi_eh_abort(struct scsi_cmnd *sc)
+iscsi_exec_abort_task(struct scsi_cmnd *sc, struct iscsi_cmd_task *ctask)
{
- int rc;
- struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
struct iscsi_conn *conn = ctask->conn;
struct iscsi_session *session = conn->session;
-
- conn->eh_abort_cnt++;
- debug_scsi("aborting [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+ struct iscsi_tm *hdr = &conn->tmhdr;
+ int rc;
/*
- * two cases for ERL=0 here:
- *
- * 1) connection-level failure;
- * 2) recovery due protocol error;
+ * ctask timed out but session is OK requests must be serialized.
*/
- mutex_lock(&conn->xmitmutex);
- spin_lock_bh(&session->lock);
- if (session->state != ISCSI_STATE_LOGGED_IN) {
- if (session->state == ISCSI_STATE_TERMINATE) {
- spin_unlock_bh(&session->lock);
- mutex_unlock(&conn->xmitmutex);
- goto failed;
- }
- spin_unlock_bh(&session->lock);
- } else {
- struct iscsi_tm *hdr = &conn->tmhdr;
+ memset(hdr, 0, sizeof(struct iscsi_tm));
+ hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
+ hdr->flags = ISCSI_TM_FUNC_ABORT_TASK;
+ hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+ memcpy(hdr->lun, ctask->hdr.lun, sizeof(hdr->lun));
+ hdr->rtt = ctask->hdr.itt;
+ hdr->refcmdsn = ctask->hdr.cmdsn;
- /*
- * Still LOGGED_IN...
- */
+ rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr,
+ NULL, 0);
+ if (rc) {
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ debug_scsi("abort sent failure [itt 0x%x] %d", ctask->itt, rc);
+ return rc;
+ }
- if (!ctask->sc || sc->SCp.phase != session->age) {
- /*
- * 1) ctask completed before time out. But session
- * is still ok => Happy Retry.
- * 2) session was re-open during time out of ctask.
- */
- spin_unlock_bh(&session->lock);
- mutex_unlock(&conn->xmitmutex);
- goto success;
- }
- conn->tmabort_state = TMABORT_INITIAL;
- spin_unlock_bh(&session->lock);
+ debug_scsi("abort sent [itt 0x%x]\n", ctask->itt);
- /*
- * ctask timed out but session is OK
- * ERL=0 requires task mgmt abort to be issued on each
- * failed command. requests must be serialized.
- */
- memset(hdr, 0, sizeof(struct iscsi_tm));
- hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
- hdr->flags = ISCSI_TM_FUNC_ABORT_TASK;
- hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- memcpy(hdr->lun, ctask->hdr.lun, sizeof(hdr->lun));
- hdr->rtt = ctask->hdr.itt;
- hdr->refcmdsn = ctask->hdr.cmdsn;
-
- rc = iscsi_conn_send_generic(conn, (struct iscsi_hdr *)hdr,
- NULL, 0);
- if (rc) {
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- debug_scsi("abort sent failure [itt 0x%x]", ctask->itt);
- } else {
- struct iscsi_r2t_info *r2t;
-
- /*
- * TMF abort vs. TMF response race logic
- */
- spin_lock_bh(&session->lock);
- ctask->mtask = (struct iscsi_mgmt_task *)
- session->mgmt_cmds[(hdr->itt & ITT_MASK) -
- ISCSI_MGMT_ITT_OFFSET];
- /*
- * have to flush r2tqueue to avoid r2t leaks
- */
- while (__kfifo_get(ctask->r2tqueue, (void*)&r2t,
- sizeof(void*))) {
- __kfifo_put(ctask->r2tpool.queue, (void*)&r2t,
- sizeof(void*));
- }
- if (conn->tmabort_state == TMABORT_INITIAL) {
- conn->tmfcmd_pdus_cnt++;
- conn->tmabort_timer.expires = 3*HZ + jiffies;
- conn->tmabort_timer.function =
- iscsi_tmabort_timedout;
- conn->tmabort_timer.data = (unsigned long)ctask;
- add_timer(&conn->tmabort_timer);
- debug_scsi("abort sent [itt 0x%x]", ctask->itt);
- } else {
- if (!ctask->sc ||
- conn->tmabort_state == TMABORT_SUCCESS) {
- conn->tmabort_state = TMABORT_INITIAL;
- spin_unlock_bh(&session->lock);
- mutex_unlock(&conn->xmitmutex);
- goto success;
- }
- conn->tmabort_state = TMABORT_INITIAL;
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- }
- spin_unlock_bh(&session->lock);
- }
+ spin_lock_bh(&session->lock);
+ ctask->mtask = (struct iscsi_mgmt_task *)
+ session->mgmt_cmds[(hdr->itt & ITT_MASK) -
+ ISCSI_MGMT_ITT_OFFSET];
+
+ if (conn->tmabort_state == TMABORT_INITIAL) {
+ conn->tmfcmd_pdus_cnt++;
+ conn->tmabort_timer.expires = 10*HZ + jiffies;
+ conn->tmabort_timer.function = iscsi_tmabort_timedout;
+ conn->tmabort_timer.data = (unsigned long)ctask;
+ add_timer(&conn->tmabort_timer);
+ debug_scsi("abort set timeout [itt 0x%x]", ctask->itt);
}
+ spin_unlock_bh(&session->lock);
mutex_unlock(&conn->xmitmutex);
-
/*
* block eh thread until:
*
- * 1) abort response;
- * 2) abort timeout;
- * 3) session re-opened;
- * 4) session terminated;
+ * 1) abort response
+ * 2) abort timeout
+ * 3) session is terminated or restarted or userspace has
+ * given up on recovery
*/
- for (;;) {
- int p_state = session->state;
-
- rc = wait_event_interruptible(conn->ehwait,
- (p_state == ISCSI_STATE_LOGGED_IN ?
- (session->state == ISCSI_STATE_TERMINATE ||
- conn->tmabort_state != TMABORT_INITIAL) :
- (session->state == ISCSI_STATE_TERMINATE ||
- session->state == ISCSI_STATE_LOGGED_IN)));
- if (rc) {
- /* shutdown.. */
- session->state = ISCSI_STATE_TERMINATE;
- goto failed;
- }
+ wait_event_interruptible(conn->ehwait,
+ sc->SCp.phase != session->age ||
+ session->state != ISCSI_STATE_LOGGED_IN ||
+ conn->tmabort_state != TMABORT_INITIAL ||
+ session->recovery_failed);
+ if (signal_pending(current))
+ flush_signals(current);
+ del_timer_sync(&conn->tmabort_timer);
- if (signal_pending(current))
- flush_signals(current);
+ mutex_lock(&conn->xmitmutex);
+ return 0;
+}
- if (session->state == ISCSI_STATE_TERMINATE)
- goto failed;
+static int
+iscsi_eh_abort(struct scsi_cmnd *sc)
+{
+ struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
+ struct iscsi_cmd_task *tmp_ctask;
+ struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_session *session = conn->session;
+ int rc;
- spin_lock_bh(&session->lock);
- if (sc->SCp.phase == session->age &&
- (conn->tmabort_state == TMABORT_TIMEDOUT ||
- conn->tmabort_state == TMABORT_FAILED)) {
- conn->tmabort_state = TMABORT_INITIAL;
- if (!ctask->sc) {
- /*
- * ctask completed before tmf abort response or
- * time out.
- * But session is still ok => Happy Retry.
- */
- spin_unlock_bh(&session->lock);
- break;
- }
- spin_unlock_bh(&session->lock);
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- continue;
+ conn->eh_abort_cnt++;
+ debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
+
+ mutex_lock(&conn->xmitmutex);
+ spin_lock_bh(&session->lock);
+
+ /*
+ * If we are not logged in or we have started a new session
+ * then let the host reset code handle this
+ */
+ if (session->state != ISCSI_STATE_LOGGED_IN ||
+ sc->SCp.phase != session->age)
+ goto failed;
+
+ /* ctask completed before time out */
+ if (!ctask->sc)
+ goto success;
+
+ /* what should we do here ? */
+ if (conn->ctask == ctask) {
+ printk(KERN_INFO "sc %p itt 0x%x partially sent. Failing "
+ "abort\n", sc, ctask->itt);
+ goto failed;
+ }
+
+ /* check for the easy pending cmd abort */
+ while (__kfifo_get(conn->xmitqueue, (void*)&tmp_ctask, sizeof(void*))) {
+ if (tmp_ctask->itt == ctask->itt) {
+ debug_scsi("found pending task\n");
+ goto success;
}
+ __kfifo_put(conn->xmitqueue, (void*)&tmp_ctask, sizeof(void*));
+ }
+
+ conn->tmabort_state = TMABORT_INITIAL;
+
+ spin_unlock_bh(&session->lock);
+ rc = iscsi_exec_abort_task(sc, ctask);
+ spin_lock_bh(&session->lock);
+
+ iscsi_ctask_mtask_cleanup(ctask);
+ if (rc || sc->SCp.phase != session->age ||
+ session->state != ISCSI_STATE_LOGGED_IN)
+ goto failed;
+
+ /* ctask completed before tmf abort response */
+ if (!ctask->sc) {
+ debug_scsi("sc completed while abort in progress\n");
+ goto success;
+ }
+
+ if (conn->tmabort_state != TMABORT_SUCCESS) {
spin_unlock_bh(&session->lock);
- break;
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ spin_lock_bh(&session->lock);
+ goto failed;
}
success:
debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
- rc = SUCCESS;
- goto exit;
-
-failed:
- debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
- rc = FAILED;
-
-exit:
- del_timer_sync(&conn->tmabort_timer);
+ spin_unlock_bh(&session->lock);
- mutex_lock(&conn->xmitmutex);
+ /* clean up task if aborted */
if (conn->sock) {
struct sock *sk = conn->sock->sk;
write_lock_bh(&sk->sk_callback_lock);
- iscsi_ctask_cleanup(conn, ctask);
+ spin_lock(&session->lock);
+ fail_command(conn, ctask, DRIVER_TIMEOUT << 24);
+ spin_unlock(&session->lock);
write_unlock_bh(&sk->sk_callback_lock);
}
mutex_unlock(&conn->xmitmutex);
- return rc;
+ return SUCCESS;
+
+failed:
+ spin_unlock_bh(&session->lock);
+ mutex_unlock(&conn->xmitmutex);
+
+ debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+ return FAILED;
}
static int
@@ -3359,16 +3499,6 @@ iscsi_conn_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param,
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_session *session = conn->session;
- spin_lock_bh(&session->lock);
- if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE &&
- conn->stop_stage != STOP_CONN_RECOVER) {
- printk(KERN_ERR "iscsi_tcp: can not change parameter [%d]\n",
- param);
- spin_unlock_bh(&session->lock);
- return 0;
- }
- spin_unlock_bh(&session->lock);
-
switch(param) {
case ISCSI_PARAM_MAX_RECV_DLENGTH: {
char *saveptr = conn->data;
@@ -3691,6 +3821,7 @@ static struct iscsi_transport iscsi_tcp_transport = {
.stop_conn = iscsi_conn_stop,
.send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_conn_get_stats,
+ .session_recovery_timedout = iscsi_session_recovery_timedout,
};
static int __init