[SCSI] megaraid_sas: Fix the frame count calculation
[safe/jmp/linux-2.6] / drivers / scsi / libiscsi.c
index 5205ef2..bdd7de7 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 #include <linux/kfifo.h>
 #include <linux/delay.h>
+#include <linux/log2.h>
 #include <asm/unaligned.h>
 #include <net/tcp.h>
 #include <scsi/scsi_cmnd.h>
@@ -156,20 +157,19 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
        rc = iscsi_add_hdr(ctask, sizeof(*hdr));
        if (rc)
                return rc;
-        hdr->opcode = ISCSI_OP_SCSI_CMD;
-        hdr->flags = ISCSI_ATTR_SIMPLE;
-        int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
-        hdr->itt = build_itt(ctask->itt, conn->id, session->age);
-        hdr->data_length = cpu_to_be32(scsi_bufflen(sc));
-        hdr->cmdsn = cpu_to_be32(session->cmdsn);
-        session->cmdsn++;
-        hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
-        memcpy(hdr->cdb, sc->cmnd, sc->cmd_len);
+       hdr->opcode = ISCSI_OP_SCSI_CMD;
+       hdr->flags = ISCSI_ATTR_SIMPLE;
+       int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
+       hdr->itt = build_itt(ctask->itt, session->age);
+       hdr->data_length = cpu_to_be32(scsi_bufflen(sc));
+       hdr->cmdsn = cpu_to_be32(session->cmdsn);
+       session->cmdsn++;
+       hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
+       memcpy(hdr->cdb, sc->cmnd, sc->cmd_len);
        if (sc->cmd_len < MAX_COMMAND_SIZE)
                memset(&hdr->cdb[sc->cmd_len], 0,
                        MAX_COMMAND_SIZE - sc->cmd_len);
 
-       ctask->data_count = 0;
        ctask->imm_count = 0;
        if (sc->sc_data_direction == DMA_TO_DEVICE) {
                hdr->flags |= ISCSI_FLAG_CMD_WRITE;
@@ -198,9 +198,9 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
                        else
                                ctask->imm_count = min(scsi_bufflen(sc),
                                                        conn->max_xmit_dlength);
-                       hton24(ctask->hdr->dlength, ctask->imm_count);
+                       hton24(hdr->dlength, ctask->imm_count);
                } else
