[SCSI] fix intermittent oops in scsi_bus_uevent
[safe/jmp/linux-2.6] / drivers / scsi / libiscsi.c
index 5205ef2..b43bf1d 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>
@@ -136,6 +137,70 @@ static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len)
        return 0;
 }
 
+/*
+ * make an extended cdb AHS
+ */
+static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
+{
+       struct scsi_cmnd *cmd = ctask->sc;
+       unsigned rlen, pad_len;
+       unsigned short ahslength;
+       struct iscsi_ecdb_ahdr *ecdb_ahdr;
+       int rc;
+
+       ecdb_ahdr = iscsi_next_hdr(ctask);
+       rlen = cmd->cmd_len - ISCSI_CDB_SIZE;
+
+       BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb));
+       ahslength = rlen + sizeof(ecdb_ahdr->reserved);
+
+       pad_len = iscsi_padding(rlen);
+
+       rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) +
+                          sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len);
+       if (rc)
+               return rc;
+
+       if (pad_len)
+               memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len);
+
+       ecdb_ahdr->ahslength = cpu_to_be16(ahslength);
+       ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB;
+       ecdb_ahdr->reserved = 0;
+       memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen);
+
+       debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
+                  "rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
+                  cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len);
+
+       return 0;
+}
+
+static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+{
+       struct scsi_cmnd *sc = ctask->sc;
+       struct iscsi_rlength_ahdr *rlen_ahdr;
+       int rc;
+
+       rlen_ahdr = iscsi_next_hdr(ctask);
+       rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
+       if (rc)
+               return rc;
+
+       rlen_ahdr->ahslength =
+               cpu_to_be16(sizeof(rlen_ahdr->read_length) +
+                                                 sizeof(rlen_ahdr->reserved));
+       rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH;
+       rlen_ahdr->reserved = 0;
+       rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length);
+
+       debug_scsi("bidi-in rlen_ahdr->read_length(%d) "
+                  "rlen_ahdr->ahslength(%d)\n",
+                  be32_to_cpu(rlen_ahdr->read_length),
+                  be16_to_cpu(rlen_ahdr->ahslength));
+       return 0;
+}
+
 /**
  * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
  * @ctask: iscsi cmd task
@@ -149,29 +214,41 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
        struct iscsi_session *session = conn->session;
        struct iscsi_cmd *hdr = ctask->hdr;
        struct scsi_cmnd *sc = ctask->sc;
-       unsigned hdrlength;
+       unsigned hdrlength, cmd_len;
        int rc;
 
        ctask->hdr_len = 0;
        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);
-       if (sc->cmd_len < MAX_COMMAND_SIZE)
-               memset(&hdr->cdb[sc->cmd_len], 0,
-                       MAX_COMMAND_SIZE - sc->cmd_len);
-
-       ctask->data_count = 0;
+       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->cmdsn = cpu_to_be32(session->cmdsn);
+       session->cmdsn++;
+       hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
+       cmd_len = sc->cmd_len;
+       if (cmd_len < ISCSI_CDB_SIZE)
+               memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
+       else if (cmd_len > ISCSI_CDB_SIZE) {
+               rc = iscsi_prep_ecdb_ahs(ctask);
+               if (rc)
+                       return rc;
+               cmd_len = ISCSI_CDB_SIZE;
+       }
+       memcpy(hdr->cdb, sc->cmnd, cmd_len);
+
        ctask->imm_count = 0;
+       if (scsi_bidi_cmnd(sc)) {
+               hdr->flags |= ISCSI_FLAG_CMD_READ;
+               rc = iscsi_prep_bidi_ahs(ctask);
+               if (rc)
+                       return rc;
+       }
        if (sc->sc_data_direction == DMA_TO_DEVICE) {
+               unsigned out_len = scsi_out(sc)->length;
+               hdr->data_length = cpu_to_be32(out_len);
                hdr->flags |= ISCSI_FLAG_CMD_WRITE;
                /*
                 * Write counters:
@@ -192,28 +269,29 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
                ctask->unsol_datasn = 0;
 
                if (session->imm_data_en) {
-                       if (scsi_bufflen(sc) >= session->first_burst)
+                       if (out_len >= session->first_burst)
                                ctask->imm_count = min(session->first_burst,
                                                        conn->max_xmit_dlength);
                        else
-                               ctask->imm_count = min(scsi_bufflen(sc),
+                               ctask->imm_count = min(out_len,
                                                        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),
-                               (scsi_bufflen(sc))) - ctask->imm_count;
+                       ctask->unsol_count = min(session->first_burst, out_len)
+                                                            - 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;
+                       hdr->flags |= ISCSI_FLAG_CMD_FINAL;
        } else {
                hdr->flags |= ISCSI_FLAG_CMD_FINAL;
                zero_data(hdr->dlength);
+               hdr->data_length = cpu_to_be32(scsi_in(sc)->length);
 
                if (sc->sc_data_direction == DMA_FROM_DEVICE)
                        hdr->flags |= ISCSI_FLAG_CMD_READ;
@@ -228,13 +306,17 @@ 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 "
-               "cmdsn %d win %d]\n",
-                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);
+       conn->scsicmd_pdus_cnt++;
+       debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
+               "len %d bidi_len %d cmdsn %d win %d]\n",
+               scsi_bidi_cmnd(sc) ? "bidirectional" :
+                    sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
+               conn->id, sc, sc->cmnd[0], ctask->itt,
+               scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+               session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
        return 0;
 }
 
@@ -248,13 +330,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);
@@ -293,7 +378,12 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
                conn->session->tt->cleanup_cmd_task(conn, ctask);
 
        sc->result = err;
-       scsi_set_resid(sc, scsi_bufflen(sc));
+       if (!scsi_bidi_cmnd(sc))
+               scsi_set_resid(sc, scsi_bufflen(sc));
+       else {
+               scsi_out(sc)->resid = scsi_out(sc)->length;
+               scsi_in(sc)->resid = scsi_in(sc)->length;
+       }
        if (conn->ctask == ctask)
                conn->ctask = NULL;
        /* release ref from queuecommand */
