|
@@ -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
|