]> Pileus Git - ~andy/linux/blobdiff - drivers/scsi/libiscsi.c
[SCSI] iscsi_tcp: fix padding, data digests, and IO at weird offsets
[~andy/linux] / drivers / scsi / libiscsi.c
index 274a1374ab64925e79327724076b5be1065f3ee2..fb65311c81ddf289d2ec29492b2ba48d83d4e023 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,53 @@ 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;
        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)
+{
+       struct iscsi_conn *conn = ctask->conn;
+
+       if (atomic_dec_and_test(&ctask->refcount)) {
+               conn->session->tt->cleanup_cmd_task(conn, ctask);
+               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
@@ -235,8 +266,8 @@ static int iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 
                if (datalen < 2) {
 invalid_datalen:
-                       printk(KERN_ERR "Got CHECK_CONDITION but invalid "
-                              "data buffer size of %d\n", datalen);
+                       printk(KERN_ERR "iscsi: Got CHECK_CONDITION but "
+                              "invalid data buffer size of %d\n", datalen);
                        sc->result = DID_BAD_TARGET << 16;
                        goto out;
                }
@@ -271,10 +302,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 +390,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:
@@ -333,53 +407,49 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
                           opcode, conn->id, mtask->itt, datalen);
 
+               rc = iscsi_check_assign_cmdsn(session,
+                                             (struct iscsi_nopin*)hdr);
+               if (rc)
+                       goto done;
+
                switch(opcode) {
-               case ISCSI_OP_LOGIN_RSP:
-               case ISCSI_OP_TEXT_RSP:
                case ISCSI_OP_LOGOUT_RSP:
-                       rc = iscsi_check_assign_cmdsn(session,
-                                                (struct iscsi_nopin*)hdr);
-                       if (rc)
+                       if (datalen) {
+                               rc = ISCSI_ERR_PROTO;
                                break;
-
-                       rc = iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen);
+                       }
+                       conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+                       /* fall through */
+               case ISCSI_OP_LOGIN_RSP:
+               case ISCSI_OP_TEXT_RSP:
+                       /*
+                        * login related PDU's exp_statsn is handled in
+                        * userspace
+                        */
+                       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,
                                            (void*)&mtask, sizeof(void*));
                        break;
                case ISCSI_OP_SCSI_TMFUNC_RSP:
-                       rc = iscsi_check_assign_cmdsn(session,
-                                                (struct iscsi_nopin*)hdr);
-                       if (rc)
-                               break;
-
                        if (datalen) {
                                rc = ISCSI_ERR_PROTO;
                                break;
                        }
-                       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;
                        }
-                       rc = iscsi_check_assign_cmdsn(session,
-                                               (struct iscsi_nopin*)hdr);
-                       if (rc)
-                               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,
@@ -390,20 +460,29 @@ 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() */
                        rc = ISCSI_ERR_BAD_OPCODE;
                        break;
@@ -414,6 +493,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        } else
                rc = ISCSI_ERR_BAD_ITT;
 
+done:
        return rc;
 }
 EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