-                       zero_data(ctask->hdr->dlength);
+                       zero_data(hdr->dlength);
 
                if (!session->initial_r2t_en) {
                        ctask->unsol_count = min((session->first_burst),
@@ -210,7 +210,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
 
                if (!ctask->unsol_count)
                        /* No unsolicit Data-Out's */
-                       ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
+                       hdr->flags |= ISCSI_FLAG_CMD_FINAL;
        } else {
                hdr->flags |= ISCSI_FLAG_CMD_FINAL;
                zero_data(hdr->dlength);
@@ -228,13 +228,15 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
        WARN_ON(hdrlength >= 256);
        hdr->hlength = hdrlength & 0xFF;
 
-       conn->scsicmd_pdus_cnt++;
+       if (conn->session->tt->init_cmd_task(conn->ctask))
+               return EIO;
 
-        debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "
+       conn->scsicmd_pdus_cnt++;
+       debug_scsi("iscsi prep [%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",
+               sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
                conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc),
-                session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+               session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
        return 0;
 }
 
@@ -248,13 +250,16 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
  */
 static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
 {
-       struct iscsi_session *session = ctask->conn->session;
+       struct iscsi_conn *conn = ctask->conn;
+       struct iscsi_session *session = 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;
+       if (conn->ctask == ctask)
+               conn->ctask = NULL;
        list_del_init(&ctask->running);
        __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
        sc->scsi_done(sc);
@@ -313,11 +318,70 @@ void iscsi_free_mgmt_task(struct iscsi_conn *conn,
        list_del_init(&mtask->running);
        if (conn->login_mtask == mtask)
                return;
+
+       if (conn->ping_mtask == mtask)
+               conn->ping_mtask = NULL;
        __kfifo_put(conn->session->mgmtpool.queue,
                    (void*)&mtask, sizeof(void*));
 }
 EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
 
+static struct iscsi_mgmt_task *
+__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+                     char *data, uint32_t data_size)
+{
+       struct iscsi_session *session = conn->session;
+       struct iscsi_mgmt_task *mtask;
+
+       if (session->state == ISCSI_STATE_TERMINATE)
+               return NULL;
+
+       if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
+           hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+               /*
+                * Login and Text are sent serially, in
+                * request-followed-by-response sequence.
+                * Same mtask can be used. Same ITT must be used.
+                * Note that login_mtask is preallocated at conn_create().
+                */
+               mtask = conn->login_mtask;
+       else {
+               BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
+               BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
+
+               if (!__kfifo_get(session->mgmtpool.queue,
+                                (void*)&mtask, sizeof(void*)))
+                       return NULL;
+       }
+
+       if (data_size) {
+               memcpy(mtask->data, data, data_size);
+               mtask->data_count = data_size;
+       } else
+               mtask->data_count = 0;
+
+       memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
+       INIT_LIST_HEAD(&mtask->running);
+       list_add_tail(&mtask->running, &conn->mgmtqueue);
+       return mtask;
+}
+
+int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
+                       char *data, uint32_t data_size)
+{
+       struct iscsi_conn *conn = cls_conn->dd_data;
+       struct iscsi_session *session = conn->session;
+       int err = 0;
+
+       spin_lock_bh(&session->lock);
+       if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
+               err = -EPERM;
+       spin_unlock_bh(&session->lock);
+       scsi_queue_work(session->host, &conn->xmitwork);
+       return err;
+}
+EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
+
 /**
  * iscsi_cmd_rsp - SCSI Command Response processing
  * @conn: iscsi connection
@@ -352,8 +416,9 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 
                if (datalen < 2) {
 invalid_datalen:
-                       printk(KERN_ERR "iscsi: Got CHECK_CONDITION but "
-                              "invalid data buffer size of %d\n", datalen);
+                       iscsi_conn_printk(KERN_ERR,  conn,
+                                        "Got CHECK_CONDITION but invalid data "
+                                        "buffer size of %d\n", datalen);
                        sc->result = DID_BAD_TARGET << 16;
                        goto out;
                }
@@ -409,6 +474,39 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
        wake_up(&conn->ehwait);
 }
 
+static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
+{
+        struct iscsi_nopout hdr;
+       struct iscsi_mgmt_task *mtask;
+
+       if (!rhdr && conn->ping_mtask)
+               return;
+
+       memset(&hdr, 0, sizeof(struct iscsi_nopout));
+       hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE;
+       hdr.flags = ISCSI_FLAG_CMD_FINAL;
+
+       if (rhdr) {
+               memcpy(hdr.lun, rhdr->lun, 8);
+               hdr.ttt = rhdr->ttt;
+               hdr.itt = RESERVED_ITT;
+       } else
+               hdr.ttt = RESERVED_ITT;
+
+       mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+       if (!mtask) {
+               iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
+               return;
+       }
+
+       /* only track our nops */
+       if (!rhdr) {
+               conn->ping_mtask = mtask;
+               conn->last_ping = jiffies;
+       }
+       scsi_queue_work(conn->session->host, &conn->xmitwork);
+}
+
 static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                               char *data, int datalen)
 {
@@ -425,9 +523,10 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
                        memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
                        itt = get_itt(rejected_pdu.itt);
-                       printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "
-                               "due to DataDigest error.\n", itt,
-                               rejected_pdu.opcode);
+                       iscsi_conn_printk(KERN_ERR, conn,
+                                         "itt 0x%x had pdu (op 0x%x) rejected "
+                                         "due to DataDigest error.\n", itt,
+                                         rejected_pdu.opcode);
                }
        }
        return 0;
@@ -444,8 +543,8 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
  * queuecommand or send generic. session lock must be held and verify
  * itt must have been called.
  */
