Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/agpgart
[pandora-kernel.git] / drivers / scsi / libiscsi.c
index 864c628..d37048c 100644 (file)
@@ -192,6 +192,8 @@ static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
 
        ctask->state = ISCSI_TASK_COMPLETED;
        ctask->sc = NULL;
+       /* SCSI eh reuses commands to verify us */
+       sc->SCp.ptr = NULL;
        list_del_init(&ctask->running);
        __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
        sc->scsi_done(sc);
@@ -211,12 +213,8 @@ static void iscsi_get_ctask(struct iscsi_cmd_task *ctask)
 
 static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
 {
-       struct iscsi_conn *conn = ctask->conn;
-
-       if (atomic_dec_and_test(&ctask->refcount)) {
-               conn->session->tt->cleanup_cmd_task(conn, ctask);
+       if (atomic_dec_and_test(&ctask->refcount))
                iscsi_complete_command(ctask);
-       }
 }
 
 static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
@@ -262,7 +260,7 @@ static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        }
 
        if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) {
-               int senselen;
+               uint16_t senselen;
 
                if (datalen < 2) {
 invalid_datalen:
@@ -272,12 +270,12 @@ invalid_datalen:
                        goto out;
                }
 
-               senselen = (data[0] << 8) | data[1];
+               senselen = be16_to_cpu(*(uint16_t *)data);
                if (datalen < senselen)
                        goto invalid_datalen;
 
                memcpy(sc->sense_buffer, data + 2,
-                      min(senselen, SCSI_SENSE_BUFFERSIZE));
+                      min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
                debug_scsi("copied %d bytes of sense\n",
                           min(senselen, SCSI_SENSE_BUFFERSIZE));
        }
@@ -483,8 +481,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        break;
                case ISCSI_OP_ASYNC_EVENT:
                        conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
-                       /* we need sth like iscsi_async_event_rsp() */
-                       rc = ISCSI_ERR_BAD_OPCODE;
+                       if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+                               rc = ISCSI_ERR_CONN_FAILED;
                        break;
                default:
                        rc = ISCSI_ERR_BAD_OPCODE;
@@ -580,6 +578,27 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_failure);
 
+static int iscsi_xmit_imm_task(struct iscsi_conn *conn)
+{
+       struct iscsi_hdr *hdr = conn->mtask->hdr;
+       int rc, was_logout = 0;
+
+       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);
+       if (rc)
+               return rc;
+
+       if (was_logout) {
+               set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+               return -ENODATA;
+       }
+       return 0;
+}
+
 /**
  * iscsi_data_xmit - xmit any command into the scheduled connection
  * @conn: iscsi connection
@@ -625,7 +644,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
                conn->ctask = NULL;
        }
        if (conn->mtask) {
-               rc = tt->xmit_mgmt_task(conn, conn->mtask);
+               rc = iscsi_xmit_imm_task(conn);
                if (rc)
                        goto again;
                /* done with this in-progress mtask */
@@ -640,7 +659,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
                        list_add_tail(&conn->mtask->running,
                                      &conn->mgmt_run_list);
                        spin_unlock_bh(&conn->session->lock);
-                       rc = tt->xmit_mgmt_task(conn, conn->mtask);
+                       rc = iscsi_xmit_imm_task(conn);
                        if (rc)
                                goto again;
                }
@@ -663,8 +682,6 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
                spin_unlock_bh(&conn->session->lock);
 
                rc = tt->xmit_cmd_task(conn, conn->ctask);
-               if (rc)
-                       goto again;
 
                spin_lock_bh(&conn->session->lock);
                __iscsi_put_ctask(conn->ctask);
@@ -702,9 +719,10 @@ again:
        return rc;
 }
 