@@ -441,7 +521,7 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
                if ((hdr->itt & ISCSI_AGE_MASK) !=
                    (session->age << ISCSI_AGE_SHIFT)) {
-                       printk(KERN_ERR "iscsi_tcp: received itt %x expected "
+                       printk(KERN_ERR "iscsi: received itt %x expected "
                                "session age (%x)\n", hdr->itt,
                                session->age & ISCSI_AGE_MASK);
                        return ISCSI_ERR_BAD_ITT;
@@ -449,7 +529,7 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 
                if ((hdr->itt & ISCSI_CID_MASK) !=
                    (conn->id << ISCSI_CID_SHIFT)) {
-                       printk(KERN_ERR "iscsi_tcp: received itt %x, expected "
+                       printk(KERN_ERR "iscsi: received itt %x, expected "
                                "CID (%x)\n", hdr->itt, conn->id);
                        return ISCSI_ERR_BAD_ITT;
                }
@@ -461,14 +541,14 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                ctask = session->cmds[itt];
 
                if (!ctask->sc) {
-                       printk(KERN_INFO "iscsi_tcp: dropping ctask with "
+                       printk(KERN_INFO "iscsi: dropping ctask with "
                               "itt 0x%x\n", ctask->itt);
                        /* force drop */
                        return ISCSI_ERR_NO_SCSI_CMD;
                }
 
                if (ctask->sc->SCp.phase != session->age) {
-                       printk(KERN_ERR "iscsi_tcp: ctask's session age %d, "
+                       printk(KERN_ERR "iscsi: ctask's session age %d, "
                                "expected %d\n", ctask->sc->SCp.phase,
                                session->age);
                        return ISCSI_ERR_SESSION_FAILED;
@@ -486,7 +566,12 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
        unsigned long flags;
 
        spin_lock_irqsave(&session->lock, flags);
-       if (session->conn_cnt == 1 || session->leadconn == conn)
+       if (session->state == ISCSI_STATE_FAILED) {
+               spin_unlock_irqrestore(&session->lock, flags);
+               return;
+       }
+
+       if (conn->stop_stage == 0)
                session->state = ISCSI_STATE_FAILED;
        spin_unlock_irqrestore(&session->lock, flags);
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
@@ -507,10 +592,11 @@ EXPORT_SYMBOL_GPL(iscsi_conn_failure);
 static int iscsi_data_xmit(struct iscsi_conn *conn)
 {
        struct iscsi_transport *tt;
+       int rc = 0;
 
        if (unlikely(conn->suspend_tx)) {
                debug_scsi("conn %d Tx suspended!\n", conn->id);
-               return 0;
+               return -ENODATA;
        }
        tt = conn->session->tt;
 
@@ -530,13 +616,17 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
        BUG_ON(conn->ctask && conn->mtask);
 
        if (conn->ctask) {
-               if (tt->xmit_cmd_task(conn, 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 */
                conn->ctask = NULL;
        }
        if (conn->mtask) {
-               if (tt->xmit_mgmt_task(conn, conn->mtask))
+               rc = tt->xmit_mgmt_task(conn, conn->mtask);
+               if (rc)
                        goto again;
                /* done with this in-progress mtask */
                conn->mtask = NULL;
@@ -546,9 +636,12 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
         if (unlikely(__kfifo_len(conn->immqueue))) {
                while (__kfifo_get(conn->immqueue, (void*)&conn->mtask,
                                   sizeof(void*))) {
+                       spin_lock_bh(&conn->session->lock);
                        list_add_tail(&conn->mtask->running,
                                      &conn->mgmt_run_list);
-                       if (tt->xmit_mgmt_task(conn, conn->mtask))
+                       spin_unlock_bh(&conn->session->lock);
+                       rc = tt->xmit_mgmt_task(conn, conn->mtask);
+                       if (rc)
                                goto again;
                }
                /* done with this mtask */
@@ -556,17 +649,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
                 */
-               if (list_empty(&conn->ctask->running))
-                       list_add_tail(&conn->ctask->running, &conn->run_list);
-               if (tt->xmit_cmd_task(conn, conn->ctask))
+               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;
 
@@ -574,34 +681,38 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
         if (unlikely(__kfifo_len(conn->mgmtqueue))) {
                while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask,
                                   sizeof(void*))) {
+                       spin_lock_bh(&conn->session->lock);
                        list_add_tail(&conn->mtask->running,
                                      &conn->mgmt_run_list);
-                       if (tt->xmit_mgmt_task(conn, conn->mtask))
+                       spin_unlock_bh(&conn->session->lock);
+                       rc = tt->xmit_mgmt_task(conn, conn->mtask);
+                       if (rc)
                                goto again;
                }
                /* done with this mtask */
                conn->mtask = NULL;
        }
 
-       return 0;
+       return -ENODATA;
 
 again:
        if (unlikely(conn->suspend_tx))
-               return 0;
+               return -ENODATA;
 
-       return -EAGAIN;
+       return rc;
 }
 
 static void iscsi_xmitworker(void *data)
 {
        struct iscsi_conn *conn = data;
-
+       int rc;
        /*
         * serialize Xmit worker on a per-connection basis.
         */
        mutex_lock(&conn->xmitmutex);
-       if (iscsi_data_xmit(conn))
-               scsi_queue_work(conn->session->host, &conn->xmitwork);
+       do {
+               rc = iscsi_data_xmit(conn);
+       } while (rc >= 0 || rc == -EAGAIN);
        mutex_unlock(&conn->xmitmutex);
 }
 
@@ -610,7 +721,9 @@ enum {
        FAILURE_SESSION_FAILED,
        FAILURE_SESSION_FREED,
        FAILURE_WINDOW_CLOSED,
+       FAILURE_OOM,
        FAILURE_SESSION_TERMINATE,
+       FAILURE_SESSION_IN_RECOVERY,
        FAILURE_SESSION_RECOVERY_TIMEOUT,
 };
 
@@ -630,18 +743,30 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
 
        spin_lock(&session->lock);
 
-       if (session->state != ISCSI_STATE_LOGGED_IN) {
-               if (session->recovery_failed) {
-                       reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
-                       goto fault;
-               } else if (session->state == ISCSI_STATE_FAILED) {
-                       reason = FAILURE_SESSION_FAILED;
+       /*
+        * ISCSI_STATE_FAILED is a temp. state. The recovery
+        * code will decide what is best to do with command queued
+        * during this time
+        */
+       if (session->state != ISCSI_STATE_LOGGED_IN &&
+           session->state != ISCSI_STATE_FAILED) {
+               /*
+                * to handle the race between when we set the recovery state
+                * and block the session we requeue here (commands could
+                * be entering our queuecommand while a block is starting
+                * up because the block code is not locked)
+                */
+               if (session->state == ISCSI_STATE_IN_RECOVERY) {
+                       reason = FAILURE_SESSION_IN_RECOVERY;
                        goto reject;
-               } else if (session->state == ISCSI_STATE_TERMINATE) {
-                       reason = FAILURE_SESSION_TERMINATE;
-                       goto fault;
                }
-               reason = FAILURE_SESSION_FREED;
+
+               if (session->state == ISCSI_STATE_RECOVERY_FAILED)
+                       reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+               else if (session->state == ISCSI_STATE_TERMINATE)
+                       reason = FAILURE_SESSION_TERMINATE;
+               else
+                       reason = FAILURE_SESSION_FREED;
                goto fault;
        }
 
@@ -655,10 +780,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;
@@ -668,7 +799,7 @@ 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",
                sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
@@ -686,7 +817,7 @@ reject:
 
 fault:
        spin_unlock(&session->lock);
-       printk(KERN_ERR "iscsi_tcp: cmd 0x%x is not queued (%d)\n",
+       printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n",
               sc->cmnd[0], reason);
        sc->result = (DID_NO_CONNECT << 16);
        sc->resid = sc->request_bufflen;
@@ -727,9 +858,10 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                 */
                mtask = conn->login_mtask;
        else {
-               BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
-               BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
+               BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
+               BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
 
+               nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
                if (!__kfifo_get(session->mgmtpool.queue,
                                 (void*)&mtask, sizeof(void*))) {
                        spin_unlock_bh(&session->lock);
@@ -738,7 +870,7 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        }
 
        /*
-        * pre-format CmdSN and ExpStatSN for outgoing PDU.
+        * pre-format CmdSN for outgoing PDU.
         */
        if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
                hdr->itt = mtask->itt | (conn->id << ISCSI_CID_SHIFT) |
@@ -751,8 +883,6 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                /* do not advance CmdSN */
                nop->cmdsn = cpu_to_be32(session->cmdsn);
 
-       nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
-
        if (data_size) {
                memcpy(mtask->data, data, data_size);
                mtask->data_count = data_size;
@@ -803,7 +933,7 @@ void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
 
        spin_lock_bh(&session->lock);
        if (session->state != ISCSI_STATE_LOGGED_IN) {
-               session->recovery_failed = 1;
+               session->state = ISCSI_STATE_RECOVERY_FAILED;
                if (conn)
                        wake_up(&conn->ehwait);
        }
@@ -838,26 +968,20 @@ failed:
         * we drop the lock here but the leadconn cannot be destoyed while
         * we are in the scsi eh
         */
-       if (fail_session) {
+       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);
+                                session->state == ISCSI_STATE_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");
+               printk(KERN_INFO "iscsi: host reset succeeded\n");
        else
                goto failed;
        spin_unlock_bh(&session->lock);
@@ -940,8 +1064,7 @@ static int iscsi_exec_abort_task(struct scsi_cmnd *sc,
        wait_event_interruptible(conn->ehwait,
                                 sc->SCp.phase != session->age ||
                                 session->state != ISCSI_STATE_LOGGED_IN ||
-                                conn->tmabort_state != TMABORT_INITIAL ||
-                                session->recovery_failed);
+                                conn->tmabort_state != TMABORT_INITIAL);
        if (signal_pending(current))
                flush_signals(current);
        del_timer_sync(&conn->tmabort_timer);
@@ -953,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");                   \
-                       break;                                          \
-               }                                                       \
-                                                                       \
-               __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);
+
+       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;
+               }
 
-iscsi_remove_task(mgmt_task);
-iscsi_remove_task(cmd_task);
+               __kfifo_put(fifo, (void*)&task, sizeof(void*));
+       }
+       return NULL;
+}
 
 static int iscsi_ctask_mtask_cleanup(struct iscsi_cmd_task *ctask)
 {
@@ -1003,15 +1122,14 @@ 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;
+       iscsi_ctask_mtask_cleanup(ctask);
+
        sc->result = err;
        sc->resid = sc->request_bufflen;
-       iscsi_complete_command(conn->session, ctask);
+       __iscsi_put_ctask(ctask);
 }
 
 int iscsi_eh_abort(struct scsi_cmnd *sc)
@@ -1019,7 +1137,6 @@ 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;
        int rc;
 
        conn->eh_abort_cnt++;
@@ -1037,27 +1154,21 @@ 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) {
-               printk(KERN_INFO "sc %p itt 0x%x partially sent. Failing "
-                      "abort\n", sc, ctask->itt);
+               printk(KERN_INFO "iscsi: sc %p itt 0x%x partially sent. "
+                      "Failing abort\n", sc, ctask->itt);
                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;
 
@@ -1065,25 +1176,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);
 
@@ -1097,6 +1214,7 @@ success:
        spin_unlock(&session->lock);
        write_unlock_bh(conn->recv_lock);
 
+success_rel_mutex:
        mutex_unlock(&conn->xmitmutex);
        return SUCCESS;
 
@@ -1239,6 +1357,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);
@@ -1258,33 +1377,27 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
                if (mgmt_task_size)
                        mtask->dd_data = &mtask[1];
                mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i;
-               mtask->data = kmalloc(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH,
-                                    GFP_KERNEL);
-               if (!mtask->data) {
-                       int j;
-
-                       for (j = 0; j < cmd_i; j++)
-                               kfree(session->mgmt_cmds[j]->data);
-                       goto immdata_alloc_fail;
-               }
+               INIT_LIST_HEAD(&mtask->running);
        }
 
        if (scsi_add_host(shost, NULL))
                goto add_host_fail;
 
+       if (!try_module_get(iscsit->owner))
+               goto cls_session_fail;
+
        cls_session = iscsi_create_session(shost, iscsit, 0);
        if (!cls_session)
-               goto cls_session_fail;
+               goto module_put;
        *(unsigned long*)shost->hostdata = (unsigned long)cls_session;
 
        return cls_session;
 
+module_put:
+       module_put(iscsit->owner);
 cls_session_fail:
        scsi_remove_host(shost);
 add_host_fail:
-       for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++)
-               kfree(session->mgmt_cmds[cmd_i]->data);
-immdata_alloc_fail:
        iscsi_pool_free(&session->mgmtpool, (void**)session->mgmt_cmds);
 mgmtpool_alloc_fail:
        iscsi_pool_free(&session->cmdpool, (void**)session->cmds);
@@ -1305,18 +1418,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);
-       int cmd_i;
+       struct module *owner = cls_session->transport->owner;
 
        scsi_remove_host(shost);
 
-       for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++)
-               kfree(session->mgmt_cmds[cmd_i]->data);
-
        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(owner);
 }
 EXPORT_SYMBOL_GPL(iscsi_session_teardown);
 
@@ -1331,6 +1444,7 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        struct iscsi_session *session = class_to_transport_session(cls_session);
        struct iscsi_conn *conn;
        struct iscsi_cls_conn *cls_conn;
+       char *data;
 
        cls_conn = iscsi_create_conn(cls_session, conn_idx);
        if (!cls_conn)
@@ -1346,12 +1460,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*),
@@ -1376,19 +1485,25 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        }
        spin_unlock_bh(&session->lock);
 