-int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
-                        char *data, int datalen)
+static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+                               char *data, int datalen)
 {
        struct iscsi_session *session = conn->session;
        int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0;
@@ -453,6 +552,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        struct iscsi_mgmt_task *mtask;
        uint32_t itt;
 
+       conn->last_recv = jiffies;
        if (hdr->itt != RESERVED_ITT)
                itt = get_itt(hdr->itt);
        else
@@ -520,14 +620,22 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        iscsi_free_mgmt_task(conn, mtask);
                        break;
                case ISCSI_OP_NOOP_IN:
-                       if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) {
+                       if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) ||
+                           datalen) {
                                rc = ISCSI_ERR_PROTO;
                                break;
                        }
                        conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
 
-                       if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
-                               rc = ISCSI_ERR_CONN_FAILED;
+                       if (conn->ping_mtask != mtask) {
+                               /*
+                                * If this is not in response to one of our
+                                * nops then it must be from userspace.
+                                */
+                               if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
+                                                  datalen))
+                                       rc = ISCSI_ERR_CONN_FAILED;
+                       }
                        iscsi_free_mgmt_task(conn, mtask);
                        break;
                default:
@@ -547,8 +655,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                        if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))
                                break;
 
-                       if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0))
-                               rc = ISCSI_ERR_CONN_FAILED;
+                       iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr);
                        break;
                case ISCSI_OP_REJECT:
                        rc = iscsi_handle_reject(conn, hdr, data, datalen);
@@ -567,7 +674,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 
        return rc;
 }
-EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
 
 int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                       char *data, int datalen)
@@ -592,18 +698,13 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
        if (hdr->itt != RESERVED_ITT) {
                if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=
                    (session->age << ISCSI_AGE_SHIFT)) {
-                       printk(KERN_ERR "iscsi: received itt %x expected "
-                               "session age (%x)\n", (__force u32)hdr->itt,
-                               session->age & ISCSI_AGE_MASK);
+                       iscsi_conn_printk(KERN_ERR, conn,
+                                         "received itt %x expected session "
+                                         "age (%x)\n", (__force u32)hdr->itt,
+                                         session->age & ISCSI_AGE_MASK);
                        return ISCSI_ERR_BAD_ITT;
                }
 
-               if (((__force u32)hdr->itt & ISCSI_CID_MASK) !=
-                   (conn->id << ISCSI_CID_SHIFT)) {
-                       printk(KERN_ERR "iscsi: received itt %x, expected "
-                               "CID (%x)\n", (__force u32)hdr->itt, conn->id);
-                       return ISCSI_ERR_BAD_ITT;
-               }
                itt = get_itt(hdr->itt);
        } else
                itt = ~0U;
@@ -612,16 +713,17 @@ int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
                ctask = session->cmds[itt];
 
                if (!ctask->sc) {
-                       printk(KERN_INFO "iscsi: dropping ctask with "
-                              "itt 0x%x\n", ctask->itt);
+                       iscsi_conn_printk(KERN_INFO, conn, "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: ctask's session age %d, "
-                               "expected %d\n", ctask->sc->SCp.phase,
-                               session->age);
+                       iscsi_conn_printk(KERN_ERR, conn,
+                                         "iscsi: ctask's session age %d, "
+                                         "expected %d\n", ctask->sc->SCp.phase,
+                                         session->age);
                        return ISCSI_ERR_SESSION_FAILED;
                }
        }
