[SCSI] libiscsi: do not block session during logout
[powerpc.git] / drivers / scsi / libiscsi.c
index d43f909..b7a2b9a 100644 (file)
@@ -37,9 +37,6 @@
 #include <scsi/scsi_transport_iscsi.h>
 #include <scsi/libiscsi.h>
 
-static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
-                        int err);
-
 struct iscsi_session *
 class_to_transport_session(struct iscsi_cls_session *cls_session)
 {
@@ -274,6 +271,53 @@ static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
                iscsi_complete_command(ctask);
 }
 
+/*
+ * session lock must be held
+ */
+static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+                        int err)
+{
+       struct scsi_cmnd *sc;
+
+       sc = ctask->sc;
+       if (!sc)
+               return;
+
+       if (ctask->state == ISCSI_TASK_PENDING)
+               /*
+                * cmd never made it to the xmit thread, so we should not count
+                * the cmd in the sequencing
+                */
+               conn->session->queued_cmdsn--;
+       else
+               conn->session->tt->cleanup_cmd_task(conn, ctask);
+
+       sc->result = err;
+       scsi_set_resid(sc, scsi_bufflen(sc));
+       if (conn->ctask == ctask)
+               conn->ctask = NULL;
+       /* release ref from queuecommand */
+       __iscsi_put_ctask(ctask);
+}
+
+/**
+ * iscsi_free_mgmt_task - return mgmt task back to pool
+ * @conn: iscsi connection
+ * @mtask: mtask
+ *
+ * Must be called with session lock.
+ */
+void iscsi_free_mgmt_task(struct iscsi_conn *conn,
+                         struct iscsi_mgmt_task *mtask)
+{
+       list_del_init(&mtask->running);
+       if (conn->login_mtask == mtask)
+               return;
+       __kfifo_put(conn->session->mgmtpool.queue,
+                   (void*)&mtask, sizeof(void*));
+}
+EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
+
 /**
  * iscsi_cmd_rsp - SCSI Command Response processing
  * @conn: iscsi connection
@@ -464,10 +508,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                         */
                        if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
                                rc = ISCSI_ERR_CONN_FAILED;
-                       list_del_init(&mtask->running);
-                       if (conn->login_mtask != mtask)
-                               __kfifo_put(session->mgmtpool.queue,
-                                           (void*)&mtask, sizeof(void*));
+                       iscsi_free_mgmt_task(conn, mtask);
                        break;
                case ISCSI_OP_SCSI_TMFUNC_RSP:
                        if (datalen) {
@@ -476,6 +517,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        }
 
                        iscsi_tmf_rsp(conn, hdr);
+                       iscsi_free_mgmt_task(conn, mtask);
                        break;
                case ISCSI_OP_NOOP_IN:
                        if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) {
@@ -486,9 +528,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 
                        if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
                                rc = ISCSI_ERR_CONN_FAILED;
-                       list_del_init(&mtask->running);
-                       __kfifo_put(session->mgmtpool.queue,
-                                   (void*)&mtask, sizeof(void*));
+                       iscsi_free_mgmt_task(conn, mtask);
                        break;
                default:
                        rc = ISCSI_ERR_BAD_OPCODE;
@@ -650,14 +690,12 @@ static void iscsi_prep_mtask(struct iscsi_conn *conn,
 static int iscsi_xmit_mtask(struct iscsi_conn *conn)
 {
        struct iscsi_hdr *hdr = conn->mtask->hdr;
-       int rc, was_logout = 0;
+       int rc;
 
+       if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
+               conn->session->state = ISCSI_STATE_LOGGING_OUT;
        spin_unlock_bh(&conn->session->lock);
-       if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) {
-               conn->session->state = ISCSI_STATE_IN_RECOVERY;
-               iscsi_block_session(session_to_cls(conn->session));
-               was_logout = 1;
-       }
+
        rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask);
        spin_lock_bh(&conn->session->lock);
        if (rc)
@@ -665,11 +703,6 @@ static int iscsi_xmit_mtask(struct iscsi_conn *conn)
 
        /* done with this in-progress mtask */
        conn->mtask = NULL;
-
-       if (was_logout) {
-               set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-               return -ENODATA;
-       }
        return 0;
 }
 
