Merge master.kernel.org:/pub/scm/linux/kernel/git/acme/net-2.6
[pandora-kernel.git] / drivers / scsi / libiscsi.c
index 7e6e031..c542d0e 100644 (file)
@@ -68,8 +68,7 @@ iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
 EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn);
 
 void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
-                                  struct iscsi_data *hdr,
-                                  int transport_data_cnt)
+                                  struct iscsi_data *hdr)
 {
        struct iscsi_conn *conn = ctask->conn;
 
@@ -82,14 +81,12 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
 
        hdr->itt = ctask->hdr->itt;
        hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
-
-       hdr->offset = cpu_to_be32(ctask->total_length -
-                                 transport_data_cnt -
-                                 ctask->unsol_count);
+       hdr->offset = cpu_to_be32(ctask->unsol_offset);
 
        if (ctask->unsol_count > conn->max_xmit_dlength) {
                hton24(hdr->dlength, conn->max_xmit_dlength);
                ctask->data_count = conn->max_xmit_dlength;
+               ctask->unsol_offset += ctask->data_count;
                hdr->flags = 0;
        } else {
                hton24(hdr->dlength, ctask->unsol_count);
@@ -125,6 +122,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
         memcpy(hdr->cdb, sc->cmnd, sc->cmd_len);
         memset(&hdr->cdb[sc->cmd_len], 0, MAX_COMMAND_SIZE - sc->cmd_len);
 
+       ctask->data_count = 0;
        if (sc->sc_data_direction == DMA_TO_DEVICE) {
                hdr->flags |= ISCSI_FLAG_CMD_WRITE;
                /*
@@ -143,6 +141,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
                 */
                ctask->imm_count = 0;
                ctask->unsol_count = 0;
+               ctask->unsol_offset = 0;
                ctask->unsol_datasn = 0;
 
                if (session->imm_data_en) {
@@ -156,9 +155,12 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
                } else
                        zero_data(ctask->hdr->dlength);
 
-               if (!session->initial_r2t_en)
+               if (!session->initial_r2t_en) {
                        ctask->unsol_count = min(session->first_burst,
                                ctask->total_length) - ctask->imm_count;
+                       ctask->unsol_offset = ctask->imm_count;
+               }
+
                if (!ctask->unsol_count)
                        /* No unsolicit Data-Out's */
                        ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
@@ -177,24 +179,51 @@ EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
 
 /**
  * iscsi_complete_command - return command back to scsi-ml
- * @session: iscsi session
  * @ctask: iscsi cmd task
  *
  * Must be called with session lock.
  * This function returns the scsi command to scsi-ml and returns
  * the cmd task to the pool of available cmd tasks.
  */
-static void iscsi_complete_command(struct iscsi_session *session,
-                                  struct iscsi_cmd_task *ctask)
+static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
 {
+       struct iscsi_session *session = ctask->conn->session;
        struct scsi_cmnd *sc = ctask->sc;
 
+       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);
 }
 
+static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask)
+{
+       atomic_inc(&ctask->refcount);
+}
+
+static void iscsi_get_ctask(struct iscsi_cmd_task *ctask)
+{
+       spin_lock_bh(&ctask->conn->session->lock);
+       __iscsi_get_ctask(ctask);
+       spin_unlock_bh(&ctask->conn->session->lock);
+}
+
+static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+{
+       if (atomic_dec_and_test(&ctask->refcount))
+               iscsi_complete_command(ctask);
+}
+
+static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+{
+       spin_lock_bh(&ctask->conn->session->lock);
+       __iscsi_put_ctask(ctask);
+       spin_unlock_bh(&ctask->conn->session->lock);
+}
+
 /**
  * iscsi_cmd_rsp - SCSI Command Response processing
  * @conn: iscsi connection
@@ -271,10 +300,53 @@ out:
                   (long)sc, sc->result, ctask->itt);
        conn->scsirsp_pdus_cnt++;
 
-       iscsi_complete_command(conn->session, ctask);
+       __iscsi_put_ctask(ctask);
        return rc;
 }
 
+static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
+{
+       struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr;
+
+       conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+       conn->tmfrsp_pdus_cnt++;
+
+       if (conn->tmabort_state != TMABORT_INITIAL)
+               return;
+
+       if (tmf->response == ISCSI_TMF_RSP_COMPLETE)
+               conn->tmabort_state = TMABORT_SUCCESS;
+       else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)
+               conn->tmabort_state = TMABORT_NOT_FOUND;
+       else
+               conn->tmabort_state = TMABORT_FAILED;
+       wake_up(&conn->ehwait);
+}
+
+static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+                              char *data, int datalen)
+{
+       struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
+       struct iscsi_hdr rejected_pdu;
+       uint32_t itt;
+
+       conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;
+
+       if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) {
+               if (ntoh24(reject->dlength) > datalen)
+                       return ISCSI_ERR_PROTO;
+
+               if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
+                       memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
+                       itt = rejected_pdu.itt & ISCSI_ITT_MASK;
+                       printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "
+                               "due to DataDigest error.\n", itt,
+                               rejected_pdu.opcode);
+               }
+       }
+       return 0;
+}
+
 /**
  * __iscsi_complete_pdu - complete pdu
  * @conn: iscsi conn
@@ -316,7 +388,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
                        if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
                                conn->scsirsp_pdus_cnt++;
-                               iscsi_complete_command(session, ctask);
+                               __iscsi_put_ctask(ctask);
                        }
                        break;
                case ISCSI_OP_R2T:
@@ -340,6 +412,10 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 
                switch(opcode) {
                case ISCSI_OP_LOGOUT_RSP:
+                       if (datalen) {
+                               rc = ISCSI_ERR_PROTO;
+                               break;
+                       }
                        conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
                        /* fall through */
                case ISCSI_OP_LOGIN_RSP:
@@ -348,7 +424,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                         * login related PDU's exp_statsn is handled in
                         * userspace
                         */
-                       rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen);
+                       if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+                               rc = ISCSI_ERR_CONN_FAILED;
                        list_del(&mtask->running);
                        if (conn->login_mtask != mtask)
                                __kfifo_put(session->mgmtpool.queue,
@@ -360,25 +437,17 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                                break;
                        }
 