@@ -666,7 +768,7 @@ static void iscsi_prep_mtask(struct iscsi_conn *conn,
         */
        nop->cmdsn = cpu_to_be32(session->cmdsn);
        if (hdr->itt != RESERVED_ITT) {
-               hdr->itt = build_itt(mtask->itt, conn->id, session->age);
+               hdr->itt = build_itt(mtask->itt, session->age);
                /*
                 * TODO: We always use immediate, so we never hit this.
                 * If we start to send tmfs or nops as non-immediate then
@@ -817,14 +919,14 @@ check_mgmt:
                conn->ctask = list_entry(conn->xmitqueue.next,
                                         struct iscsi_cmd_task, running);
                if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
-                       fail_command(conn, conn->ctask, DID_NO_CONNECT << 16);
+                       fail_command(conn, conn->ctask, DID_IMM_RETRY << 16);
                        continue;
                }
                if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
                        fail_command(conn, conn->ctask, DID_ABORT << 16);
                        continue;
                }
-               conn->session->tt->init_cmd_task(conn->ctask);
+
                conn->ctask->state = ISCSI_TASK_RUNNING;
                list_move_tail(conn->xmitqueue.next, &conn->run_list);
                rc = iscsi_xmit_ctask(conn);
@@ -892,6 +994,7 @@ enum {
        FAILURE_SESSION_IN_RECOVERY,
        FAILURE_SESSION_RECOVERY_TIMEOUT,
        FAILURE_SESSION_LOGGING_OUT,
+       FAILURE_SESSION_NOT_READY,
 };
 
 int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
@@ -907,10 +1010,17 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
        sc->SCp.ptr = NULL;
 
        host = sc->device->host;
-       session = iscsi_hostdata(host->hostdata);
+       spin_unlock(host->host_lock);
 
+       session = iscsi_hostdata(host->hostdata);
        spin_lock(&session->lock);
 
+       reason = iscsi_session_chkready(session_to_cls(session));
+       if (reason) {
+               sc->result = reason;
+               goto fault;
+       }
+
        /*
         * ISCSI_STATE_FAILED is a temp. state. The recovery
         * code will decide what is best to do with command queued
@@ -924,23 +1034,26 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                 * 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;
-               }
-
                switch (session->state) {
+               case ISCSI_STATE_IN_RECOVERY:
+                       reason = FAILURE_SESSION_IN_RECOVERY;
+                       sc->result = DID_IMM_RETRY << 16;
+                       break;
+               case ISCSI_STATE_LOGGING_OUT:
+                       reason = FAILURE_SESSION_LOGGING_OUT;
+                       sc->result = DID_IMM_RETRY << 16;
+                       break;
                case ISCSI_STATE_RECOVERY_FAILED:
                        reason = FAILURE_SESSION_RECOVERY_TIMEOUT;
+                       sc->result = DID_NO_CONNECT << 16;
                        break;
                case ISCSI_STATE_TERMINATE:
                        reason = FAILURE_SESSION_TERMINATE;
-                       break;
-               case ISCSI_STATE_LOGGING_OUT:
-                       reason = FAILURE_SESSION_LOGGING_OUT;
+                       sc->result = DID_NO_CONNECT << 16;
                        break;
                default:
                        reason = FAILURE_SESSION_FREED;
+                       sc->result = DID_NO_CONNECT << 16;
                }
                goto fault;
        }
@@ -948,6 +1061,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
        conn = session->leadconn;
        if (!conn) {
                reason = FAILURE_SESSION_FREED;
+               sc->result = DID_NO_CONNECT << 16;
                goto fault;
        }
 
@@ -976,20 +1090,21 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
        spin_unlock(&session->lock);
 
        scsi_queue_work(host, &conn->xmitwork);
+       spin_lock(host->host_lock);
        return 0;
 
 reject:
        spin_unlock(&session->lock);
        debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason);
+       spin_lock(host->host_lock);
        return SCSI_MLQUEUE_HOST_BUSY;
 
 fault:
        spin_unlock(&session->lock);
-       printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n",
-              sc->cmnd[0], reason);
-       sc->result = (DID_NO_CONNECT << 16);
+       debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
        scsi_set_resid(sc, scsi_bufflen(sc));
        sc->scsi_done(sc);
+       spin_lock(host->host_lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(iscsi_queuecommand);
@@ -1003,62 +1118,6 @@ int iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
 }
 EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
 
-static struct iscsi_mgmt_task *
-__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
-                     char *data, uint32_t data_size)
-{
-       struct iscsi_session *session = conn->session;
-       struct iscsi_mgmt_task *mtask;
-
-       if (session->state == ISCSI_STATE_TERMINATE)
-               return NULL;
-
-       if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) ||
-           hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
-               /*
-                * Login and Text are sent serially, in
-                * request-followed-by-response sequence.
-                * Same mtask can be used. Same ITT must be used.
-                * Note that login_mtask is preallocated at conn_create().
-                */
-               mtask = conn->login_mtask;
-       else {
-               BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
-               BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
-
-               if (!__kfifo_get(session->mgmtpool.queue,
-                                (void*)&mtask, sizeof(void*)))
-                       return NULL;
-       }
-
-       if (data_size) {
-               memcpy(mtask->data, data, data_size);
-               mtask->data_count = data_size;
-       } else
-               mtask->data_count = 0;
-
-       memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
-       INIT_LIST_HEAD(&mtask->running);
-       list_add_tail(&mtask->running, &conn->mgmtqueue);
-       return mtask;
-}
-
-int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
-                       char *data, uint32_t data_size)
-{
-       struct iscsi_conn *conn = cls_conn->dd_data;
-       struct iscsi_session *session = conn->session;
-       int err = 0;
-
-       spin_lock_bh(&session->lock);
-       if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
-               err = -EPERM;
-       spin_unlock_bh(&session->lock);
-       scsi_queue_work(session->host, &conn->xmitwork);
-       return err;
-}
-EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
-
 void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
 {
        struct iscsi_session *session = class_to_transport_session(cls_session);
@@ -1109,7 +1168,8 @@ failed:
        mutex_lock(&session->eh_mutex);
        spin_lock_bh(&session->lock);
        if (session->state == ISCSI_STATE_LOGGED_IN)
-               printk(KERN_INFO "iscsi: host reset succeeded\n");
+               iscsi_session_printk(KERN_INFO, session,
+                                    "host reset succeeded\n");
        else
                goto failed;
        spin_unlock_bh(&session->lock);
@@ -1134,7 +1194,8 @@ static void iscsi_tmf_timedout(unsigned long data)
 }
 
 static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