+       data = kmalloc(DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH, GFP_KERNEL);
+       if (!data)
+               goto login_mtask_data_alloc_fail;
+       conn->login_mtask->data = conn->data = data;
+
        init_timer(&conn->tmabort_timer);
        mutex_init(&conn->xmitmutex);
        init_waitqueue_head(&conn->ehwait);
 
        return cls_conn;
 
+login_mtask_data_alloc_fail:
+       __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+                   sizeof(void*));
 login_mtask_alloc_fail:
        kfifo_free(conn->mgmtqueue);
 mgmtqueue_alloc_fail:
        kfifo_free(conn->immqueue);
 immqueue_alloc_fail:
-       kfifo_free(conn->xmitqueue);
-xmitqueue_alloc_fail:
        iscsi_destroy_conn(cls_conn);
        return NULL;
 }
@@ -1407,14 +1522,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
        struct iscsi_session *session = conn->session;
        unsigned long flags;
 
-       mutex_lock(&conn->xmitmutex);
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-       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);
-       }
+       mutex_lock(&conn->xmitmutex);
 
        spin_lock_bh(&session->lock);
        conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
@@ -1441,8 +1550,9 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
                }
                spin_unlock_irqrestore(session->host->host_lock, flags);
                msleep_interruptible(500);
-               printk("conn_destroy(): host_busy %d host_failed %d\n",
-                       session->host->host_busy, session->host->host_failed);
+               printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d "
+                      "host_failed %d\n", session->host->host_busy,
+                      session->host->host_failed);
                /*
                 * force eh_abort() to unblock
                 */