-static void iscsi_xmitworker(void *data)
+static void iscsi_xmitworker(struct work_struct *work)
 {
-       struct iscsi_conn *conn = data;
+       struct iscsi_conn *conn =
+               container_of(work, struct iscsi_conn, xmitwork);
        int rc;
        /*
         * serialize Xmit worker on a per-connection basis.
@@ -737,6 +755,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 
        sc->scsi_done = done;
        sc->result = 0;
+       sc->SCp.ptr = NULL;
 
        host = sc->device->host;
        session = iscsi_hostdata(host->hostdata);
@@ -779,6 +798,10 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
        }
 
        conn = session->leadconn;
+       if (!conn) {
+               reason = FAILURE_SESSION_FREED;
+               goto fault;
+       }
 
        if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
                         sizeof(void*))) {
@@ -801,9 +824,10 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 
        list_add_tail(&ctask->running, &conn->xmitqueue);
        debug_scsi(
-              "ctask enq [%s cid %d sc %lx itt 0x%x len %d cmdsn %d win %d]\n",
+              "ctask enq [%s cid %d sc %p cdb 0x%x itt 0x%x len %d cmdsn %d "
+               "win %d]\n",
                sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
-               conn->id, (long)sc, ctask->itt, sc->request_bufflen,
+               conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen,
                session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
        spin_unlock(&session->lock);
 
@@ -952,13 +976,13 @@ int iscsi_eh_host_reset(struct scsi_cmnd *sc)
        if (session->state == ISCSI_STATE_TERMINATE) {
 failed:
                debug_scsi("failing host reset: session terminated "
-                          "[CID %d age %d]", conn->id, session->age);
+                          "[CID %d age %d]\n", 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",
+               debug_scsi("failing connection CID %d due to SCSI host reset\n",
                           conn->id);
                fail_session = 1;
        }
@@ -1031,7 +1055,8 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
                                     NULL, 0);
        if (rc) {
                iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
-               debug_scsi("abort sent failure [itt 0x%x] %d", ctask->itt, rc);
+               debug_scsi("abort sent failure [itt 0x%x] %d\n", ctask->itt,
+                          rc);
                return rc;
        }
 
@@ -1048,7 +1073,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
                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);
+               debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt);
        }
        spin_unlock_bh(&session->lock);
        mutex_unlock(&conn->xmitmutex);
@@ -1125,20 +1150,36 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
        sc = ctask->sc;
        if (!sc)
                return;
+
+       conn->session->tt->cleanup_cmd_task(conn, ctask);
        iscsi_ctask_mtask_cleanup(ctask);
 
        sc->result = err;
        sc->resid = sc->request_bufflen;
+       /* release ref from queuecommand */
        __iscsi_put_ctask(ctask);
 }
 
 int iscsi_eh_abort(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 iscsi_cmd_task *ctask;
+       struct iscsi_conn *conn;
+       struct iscsi_session *session;
        int rc;
 
+       /*
+        * 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");
+               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);
 
@@ -1361,7 +1402,6 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
        }
 
        spin_lock_init(&session->lock);
-       INIT_LIST_HEAD(&session->connections);
 
        /* initialize immediate command pool */
        if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max,
@@ -1473,7 +1513,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        if (conn->mgmtqueue == ERR_PTR(-ENOMEM))
                goto mgmtqueue_alloc_fail;
 
-       INIT_WORK(&conn->xmitwork, iscsi_xmitworker, conn);
+       INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
 
        /* allocate login_mtask used for the login/text sequences */
        spin_lock_bh(&session->lock);
@@ -1564,16 +1604,11 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
        kfree(conn->persistent_address);
        __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
                    sizeof(void*));
-       list_del(&conn->item);
-       if (list_empty(&session->connections))
+       if (session->leadconn == conn) {
                session->leadconn = NULL;
-       if (session->leadconn && session->leadconn == conn)
-               session->leadconn = container_of(session->connections.next,
-                       struct iscsi_conn, item);
-
-       if (session->leadconn == NULL)
                /* no connections exits.. reset sequencing */
                session->cmdsn = session->max_cmdsn = session->exp_cmdsn = 1;
+       }
        spin_unlock_bh(&session->lock);
 
        kfifo_free(conn->immqueue);
@@ -1761,32 +1796,12 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
                    struct iscsi_cls_conn *cls_conn, int is_leading)
 {
        struct iscsi_session *session = class_to_transport_session(cls_session);
-       struct iscsi_conn *tmp = ERR_PTR(-EEXIST), *conn = cls_conn->dd_data;
+       struct iscsi_conn *conn = cls_conn->dd_data;
 
-       /* lookup for existing connection */
        spin_lock_bh(&session->lock);
-       list_for_each_entry(tmp, &session->connections, item) {
-               if (tmp == conn) {
-                       if (conn->c_stage != ISCSI_CONN_STOPPED ||
-                           conn->stop_stage == STOP_CONN_TERM) {
-                               printk(KERN_ERR "iscsi: can't bind "
-                                      "non-stopped connection (%d:%d)\n",
-                                      conn->c_stage, conn->stop_stage);
-                               spin_unlock_bh(&session->lock);
-                               return -EIO;
-                       }
-                       break;
-               }
-       }
-       if (tmp != conn) {
-               /* bind new iSCSI connection to session */
-               conn->session = session;
-               list_add(&conn->item, &session->connections);
-       }
-       spin_unlock_bh(&session->lock);
-
        if (is_leading)
                session->leadconn = conn;
+       spin_unlock_bh(&session->lock);
 
        /*
         * Unblock xmitworker(), Login Phase will pass through.