-                                  struct iscsi_tm *hdr, int age)
+                                  struct iscsi_tm *hdr, int age,
+                                  int timeout)
 {
        struct iscsi_session *session = conn->session;
        struct iscsi_mgmt_task *mtask;
@@ -1149,7 +1210,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
                return -EPERM;
        }
        conn->tmfcmd_pdus_cnt++;
-       conn->tmf_timer.expires = 30 * HZ + jiffies;
+       conn->tmf_timer.expires = timeout * HZ + jiffies;
        conn->tmf_timer.function = iscsi_tmf_timedout;
        conn->tmf_timer.data = (unsigned long)conn;
        add_timer(&conn->tmf_timer);
@@ -1187,7 +1248,8 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
  * Fail commands. session lock held and recv side suspended and xmit
  * thread flushed
  */
-static void fail_all_commands(struct iscsi_conn *conn, unsigned lun)
+static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
+                             int error)
 {
        struct iscsi_cmd_task *ctask, *tmp;
 
@@ -1199,7 +1261,7 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun)
                if (lun == ctask->sc->device->lun || lun == -1) {
                        debug_scsi("failing pending sc %p itt 0x%x\n",
                                   ctask->sc, ctask->itt);
-                       fail_command(conn, ctask, DID_BUS_BUSY << 16);
+                       fail_command(conn, ctask, error << 16);
                }
        }
 
@@ -1207,7 +1269,7 @@ static void fail_all_commands(struct iscsi_conn *conn, unsigned lun)
                if (lun == ctask->sc->device->lun || lun == -1) {
                        debug_scsi("failing requeued sc %p itt 0x%x\n",
                                   ctask->sc, ctask->itt);
-                       fail_command(conn, ctask, DID_BUS_BUSY << 16);
+                       fail_command(conn, ctask, error << 16);
                }
        }
 
@@ -1233,6 +1295,103 @@ static void iscsi_start_tx(struct iscsi_conn *conn)
        scsi_queue_work(conn->session->host, &conn->xmitwork);
 }
 