-                       conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
-                       conn->tmfrsp_pdus_cnt++;
-                       if (conn->tmabort_state == TMABORT_INITIAL) {
-                               conn->tmabort_state =
-                                       ((struct iscsi_tm_rsp *)hdr)->
-                                       response == ISCSI_TMF_RSP_COMPLETE ?
-                                               TMABORT_SUCCESS:TMABORT_FAILED;
-                               /* unblock eh_abort() */
-                               wake_up(&conn->ehwait);
-                       }
+                       iscsi_tmf_rsp(conn, hdr);
                        break;
                case ISCSI_OP_NOOP_IN:
-                       if (hdr->ttt != ISCSI_RESERVED_TAG) {
+                       if (hdr->ttt != ISCSI_RESERVED_TAG || datalen) {
                                rc = ISCSI_ERR_PROTO;
                                break;
                        }
                        conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
 
-                       rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen);
+                       if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+                               rc = ISCSI_ERR_CONN_FAILED;
                        list_del(&mtask->running);
                        if (conn->login_mtask != mtask)
                                __kfifo_put(session->mgmtpool.queue,
@@ -389,19 +458,27 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        break;
                }
        } else if (itt == ISCSI_RESERVED_TAG) {
+               rc = iscsi_check_assign_cmdsn(session,
+                                            (struct iscsi_nopin*)hdr);
+               if (rc)
+                       goto done;
+
                switch(opcode) {
                case ISCSI_OP_NOOP_IN:
-                       if (!datalen) {
-                               rc = iscsi_check_assign_cmdsn(session,
-                                                (struct iscsi_nopin*)hdr);
-                               if (!rc && hdr->ttt != ISCSI_RESERVED_TAG)
-                                       rc = iscsi_recv_pdu(conn->cls_conn,
-                                                           hdr, NULL, 0);
-                       } else
+                       if (datalen) {
                                rc = ISCSI_ERR_PROTO;
+                               break;
+                       }
+
+                       if (hdr->ttt == ISCSI_RESERVED_TAG)
+                               break;
+
+                       if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0))
+                               rc = ISCSI_ERR_CONN_FAILED;
                        break;
                case ISCSI_OP_REJECT:
-                       /* we need sth like iscsi_reject_rsp()*/
+                       rc = iscsi_handle_reject(conn, hdr, data, datalen);
+                       break;
                case ISCSI_OP_ASYNC_EVENT:
                        conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
                        /* we need sth like iscsi_async_event_rsp() */
@@ -537,7 +614,9 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
        BUG_ON(conn->ctask && conn->mtask);
 
        if (conn->ctask) {
+               iscsi_get_ctask(conn->ctask);
                rc = tt->xmit_cmd_task(conn, conn->ctask);
+               iscsi_put_ctask(conn->ctask);
                if (rc)
                        goto again;
                /* done with this in-progress ctask */
@@ -568,20 +647,31 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
        }
 
        /* process command queue */
-       while (__kfifo_get(conn->xmitqueue, (void*)&conn->ctask,
-                          sizeof(void*))) {
+       spin_lock_bh(&conn->session->lock);
+       while (!list_empty(&conn->xmitqueue)) {
                /*
                 * iscsi tcp may readd the task to the xmitqueue to send
                 * write data
                 */
-               spin_lock_bh(&conn->session->lock);
-               if (list_empty(&conn->ctask->running))
-                       list_add_tail(&conn->ctask->running, &conn->run_list);
+               conn->ctask = list_entry(conn->xmitqueue.next,
+                                        struct iscsi_cmd_task, running);
+               conn->ctask->state = ISCSI_TASK_RUNNING;
+               list_move_tail(conn->xmitqueue.next, &conn->run_list);
+               __iscsi_get_ctask(conn->ctask);
                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);
+               if (rc) {
+                       spin_unlock_bh(&conn->session->lock);
+                       goto again;
+               }
        }
+       spin_unlock_bh(&conn->session->lock);
        /* done with this ctask */
        conn->ctask = NULL;
 
@@ -629,6 +719,7 @@ enum {
        FAILURE_SESSION_FAILED,
        FAILURE_SESSION_FREED,
        FAILURE_WINDOW_CLOSED,
+       FAILURE_OOM,
        FAILURE_SESSION_TERMINATE,
        FAILURE_SESSION_IN_RECOVERY,
        FAILURE_SESSION_RECOVERY_TIMEOUT,
@@ -644,6 +735,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);
@@ -687,10 +779,16 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 
        conn = session->leadconn;
 
-       __kfifo_get(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
+       if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
+                        sizeof(void*))) {
+               reason = FAILURE_OOM;
+               goto reject;
+       }
        sc->SCp.phase = session->age;
        sc->SCp.ptr = (char *)ctask;
 
+       atomic_set(&ctask->refcount, 1);
+       ctask->state = ISCSI_TASK_PENDING;
        ctask->mtask = NULL;
        ctask->conn = conn;
        ctask->sc = sc;
@@ -700,11 +798,12 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 
        session->tt->init_cmd_task(ctask);
 
-       __kfifo_put(conn->xmitqueue, (void*)&ctask, sizeof(void*));
+       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);
 
@@ -977,31 +1076,27 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
 /*
  * xmit mutex and session lock must be held
  */