@@ -1450,6 +1560,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
        }
 
        spin_lock_bh(&session->lock);
+       kfree(conn->data);
+       kfree(conn->persistent_address);
        __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
                    sizeof(void*));
        list_del(&conn->item);
@@ -1464,7 +1576,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);
 
@@ -1477,11 +1588,18 @@ 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->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;
@@ -1492,24 +1610,16 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
                 * unblock eh_abort() if it is blocked. re-try all
                 * commands after successful recovery
                 */
-               session->conn_cnt++;
                conn->stop_stage = 0;
                conn->tmabort_state = TMABORT_INITIAL;
                session->age++;
-               session->recovery_failed = 0;
                spin_unlock_bh(&session->lock);
 
                iscsi_unblock_session(session_to_cls(session));
                wake_up(&conn->ehwait);
                return 0;
        case STOP_CONN_TERM:
-               session->conn_cnt++;
-               conn->stop_stage = 0;
-               break;
-       case STOP_CONN_SUSPEND:
                conn->stop_stage = 0;
-               clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
-               clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
                break;
        default:
                break;
@@ -1538,10 +1648,11 @@ flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
        /* 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(&mtask->running);
+
                if (mtask == conn->login_mtask)
                        continue;
-               list_del(&mtask->running);
-               __kfifo_put(session->mgmtpool.queue, (void*)&conn->mtask,
+               __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
                           sizeof(void*));
        }
 
@@ -1554,7 +1665,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);
@@ -1570,41 +1681,38 @@ static void fail_all_commands(struct iscsi_conn *conn)
        conn->ctask = NULL;
 }
 
-void iscsi_start_session_recovery(struct iscsi_session *session,
-                                 struct iscsi_conn *conn, int flag)
+static void iscsi_start_session_recovery(struct iscsi_session *session,
+                                        struct iscsi_conn *conn, int flag)
 {
+       int old_stop_stage;
+
        spin_lock_bh(&session->lock);
-       if (conn->stop_stage == STOP_CONN_RECOVER ||
-           conn->stop_stage == STOP_CONN_TERM) {
+       if (conn->stop_stage == STOP_CONN_TERM) {
                spin_unlock_bh(&session->lock);
                return;
        }
-       conn->stop_stage = flag;
-       spin_unlock_bh(&session->lock);
 
-       if (session->tt->suspend_conn_recv)
-               session->tt->suspend_conn_recv(conn);
+       /*
+        * When this is called for the in_login state, we only want to clean
+        * up the login task and connection. We do not need to block and set
+        * the recovery state again
+        */
+       if (flag == STOP_CONN_TERM)
+               session->state = ISCSI_STATE_TERMINATE;
+       else if (conn->stop_stage != STOP_CONN_RECOVER)
+               session->state = ISCSI_STATE_IN_RECOVERY;
 