+static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
+{
+       struct iscsi_cls_session *cls_session;
+       struct iscsi_session *session;
+       struct iscsi_conn *conn;
+       enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
+
+       cls_session = starget_to_session(scsi_target(scmd->device));
+       session = class_to_transport_session(cls_session);
+
+       debug_scsi("scsi cmd %p timedout\n", scmd);
+
+       spin_lock(&session->lock);
+       if (session->state != ISCSI_STATE_LOGGED_IN) {
+               /*
+                * We are probably in the middle of iscsi recovery so let
+                * that complete and handle the error.
+                */
+               rc = EH_RESET_TIMER;
+               goto done;
+       }
+
+       conn = session->leadconn;
+       if (!conn) {
+               /* In the middle of shuting down */
+               rc = EH_RESET_TIMER;
+               goto done;
+       }
+
+       if (!conn->recv_timeout && !conn->ping_timeout)
+               goto done;
+       /*
+        * if the ping timedout then we are in the middle of cleaning up
+        * and can let the iscsi eh handle it
+        */
+       if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) +
+                           (conn->ping_timeout * HZ), jiffies))
+               rc = EH_RESET_TIMER;
+       /*
+        * if we are about to check the transport then give the command
+        * more time
+        */
+       if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ),
+                          jiffies))
+               rc = EH_RESET_TIMER;
+       /* if in the middle of checking the transport then give us more time */
+       if (conn->ping_mtask)
+               rc = EH_RESET_TIMER;
+done:
+       spin_unlock(&session->lock);
+       debug_scsi("return %s\n", rc == EH_RESET_TIMER ? "timer reset" : "nh");
+       return rc;
+}
+
+static void iscsi_check_transport_timeouts(unsigned long data)
+{
+       struct iscsi_conn *conn = (struct iscsi_conn *)data;
+       struct iscsi_session *session = conn->session;
+       unsigned long timeout, next_timeout = 0, last_recv;
+
+       spin_lock(&session->lock);
+       if (session->state != ISCSI_STATE_LOGGED_IN)
+               goto done;
+
+       timeout = conn->recv_timeout;
+       if (!timeout)
+               goto done;
+
+       timeout *= HZ;
+       last_recv = conn->last_recv;
+       if (time_before_eq(last_recv + timeout + (conn->ping_timeout * HZ),
+                          jiffies)) {
+               iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
+                                 "expired, last rx %lu, last ping %lu, "
+                                 "now %lu\n", conn->ping_timeout, last_recv,
+                                 conn->last_ping, jiffies);
+               spin_unlock(&session->lock);
+               iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+               return;
+       }
+
+       if (time_before_eq(last_recv + timeout, jiffies)) {
+               if (time_before_eq(conn->last_ping, last_recv)) {
+                       /* send a ping to try to provoke some traffic */
+                       debug_scsi("Sending nopout as ping on conn %p\n", conn);
+                       iscsi_send_nopout(conn, NULL);
+               }
+               next_timeout = last_recv + timeout + (conn->ping_timeout * HZ);
+       } else
+               next_timeout = last_recv + timeout;
+
+       debug_scsi("Setting next tmo %lu\n", next_timeout);
+       mod_timer(&conn->transport_timer, next_timeout);
+done:
+       spin_unlock(&session->lock);
+}
+
 static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask,
                                      struct iscsi_tm *hdr)
 {
@@ -1304,7 +1463,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
        hdr = &conn->tmhdr;
        iscsi_prep_abort_task_pdu(ctask, hdr);
 
-       if (iscsi_exec_task_mgmt_fn(conn, hdr, age)) {
+       if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
                rc = FAILED;
                goto failed;
        }
@@ -1365,7 +1524,7 @@ static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
        hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK;
        hdr->flags |= ISCSI_FLAG_CMD_FINAL;
        int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
-       hdr->rtt = ISCSI_RESERVED_TAG;
+       hdr->rtt = RESERVED_ITT;
 }
 
 int iscsi_eh_device_reset(struct scsi_cmnd *sc)
@@ -1396,7 +1555,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
        hdr = &conn->tmhdr;
        iscsi_prep_lun_reset_pdu(sc, hdr);
 
-       if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age)) {
+       if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age,
+                                   session->lu_reset_timeout)) {
                rc = FAILED;
                goto unlock;
        }
@@ -1420,7 +1580,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
        /* need to grab the recv lock then session lock */
        write_lock_bh(conn->recv_lock);
        spin_lock(&session->lock);