@@ -313,11 +403,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 +501,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;
                }
@@ -368,6 +518,18 @@ invalid_datalen:
                           min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));
        }
 
+       if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
+                          ISCSI_FLAG_CMD_BIDI_OVERFLOW)) {
+               int res_count = be32_to_cpu(rhdr->bi_residual_count);
+
+               if (scsi_bidi_cmnd(sc) && res_count > 0 &&
+                               (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
+                                res_count <= scsi_in(sc)->length))
+                       scsi_in(sc)->resid = res_count;
+               else
+                       sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
+       }
+
        if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW |
                           ISCSI_FLAG_CMD_OVERFLOW)) {
                int res_count = be32_to_cpu(rhdr->residual_count);
@@ -375,13 +537,11 @@ invalid_datalen:
                if (res_count > 0 &&
                    (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
                     res_count <= scsi_bufflen(sc)))
+                       /* write side for bidi or uni-io set_resid */
                        scsi_set_resid(sc, res_count);
                else
                        sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
-       } else if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW |
-                                 ISCSI_FLAG_CMD_BIDI_OVERFLOW))
-               sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
-
+       }
 out:
        debug_scsi("done [sc %lx res %d itt 0x%x]\n",
                   (long)sc, sc->result, ctask->itt);
@@ -409,6 +569,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 +618,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 +638,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 +647,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 +715,24 @@ 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;
+                       } else
+                               mod_timer(&conn->transport_timer,
+                                         jiffies + conn->recv_timeout);
                        iscsi_free_mgmt_task(conn, mtask);
                        break;
                default:
@@ -547,8 +752,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 +771,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 +795,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 +810,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 +865,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 +1016,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 +1091,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 +1107,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 +1131,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 +1158,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 +1187,26 @@ 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);
-       scsi_set_resid(sc, scsi_bufflen(sc));
+       debug_scsi("iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason);
+       if (!scsi_bidi_cmnd(sc))
+               scsi_set_resid(sc, scsi_bufflen(sc));
+       else {
+               scsi_out(sc)->resid = scsi_out(sc)->length;
+               scsi_in(sc)->resid = scsi_in(sc)->length;
+       }
        sc->scsi_done(sc);
+       spin_lock(host->host_lock);
        return 0;
 }
 EXPORT_SYMBOL_GPL(iscsi_queuecommand);
@@ -1003,62 +1220,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 +1270,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 +1296,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 +1312,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 +1350,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 +1363,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 +1371,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 +1397,102 @@ 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 recv_timeout, next_timeout = 0, last_recv;
+
+       spin_lock(&session->lock);
+       if (session->state != ISCSI_STATE_LOGGED_IN)
+               goto done;
+
+       recv_timeout = conn->recv_timeout;
+       if (!recv_timeout)
+               goto done;
+
+       recv_timeout *= HZ;
+       last_recv = conn->last_recv;
+       if (conn->ping_mtask &&
+           time_before_eq(conn->last_ping + (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 + recv_timeout, jiffies)) {
+               /* 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 = conn->last_ping + (conn->ping_timeout * HZ);
+       } else
+               next_timeout = last_recv + 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 +1564,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 +1625,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 +1656,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 +1681,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 +1809,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 +1833,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 +1973,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 +2027,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 +2052,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 +2084,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 +2129,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 +2140,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 +2171,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 +2233,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 +2251,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 +2289,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 +2418,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 +2481,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;