]> err.no Git - linux-2.6/blobdiff - drivers/scsi/libiscsi.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / drivers / scsi / libiscsi.c
index 5606d1e62978433e7ca763e917b15e1c8af4c8e6..efceed451b466245d847f59e4da02b7c141be70a 100644 (file)
@@ -737,12 +737,19 @@ check_mgmt:
                 */
                conn->ctask = list_entry(conn->xmitqueue.next,
                                         struct iscsi_cmd_task, running);
-               if (conn->ctask->state == ISCSI_TASK_PENDING) {
+               switch (conn->ctask->state) {
+               case ISCSI_TASK_ABORTING:
+                       break;
+               case ISCSI_TASK_PENDING:
                        iscsi_prep_scsi_cmd_pdu(conn->ctask);
                        conn->session->tt->init_cmd_task(conn->ctask);
+                       /* fall through */
+               default:
+                       conn->ctask->state = ISCSI_TASK_RUNNING;
+                       break;
                }
-               conn->ctask->state = ISCSI_TASK_RUNNING;
                list_move_tail(conn->xmitqueue.next, &conn->run_list);
+
                rc = iscsi_xmit_ctask(conn);
                if (rc)
                        goto again;
@@ -1049,7 +1056,9 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
        ctask->mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
                                            NULL, 0);
        if (!ctask->mtask) {
+               spin_unlock_bh(&session->lock);
                iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+               spin_lock_bh(&session->lock)
                debug_scsi("abort sent failure [itt 0x%x]\n", ctask->itt);
                return -EPERM;
        }
@@ -1066,6 +1075,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
                debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt);
        }
        spin_unlock_bh(&session->lock);
+       mutex_unlock(&session->eh_mutex);
        scsi_queue_work(session->host, &conn->xmitwork);
 
        /*
@@ -1083,6 +1093,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
        if (signal_pending(current))
                flush_signals(current);
        del_timer_sync(&conn->tmabort_timer);
+       mutex_lock(&session->eh_mutex);
        spin_lock_bh(&session->lock);
        return 0;
 }
@@ -1158,31 +1169,45 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
        __iscsi_put_ctask(ctask);
 }
 
+static void iscsi_suspend_tx(struct iscsi_conn *conn)
+{
+       set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+       scsi_flush_work(conn->session->host);
+}
+
+static void iscsi_start_tx(struct iscsi_conn *conn)
+{
+       clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+       scsi_queue_work(conn->session->host, &conn->xmitwork);
+}
+
 int iscsi_eh_abort(struct scsi_cmnd *sc)
 {
+       struct Scsi_Host *host = sc->device->host;
+       struct iscsi_session *session = iscsi_hostdata(host->hostdata);
        struct iscsi_cmd_task *ctask;
        struct iscsi_conn *conn;
-       struct iscsi_session *session;
        int rc;
 
+       mutex_lock(&session->eh_mutex);
+       spin_lock_bh(&session->lock);
        /*
         * if session was ISCSI_STATE_IN_RECOVERY then we may not have
         * got the command.
         */
        if (!sc->SCp.ptr) {
                debug_scsi("sc never reached iscsi layer or it completed.\n");
+               spin_unlock_bh(&session->lock);
+               mutex_unlock(&session->eh_mutex);
                return SUCCESS;
        }
 
        ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
        conn = ctask->conn;
-       session = conn->session;
 
        conn->eh_abort_cnt++;
        debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
 
-       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
@@ -1219,6 +1244,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
        switch (conn->tmabort_state) {
        case TMABORT_SUCCESS:
                spin_unlock_bh(&session->lock);
+               iscsi_suspend_tx(conn);
                /*
                 * clean up task if aborted. grab the recv lock as a writer
                 */
@@ -1227,11 +1253,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
                fail_command(conn, ctask, DID_ABORT << 16);
                spin_unlock(&session->lock);
                write_unlock_bh(conn->recv_lock);
-               /*
-                * make sure xmit thread is not still touching the
-                * ctask/scsi_cmnd
-                */
-               scsi_flush_work(session->host);
+               iscsi_start_tx(conn);
                goto success_unlocked;
        case TMABORT_NOT_FOUND:
                if (!ctask->sc) {
@@ -1251,12 +1273,14 @@ success:
        spin_unlock_bh(&session->lock);
 success_unlocked:
        debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+       mutex_unlock(&session->eh_mutex);
        return SUCCESS;
 
 failed:
        spin_unlock_bh(&session->lock);
 failed_unlocked:
        debug_scsi("abort failed [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+       mutex_unlock(&session->eh_mutex);
        return FAILED;
 }
 EXPORT_SYMBOL_GPL(iscsi_eh_abort);
@@ -1403,6 +1427,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
        session->max_cmdsn = initial_cmdsn + 1;
        session->max_r2t = 1;
        session->tt = iscsit;
+       mutex_init(&session->eh_mutex);
 
        /* initialize SCSI PDU commands pool */
        if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
@@ -1736,9 +1761,22 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
 {
        int old_stop_stage;
 
+       mutex_lock(&session->eh_mutex);
        spin_lock_bh(&session->lock);
        if (conn->stop_stage == STOP_CONN_TERM) {
                spin_unlock_bh(&session->lock);
+               mutex_unlock(&session->eh_mutex);
+               return;
+       }
+
+       /*
+        * The LLD either freed/unset the lock on us, or userspace called
+        * stop but did not create a proper connection (connection was never
+        * bound or it was unbound then stop was called).
+        */
+       if (!conn->recv_lock) {
+               spin_unlock_bh(&session->lock);
+               mutex_unlock(&session->eh_mutex);
                return;
        }
 
@@ -1755,9 +1793,9 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
        old_stop_stage = conn->stop_stage;
        conn->stop_stage = flag;
        conn->c_stage = ISCSI_CONN_STOPPED;
-       set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
        spin_unlock_bh(&session->lock);
-       scsi_flush_work(session->host);
+
+       iscsi_suspend_tx(conn);
 
        write_lock_bh(conn->recv_lock);
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
@@ -1786,6 +1824,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
        fail_all_commands(conn);
        flush_control_queues(session, conn);
        spin_unlock_bh(&session->lock);
+       mutex_unlock(&session->eh_mutex);
 }
 
 void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)