-       fail_all_commands(conn, sc->device->lun);
+       fail_all_commands(conn, sc->device->lun, DID_ERROR);
        conn->tmf_state = TMF_INITIAL;
        spin_unlock(&session->lock);
        write_unlock_bh(conn->recv_lock);
@@ -1548,8 +1708,8 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
                qdepth = ISCSI_DEF_CMD_PER_LUN;
        }
 
-       if (cmds_max < 2 || (cmds_max & (cmds_max - 1)) ||
-           cmds_max >= ISCSI_MGMT_ITT_OFFSET) {
+       if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET ||
+           cmds_max < 2) {
                if (cmds_max != 0)
                        printk(KERN_ERR "iscsi: invalid can_queue of %d. "
                               "can_queue must be a power of 2 and between "
@@ -1572,12 +1732,16 @@ iscsi_session_setup(struct iscsi_transport *iscsit,
        shost->max_cmd_len = iscsit->max_cmd_len;
        shost->transportt = scsit;
        shost->transportt->create_work_queue = 1;
+       shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
        *hostno = shost->host_no;
 
        session = iscsi_hostdata(shost->hostdata);
        memset(session, 0, sizeof(struct iscsi_session));
        session->host = shost;
        session->state = ISCSI_STATE_FREE;
+       session->fast_abort = 1;
+       session->lu_reset_timeout = 15;
+       session->abort_timeout = 10;
        session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
        session->cmds_max = cmds_max;
        session->queued_cmdsn = session->cmdsn = initial_cmdsn;
@@ -1708,6 +1872,11 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
        conn->id = conn_idx;
        conn->exp_statsn = 0;
        conn->tmf_state = TMF_INITIAL;
+
+       init_timer(&conn->transport_timer);
+       conn->transport_timer.data = (unsigned long)conn;
+       conn->transport_timer.function = iscsi_check_transport_timeouts;
+
        INIT_LIST_HEAD(&conn->run_list);
        INIT_LIST_HEAD(&conn->mgmt_run_list);
        INIT_LIST_HEAD(&conn->mgmtqueue);
@@ -1757,6 +1926,8 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
        struct iscsi_session *session = conn->session;
        unsigned long flags;
 
+       del_timer_sync(&conn->transport_timer);
+
        spin_lock_bh(&session->lock);
        conn->c_stage = ISCSI_CONN_CLEANUP_WAIT;
        if (session->leadconn == conn) {
@@ -1780,9 +1951,10 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
                }
                spin_unlock_irqrestore(session->host->host_lock, flags);
                msleep_interruptible(500);
-               printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d "
-                      "host_failed %d\n", session->host->host_busy,
-                      session->host->host_failed);
+               iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): "
+                                 "host_busy %d host_failed %d\n",
+                                 session->host->host_busy,
+                                 session->host->host_failed);
                /*
                 * force eh_abort() to unblock
                 */
@@ -1811,23 +1983,42 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
        struct iscsi_session *session = conn->session;
 
        if (!session) {
-               printk(KERN_ERR "iscsi: can't start unbound connection\n");
+               iscsi_conn_printk(KERN_ERR, conn,
+                                 "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);
+               iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: "
+                                 "first_burst %d max_burst %d\n",
+                                 session->first_burst, session->max_burst);
                return -EINVAL;
        }
 
+       if (conn->ping_timeout && !conn->recv_timeout) {
+               iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of "
+                                 "zero. Using 5 seconds\n.");
+               conn->recv_timeout = 5;
+       }
+
+       if (conn->recv_timeout && !conn->ping_timeout) {
+               iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of "
+                                 "zero. Using 5 seconds.\n");
+               conn->ping_timeout = 5;
+       }
+
        spin_lock_bh(&session->lock);
        conn->c_stage = ISCSI_CONN_STARTED;
        session->state = ISCSI_STATE_LOGGED_IN;
        session->queued_cmdsn = session->cmdsn;
 