-       mutex_lock(&conn->xmitmutex);
-       spin_lock_bh(&session->lock);
+       old_stop_stage = conn->stop_stage;
+       conn->stop_stage = flag;
        conn->c_stage = ISCSI_CONN_STOPPED;
        set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
-
-       session->conn_cnt--;
-       if (session->conn_cnt == 0 || session->leadconn == conn)
-               session->state = ISCSI_STATE_FAILED;
-
        spin_unlock_bh(&session->lock);
 
-       session->tt->terminate_conn(conn);
-       /*
-        * flush queues.
-        */
-       spin_lock_bh(&session->lock);
-       fail_all_commands(conn);
-       flush_control_queues(session, conn);
-       spin_unlock_bh(&session->lock);
+       write_lock_bh(conn->recv_lock);
+       set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+       write_unlock_bh(conn->recv_lock);
 
+       mutex_lock(&conn->xmitmutex);
        /*
         * for connection level recovery we should not calculate
         * header digest. conn->hdr_size used for optimization
@@ -1614,13 +1722,23 @@ void iscsi_start_session_recovery(struct iscsi_session *session,
        if (flag == STOP_CONN_RECOVER) {
                conn->hdrdgst_en = 0;
                conn->datadgst_en = 0;
-
-               if (session->state == ISCSI_STATE_FAILED)
+               if (session->state == ISCSI_STATE_IN_RECOVERY &&
+                   old_stop_stage != STOP_CONN_RECOVER) {
+                       debug_scsi("blocking session\n");
                        iscsi_block_session(session_to_cls(session));
+               }
        }
+
+       /*
+        * flush queues.
+        */
+       spin_lock_bh(&session->lock);
+       fail_all_commands(conn);
+       flush_control_queues(session, conn);
+       spin_unlock_bh(&session->lock);
+
        mutex_unlock(&conn->xmitmutex);
 }