-#define iscsi_remove_task(tasktype)                                    \
-static struct iscsi_##tasktype *                                       \
-iscsi_remove_##tasktype(struct kfifo *fifo, uint32_t itt)              \
-{                                                                      \
-       int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*);            \
-       struct iscsi_##tasktype *task;                                  \
-                                                                       \
-       debug_scsi("searching %d tasks\n", nr_tasks);                   \
-                                                                       \
-       for (i = 0; i < nr_tasks; i++) {                                \
-               __kfifo_get(fifo, (void*)&task, sizeof(void*));         \
-               debug_scsi("check task %u\n", task->itt);               \
-                                                                       \
-               if (task->itt == itt) {                                 \
-                       debug_scsi("matched task\n");                   \
-                       return task;                                    \
-               }                                                       \
-                                                                       \
-               __kfifo_put(fifo, (void*)&task, sizeof(void*));         \
-       }                                                               \
-       return NULL;                                                    \
-}
+static struct iscsi_mgmt_task *
+iscsi_remove_mgmt_task(struct kfifo *fifo, uint32_t itt)
+{
+       int i, nr_tasks = __kfifo_len(fifo) / sizeof(void*);
+       struct iscsi_mgmt_task *task;
+
+       debug_scsi("searching %d tasks\n", nr_tasks);
 
-iscsi_remove_task(mgmt_task);
-iscsi_remove_task(cmd_task);
+       for (i = 0; i < nr_tasks; i++) {
+               __kfifo_get(fifo, (void*)&task, sizeof(void*));
+               debug_scsi("check task %u\n", task->itt);
+
+               if (task->itt == itt) {
+                       debug_scsi("matched task\n");
+                       return task;
+               }
+
+               __kfifo_put(fifo, (void*)&task, sizeof(void*));
+       }
+       return NULL;
+}
 
 static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
 {
@@ -1027,25 +1122,39 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
 {
        struct scsi_cmnd *sc;
 
-       conn->session->tt->cleanup_cmd_task(conn, ctask);
-       iscsi_ctask_mtask_cleanup(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;
-       iscsi_complete_command(conn->session, ctask);
+       /* 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 *pending_ctask;
+       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);
 
@@ -1061,8 +1170,11 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
                goto failed;
 
        /* ctask completed before time out */
-       if (!ctask->sc)
-               goto success;
+       if (!ctask->sc) {
+               spin_unlock_bh(&session->lock);
+               debug_scsi("sc completed while abort in progress\n");
+               goto success_rel_mutex;
+       }
 
        /* what should we do here ? */
        if (conn->ctask == ctask) {
@@ -1071,17 +1183,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
                goto failed;
        }
 
-       /* check for the easy pending cmd abort */
-       pending_ctask = iscsi_remove_cmd_task(conn->xmitqueue, ctask->itt);
-       if (pending_ctask) {
-               /* iscsi_tcp queues write transfers on the xmitqueue */
-               if (list_empty(&pending_ctask->running)) {
-                       debug_scsi("found pending task\n");
-                       goto success;
-               } else
-                       __kfifo_put(conn->xmitqueue, (void*)&pending_ctask,
-                                   sizeof(void*));
-       }
+       if (ctask->state == ISCSI_TASK_PENDING)
+               goto success_cleanup;
 
        conn->tmabort_state = TMABORT_INITIAL;
 
@@ -1089,25 +1192,31 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
        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;
+       iscsi_ctask_mtask_cleanup(ctask);
 
-       /* 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) {
+       switch (conn->tmabort_state) {
+       case TMABORT_SUCCESS:
+               goto success_cleanup;
+       case TMABORT_NOT_FOUND:
+               if (!ctask->sc) {
+                       /* ctask completed before tmf abort response */
+                       spin_unlock_bh(&session->lock);
+                       debug_scsi("sc completed while abort in progress\n");
+                       goto success_rel_mutex;
+               }
+               /* fall through */
+       default:
+               /* timedout or failed */
                spin_unlock_bh(&session->lock);
                iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
                spin_lock_bh(&session->lock);
                goto failed;
        }
 
-success:
+success_cleanup:
        debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
        spin_unlock_bh(&session->lock);
 
@@ -1121,6 +1230,7 @@ success:
        spin_unlock(&session->lock);
        write_unlock_bh(conn->recv_lock);
 
+success_rel_mutex:
        mutex_unlock(&conn->xmitmutex);
        return SUCCESS;
 
@@ -1263,6 +1373,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
                if (cmd_task_size)
                        ctask->dd_data = &ctask[1];
                ctask->itt = cmd_i;
+               INIT_LIST_HEAD(&ctask->running);
        }
 
        spin_lock_init(&session->lock);
@@ -1282,6 +1393,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
                if (mgmt_task_size)
                        mtask->dd_data = &mtask[1];
                mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i;
+               INIT_LIST_HEAD(&mtask->running);
        }
 
        if (scsi_add_host(shost, NULL))
@@ -1322,15 +1434,18 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
 {
        struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
        struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+       struct module *owner = cls_session->transport->owner;
 
        scsi_remove_host(shost);
 
        iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
        iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
 
+       kfree(session->targetname);
+
        iscsi_destroy_session(cls_session);
        scsi_host_put(shost);
-       module_put(cls_session->transport->owner);
+       module_put(owner);
 }
 EXPORT_SYMBOL_GPL(iscsi_session_teardown);
 