+       conn->last_recv = jiffies;
+       conn->last_ping = jiffies;
+       if (conn->recv_timeout && conn->ping_timeout)
+               mod_timer(&conn->transport_timer,
+                         jiffies + (conn->recv_timeout * HZ));
+
        switch(conn->stop_stage) {
        case STOP_CONN_RECOVER:
                /*
@@ -1837,11 +2028,9 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
                conn->stop_stage = 0;
                conn->tmf_state = TMF_INITIAL;
                session->age++;
-               spin_unlock_bh(&session->lock);
-
-               iscsi_unblock_session(session_to_cls(session));
-               wake_up(&conn->ehwait);
-               return 0;
+               if (session->age == 16)
+                       session->age = 0;
+               break;
        case STOP_CONN_TERM:
                conn->stop_stage = 0;
                break;
@@ -1850,6 +2039,8 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
        }
        spin_unlock_bh(&session->lock);
 
+       iscsi_unblock_session(session_to_cls(session));
+       wake_up(&conn->ehwait);
        return 0;
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_start);
@@ -1879,6 +2070,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
 {
        int old_stop_stage;
 
+       del_timer_sync(&conn->transport_timer);
+
        mutex_lock(&session->eh_mutex);
        spin_lock_bh(&session->lock);
        if (conn->stop_stage == STOP_CONN_TERM) {
@@ -1939,7 +2132,8 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
         * flush queues.
         */
        spin_lock_bh(&session->lock);
-       fail_all_commands(conn, -1);
+       fail_all_commands(conn, -1,
+                       STOP_CONN_RECOVER ? DID_BUS_BUSY : DID_ERROR);
        flush_control_queues(session, conn);
        spin_unlock_bh(&session->lock);
        mutex_unlock(&session->eh_mutex);
@@ -1956,7 +2150,8 @@ void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
                iscsi_start_session_recovery(session, conn, flag);
                break;
        default:
-               printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag);
+               iscsi_conn_printk(KERN_ERR, conn,
+                                 "invalid stop flag %d\n", flag);
        }
 }
 EXPORT_SYMBOL_GPL(iscsi_conn_stop);
@@ -1993,6 +2188,18 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
        case ISCSI_PARAM_FAST_ABORT:
                sscanf(buf, "%d", &session->fast_abort);
                break;
+       case ISCSI_PARAM_ABORT_TMO:
+               sscanf(buf, "%d", &session->abort_timeout);
+               break;
+       case ISCSI_PARAM_LU_RESET_TMO:
+               sscanf(buf, "%d", &session->lu_reset_timeout);
+               break;
+       case ISCSI_PARAM_PING_TMO:
+               sscanf(buf, "%d", &conn->ping_timeout);
+               break;
+       case ISCSI_PARAM_RECV_TMO:
+               sscanf(buf, "%d", &conn->recv_timeout);
+               break;
        case ISCSI_PARAM_MAX_RECV_DLENGTH:
                sscanf(buf, "%d", &conn->max_recv_dlength);
                break;
@@ -2110,6 +2317,12 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
        case ISCSI_PARAM_FAST_ABORT:
                len = sprintf(buf, "%d\n", session->fast_abort);
                break;
+       case ISCSI_PARAM_ABORT_TMO:
+               len = sprintf(buf, "%d\n", session->abort_timeout);
+               break;
+       case ISCSI_PARAM_LU_RESET_TMO:
+               len = sprintf(buf, "%d\n", session->lu_reset_timeout);
+               break;
        case ISCSI_PARAM_INITIAL_R2T_EN:
                len = sprintf(buf, "%d\n", session->initial_r2t_en);
                break;
@@ -2167,6 +2380,12 @@ int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
        int len;
 
        switch(param) {
+       case ISCSI_PARAM_PING_TMO:
+               len = sprintf(buf, "%u\n", conn->ping_timeout);
+               break;
+       case ISCSI_PARAM_RECV_TMO:
+               len = sprintf(buf, "%u\n", conn->recv_timeout);
+               break;
        case ISCSI_PARAM_MAX_RECV_DLENGTH:
                len = sprintf(buf, "%u\n", conn->max_recv_dlength);
                break;