@@ -763,6 +796,12 @@ check_mgmt:
        while (!list_empty(&conn->mgmtqueue)) {
                conn->mtask = list_entry(conn->mgmtqueue.next,
                                         struct iscsi_mgmt_task, running);
+               if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+                       iscsi_free_mgmt_task(conn, conn->mtask);
+                       conn->mtask = NULL;
+                       continue;
+               }
+
                iscsi_prep_mtask(conn, conn->mtask);
                list_move_tail(conn->mgmtqueue.next, &conn->mgmt_run_list);
                rc = iscsi_xmit_mtask(conn);
@@ -777,6 +816,10 @@ check_mgmt:
 
                conn->ctask = list_entry(conn->xmitqueue.next,
                                         struct iscsi_cmd_task, running);
+               if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
+                       fail_command(conn, conn->ctask, DID_NO_CONNECT << 16);
+                       continue;
+               }
                if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
                        fail_command(conn, conn->ctask, DID_ABORT << 16);
                        continue;
@@ -800,6 +843,12 @@ check_mgmt:
                if (conn->session->fast_abort && conn->tmf_state != TMF_INITIAL)
                        break;
 
+               /*
+                * we always do fastlogout - conn stop code will clean up.
+                */
+               if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
+                       break;
+
                conn->ctask = list_entry(conn->requeue.next,
                                         struct iscsi_cmd_task, running);
                conn->ctask->state = ISCSI_TASK_RUNNING;
@@ -842,6 +891,7 @@ enum {
        FAILURE_SESSION_TERMINATE,
        FAILURE_SESSION_IN_RECOVERY,
        FAILURE_SESSION_RECOVERY_TIMEOUT,
+       FAILURE_SESSION_LOGGING_OUT,
 };
 
 int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
@@ -879,12 +929,19 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                        goto reject;
                }
 
-               if (session->state == ISCSI_STATE_RECOVERY_FAILED)
+               switch (session->state) {
+               case ISCSI_STATE_RECOVERY_FAILED:
                        reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
-               else if (session->state == ISCSI_STATE_TERMINATE)
+                       break;
+               case ISCSI_STATE_TERMINATE:
                        reason = FAILURE_SESSION_TERMINATE;
-               else
+                       break;
+               case ISCSI_STATE_LOGGING_OUT:
+                       reason = FAILURE_SESSION_LOGGING_OUT;
+                       break;
+               default:
                        reason = FAILURE_SESSION_FREED;
+               }
                goto fault;
        }
 
@@ -1120,44 +1177,9 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
        if (age != session->age ||
            session->state != ISCSI_STATE_LOGGED_IN)
                return -ENOTCONN;
-
-       if (!list_empty(&mtask->running)) {
-               list_del_init(&mtask->running);
-               __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
-                           sizeof(void*));
-       }
        return 0;
 }
 
-/*
- * session lock must be held
- */
-static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
-                        int err)
-{
-       struct scsi_cmnd *sc;
-
-       sc = ctask->sc;
-       if (!sc)
-               return;
-
-       if (ctask->state == ISCSI_TASK_PENDING)
-               /*
-                * cmd never made it to the xmit thread, so we should not count
-                * the cmd in the sequencing
-                */
-               conn->session->queued_cmdsn--;
-       else
-               conn->session->tt->cleanup_cmd_task(conn, ctask);
-
-       sc->result = err;
-       scsi_set_resid(sc, scsi_bufflen(sc));
-       if (conn->ctask == ctask)
-               conn->ctask = NULL;
-       /* release ref from queuecommand */
-       __iscsi_put_ctask(ctask);
-}
-
 /*
  * Fail commands. session lock held and recv side suspended and xmit
  * thread flushed
@@ -1837,22 +1859,13 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
        /* handle pending */
        list_for_each_entry_safe(mtask, tmp, &conn->mgmtqueue, running) {
                debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt);
-               list_del_init(&mtask->running);
-               if (mtask == conn->login_mtask)
-                       continue;
-               __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
-                           sizeof(void*));
+               iscsi_free_mgmt_task(conn, mtask);
        }
 
        /* handle running */
        list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) {
                debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt);
-               list_del_init(&mtask->running);
-
-               if (mtask == conn->login_mtask)
-                       continue;
-               __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
-                          sizeof(void*));
+               iscsi_free_mgmt_task(conn, mtask);
        }
 
        conn->mtask = NULL;