@@ -1361,12 +1476,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        conn->tmabort_state = TMABORT_INITIAL;
        INIT_LIST_HEAD(&conn->run_list);
        INIT_LIST_HEAD(&conn->mgmt_run_list);
-
-       /* initialize general xmit PDU commands queue */
-       conn->xmitqueue = kfifo_alloc(session->cmds_max * sizeof(void*),
-                                       GFP_KERNEL, NULL);
-       if (conn->xmitqueue == ERR_PTR(-ENOMEM))
-               goto xmitqueue_alloc_fail;
+       INIT_LIST_HEAD(&conn->xmitqueue);
 
        /* initialize general immediate & non-immediate PDU commands queue */
        conn->immqueue = kfifo_alloc(session->mgmtpool_max * sizeof(void*),
@@ -1394,7 +1504,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        data = kmalloc(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH, GFP_KERNEL);
        if (!data)
                goto login_mtask_data_alloc_fail;
-       conn->login_mtask->data = data;
+       conn->login_mtask->data = conn->data = data;
 
        init_timer(&conn->tmabort_timer);
        mutex_init(&conn->xmitmutex);
@@ -1410,8 +1520,6 @@ login_mtask_alloc_fail:
 mgmtqueue_alloc_fail:
        kfifo_free(conn->immqueue);
 immqueue_alloc_fail:
-       kfifo_free(conn->xmitqueue);
-xmitqueue_alloc_fail:
        iscsi_destroy_conn(cls_conn);
        return NULL;
 }
@@ -1432,12 +1540,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
 
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
        mutex_lock(&conn->xmitmutex);
-       if (conn->c_stage == ISCSI_CONN_INITIAL_STAGE) {
-               if (session->tt->suspend_conn_recv)
-                       session->tt->suspend_conn_recv(conn);
-
-               session->tt->terminate_conn(conn);
-       }
 
        spin_lock_bh(&session->lock);
        conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
@@ -1474,7 +1576,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
        }
 
        spin_lock_bh(&session->lock);
-       kfree(conn->login_mtask->data);
+       kfree(conn->data);
+       kfree(conn->persistent_address);
        __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
                    sizeof(void*));
        list_del(&conn->item);
@@ -1489,7 +1592,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
                session->cmdsn = session->max_cmdsn = session->exp_cmdsn = 1;
        spin_unlock_bh(&session->lock);
 
-       kfifo_free(conn->xmitqueue);
        kfifo_free(conn->immqueue);
        kfifo_free(conn->mgmtqueue);
 
@@ -1502,11 +1604,19 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
        struct iscsi_conn *conn = cls_conn->dd_data;
        struct iscsi_session *session = conn->session;
 
-       if (session == NULL) {
+       if (!session) {
                printk(KERN_ERR "iscsi: can't start unbound connection\n");
                return -EPERM;
        }
 
+       if ((session->imm_data_en || !session->initial_r2t_en) &&
+            session->first_burst > session->max_burst) {
+               printk("iscsi: invalid burst lengths: "
+                      "first_burst %d max_burst %d\n",
+                      session->first_burst, session->max_burst);
+               return -EINVAL;
+       }
+
        spin_lock_bh(&session->lock);
        conn->c_stage = ISCSI_CONN_STARTED;
        session->state = ISCSI_STATE_LOGGED_IN;
@@ -1572,7 +1682,7 @@ static void fail_all_commands(struct iscsi_conn *conn)
        struct iscsi_cmd_task *ctask, *tmp;
 
        /* flush pending */
-       while (__kfifo_get(conn->xmitqueue, (void*)&ctask, sizeof(void*))) {
+       list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) {
                debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc,
                           ctask->itt);
                fail_command(conn, ctask, DID_BUS_BUSY << 16);
@@ -1615,8 +1725,9 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
        spin_unlock_bh(&session->lock);
 
-       if (session->tt->suspend_conn_recv)
-               session->tt->suspend_conn_recv(conn);
+       write_lock_bh(conn->recv_lock);
+       set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+       write_unlock_bh(conn->recv_lock);
 
        mutex_lock(&conn->xmitmutex);
        /*
@@ -1635,7 +1746,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
                }
        }
 
-       session->tt->terminate_conn(conn);
        /*
         * flush queues.
         */