-EXPORT_SYMBOL_GPL(iscsi_start_session_recovery);
 
 void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
 {
@@ -1631,23 +1749,9 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
        case STOP_CONN_RECOVER:
        case STOP_CONN_TERM:
                iscsi_start_session_recovery(session, conn, flag);
-               return;
-       case STOP_CONN_SUSPEND:
-               if (session->tt->suspend_conn_recv)
-                       session->tt->suspend_conn_recv(conn);
-
-               mutex_lock(&conn->xmitmutex);
-               spin_lock_bh(&session->lock);
-
-               conn->stop_stage = flag;
-               conn->c_stage = ISCSI_CONN_STOPPED;
-               set_bit(ISCSI_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);
+               printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);
        }
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_stop);
@@ -1664,7 +1768,7 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
                if (tmp == conn) {
                        if (conn->c_stage != ISCSI_CONN_STOPPED ||
                            conn->stop_stage == STOP_CONN_TERM) {
-                               printk(KERN_ERR "iscsi_tcp: can't bind "
+                               printk(KERN_ERR "iscsi: can't bind "
                                       "non-stopped connection (%d:%d)\n",
                                       conn->c_stage, conn->stop_stage);
                                spin_unlock_bh(&session->lock);
@@ -1692,6 +1796,185 @@ int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_bind);
 
+
+int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
+                   enum iscsi_param param, char *buf, int buflen)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct iscsi_session *session = conn->session;
+       uint32_t value;
+
+       switch(param) {
+       case ISCSI_PARAM_MAX_RECV_DLENGTH:
+               sscanf(buf, "%d", &conn->max_recv_dlength);
+               break;
+       case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+               sscanf(buf, "%d", &conn->max_xmit_dlength);
+               break;
+       case ISCSI_PARAM_HDRDGST_EN:
+               sscanf(buf, "%d", &conn->hdrdgst_en);
+               break;
+       case ISCSI_PARAM_DATADGST_EN:
+               sscanf(buf, "%d", &conn->datadgst_en);
+               break;
+       case ISCSI_PARAM_INITIAL_R2T_EN:
+               sscanf(buf, "%d", &session->initial_r2t_en);
+               break;
+       case ISCSI_PARAM_MAX_R2T:
+               sscanf(buf, "%d", &session->max_r2t);
+               break;
+       case ISCSI_PARAM_IMM_DATA_EN:
+               sscanf(buf, "%d", &session->imm_data_en);
+               break;
+       case ISCSI_PARAM_FIRST_BURST:
+               sscanf(buf, "%d", &session->first_burst);
+               break;
+       case ISCSI_PARAM_MAX_BURST:
+               sscanf(buf, "%d", &session->max_burst);
+               break;
+       case ISCSI_PARAM_PDU_INORDER_EN:
+               sscanf(buf, "%d", &session->pdu_inorder_en);
+               break;
+       case ISCSI_PARAM_DATASEQ_INORDER_EN:
+               sscanf(buf, "%d", &session->dataseq_inorder_en);
+               break;
+       case ISCSI_PARAM_ERL:
+               sscanf(buf, "%d", &session->erl);
+               break;
+       case ISCSI_PARAM_IFMARKER_EN:
+               sscanf(buf, "%d", &value);
+               BUG_ON(value);
+               break;
+       case ISCSI_PARAM_OFMARKER_EN:
+               sscanf(buf, "%d", &value);
+               BUG_ON(value);
+               break;
+       case ISCSI_PARAM_EXP_STATSN:
+               sscanf(buf, "%u", &conn->exp_statsn);
+               break;
+       case ISCSI_PARAM_TARGET_NAME:
+               /* this should not change between logins */
+               if (session->targetname)
+                       break;
+
+               session->targetname = kstrdup(buf, GFP_KERNEL);
+               if (!session->targetname)
+                       return -ENOMEM;
+               break;
+       case ISCSI_PARAM_TPGT:
+               sscanf(buf, "%d", &session->tpgt);
+               break;
+       case ISCSI_PARAM_PERSISTENT_PORT:
+               sscanf(buf, "%d", &conn->persistent_port);
+               break;
+       case ISCSI_PARAM_PERSISTENT_ADDRESS:
+               /*
+                * this is the address returned in discovery so it should
+                * not change between logins.
+                */
+               if (conn->persistent_address)
+                       break;
+
+               conn->persistent_address = kstrdup(buf, GFP_KERNEL);
+               if (!conn->persistent_address)
+                       return -ENOMEM;
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_set_param);
+
+int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
+                           enum iscsi_param param, char *buf)
+{
+       struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+       struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+       int len;
+
+       switch(param) {
+       case ISCSI_PARAM_INITIAL_R2T_EN:
+               len = sprintf(buf, "%d\n", session->initial_r2t_en);
+               break;
+       case ISCSI_PARAM_MAX_R2T:
+               len = sprintf(buf, "%hu\n", session->max_r2t);
+               break;
+       case ISCSI_PARAM_IMM_DATA_EN:
+               len = sprintf(buf, "%d\n", session->imm_data_en);
+               break;
+       case ISCSI_PARAM_FIRST_BURST:
+               len = sprintf(buf, "%u\n", session->first_burst);
+               break;
+       case ISCSI_PARAM_MAX_BURST:
+               len = sprintf(buf, "%u\n", session->max_burst);
+               break;
+       case ISCSI_PARAM_PDU_INORDER_EN:
+               len = sprintf(buf, "%d\n", session->pdu_inorder_en);
+               break;
+       case ISCSI_PARAM_DATASEQ_INORDER_EN:
+               len = sprintf(buf, "%d\n", session->dataseq_inorder_en);
+               break;
+       case ISCSI_PARAM_ERL:
+               len = sprintf(buf, "%d\n", session->erl);
+               break;
+       case ISCSI_PARAM_TARGET_NAME:
+               len = sprintf(buf, "%s\n", session->targetname);
+               break;
+       case ISCSI_PARAM_TPGT:
+               len = sprintf(buf, "%d\n", session->tpgt);
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_session_get_param);
+
+int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
+                        enum iscsi_param param, char *buf)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       int len;
+
+       switch(param) {
+       case ISCSI_PARAM_MAX_RECV_DLENGTH:
+               len = sprintf(buf, "%u\n", conn->max_recv_dlength);
+               break;
+       case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+               len = sprintf(buf, "%u\n", conn->max_xmit_dlength);
+               break;
+       case ISCSI_PARAM_HDRDGST_EN:
+               len = sprintf(buf, "%d\n", conn->hdrdgst_en);
+               break;
+       case ISCSI_PARAM_DATADGST_EN:
+               len = sprintf(buf, "%d\n", conn->datadgst_en);
+               break;
+       case ISCSI_PARAM_IFMARKER_EN:
+               len = sprintf(buf, "%d\n", conn->ifmarker_en);
+               break;
+       case ISCSI_PARAM_OFMARKER_EN:
+               len = sprintf(buf, "%d\n", conn->ofmarker_en);
+               break;
+       case ISCSI_PARAM_EXP_STATSN:
+               len = sprintf(buf, "%u\n", conn->exp_statsn);
+               break;
+       case ISCSI_PARAM_PERSISTENT_PORT:
+               len = sprintf(buf, "%d\n", conn->persistent_port);
+               break;
+       case ISCSI_PARAM_PERSISTENT_ADDRESS:
+               len = sprintf(buf, "%s\n", conn->persistent_address);
+               break;
+       default:
+               return -ENOSYS;
+       }
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
+
 MODULE_AUTHOR("Mike Christie");
 MODULE_DESCRIPTION("iSCSI library functions");
 MODULE_LICENSE("GPL");