[SCSI] iscsi: data under/over flow fix
[cascardo/linux.git] / drivers / scsi / iscsi_tcp.c
index 810e5e5..550ff66 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/delay.h>
 #include <linux/kfifo.h>
 #include <linux/scatterlist.h>
+#include <linux/mutex.h>
 #include <net/tcp.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
@@ -49,7 +50,7 @@ MODULE_AUTHOR("Dmitry Yusupov <dmitry_yus@yahoo.com>, "
              "Alex Aizman <itn780@yahoo.com>");
 MODULE_DESCRIPTION("iSCSI/TCP data-path");
 MODULE_LICENSE("GPL");
-
+MODULE_VERSION("0:4.445");
 /* #define DEBUG_TCP */
 /* #define DEBUG_SCSI */
 #define DEBUG_ASSERT
@@ -129,14 +130,11 @@ iscsi_buf_left(struct iscsi_buf *ibuf)
 }
 
 static inline void
-iscsi_buf_init_hdr(struct iscsi_conn *conn, struct iscsi_buf *ibuf,
-                  char *vbuf, u8 *crc)
+iscsi_hdr_digest(struct iscsi_conn *conn, struct iscsi_buf *buf,
+                u8* crc)
 {
-       iscsi_buf_init_virt(ibuf, vbuf, sizeof(struct iscsi_hdr));
-       if (conn->hdrdgst_en) {
-               crypto_digest_digest(conn->tx_tfm, &ibuf->sg, 1, crc);
-               ibuf->sg.length += sizeof(uint32_t);
-       }
+       crypto_digest_digest(conn->tx_tfm, &buf->sg, 1, crc);
+       buf->sg.length += sizeof(uint32_t);
 }
 
 static void
@@ -359,7 +357,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
                struct scsi_cmnd *sc = ctask->sc;
 
                conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
-               if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {
+               if (rhdr->flags & ISCSI_FLAG_DATA_UNDERFLOW) {
                        int res_count = be32_to_cpu(rhdr->residual_count);
 
                        if (res_count > 0 &&
@@ -369,9 +367,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
                        } else
                                sc->result = (DID_BAD_TARGET << 16) |
                                        rhdr->cmd_status;
-               } else if (rhdr->flags & ISCSI_FLAG_CMD_BIDI_UNDERFLOW)
-                       sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
-               else if (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW) {
+               } else if (rhdr->flags & ISCSI_FLAG_DATA_OVERFLOW) {
                        sc->resid = be32_to_cpu(rhdr->residual_count);
                        sc->result = (DID_OK << 16) | rhdr->cmd_status;
                } else
@@ -427,8 +423,8 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
 
        r2t->sent = 0;
 
-       iscsi_buf_init_hdr(conn, &r2t->headbuf, (char*)hdr,
-                          (u8 *)dtask->hdrext);
+       iscsi_buf_init_virt(&r2t->headbuf, (char*)hdr,
+                          sizeof(struct iscsi_hdr));
 
        r2t->dtask = dtask;
 
@@ -560,7 +556,7 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
        conn->data_copied = 0;
 
        /* read AHS */
-       conn->in.ahslen = hdr->hlength*(4*sizeof(__u16));
+       conn->in.ahslen = hdr->hlength * 4;
        conn->in.offset += conn->in.ahslen;
        conn->in.copy -= conn->in.ahslen;
        if (conn->in.copy < 0) {
@@ -584,10 +580,16 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
                crypto_digest_digest(conn->rx_tfm, &sg, 1, (u8 *)&cdgst);
                rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) +
                                     conn->in.ahslen);
+               if (cdgst != rdgst) {
+                       printk(KERN_ERR "iscsi_tcp: itt %x: hdrdgst error "
+                              "recv 0x%x calc 0x%x\n", conn->in.itt, rdgst,
+                              cdgst);
+                       return ISCSI_ERR_HDR_DGST;
+               }
        }
 
        /* save opcode for later */
-       conn->in.opcode = hdr->opcode;
+       conn->in.opcode = hdr->opcode & ISCSI_OPCODE_MASK;
 
        /* verify itt (itt encoding: age+cid+itt) */
        if (hdr->itt != cpu_to_be32(ISCSI_RESERVED_TAG)) {
@@ -613,13 +615,6 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
                  conn->in.ahslen, conn->in.datalen);
 
        if (conn->in.itt < session->cmds_max) {
-               if (conn->hdrdgst_en && cdgst != rdgst) {
-                       printk(KERN_ERR "iscsi_tcp: itt %x: hdrdgst error "
-                              "recv 0x%x calc 0x%x\n", conn->in.itt, rdgst,
-                              cdgst);
-                       return ISCSI_ERR_HDR_DGST;
-               }
-
                ctask = (struct iscsi_cmd_task *)session->cmds[conn->in.itt];
 
                if (!ctask->sc) {
@@ -645,9 +640,7 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
                switch(conn->in.opcode) {
                case ISCSI_OP_SCSI_CMD_RSP:
                        BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
-                       if (ctask->hdr.flags & ISCSI_FLAG_CMD_WRITE)
-                               rc = iscsi_cmd_rsp(conn, ctask);
-                       else if (!conn->in.datalen)
+                       if (!conn->in.datalen)
                                rc = iscsi_cmd_rsp(conn, ctask);
                        else
                                /*
@@ -669,40 +662,11 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
                        break;
                case ISCSI_OP_R2T:
                        BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
-                       if (ctask->hdr.flags & ISCSI_FLAG_CMD_WRITE &&
-                           ctask->sc->sc_data_direction == DMA_TO_DEVICE)
+                       if (ctask->sc->sc_data_direction == DMA_TO_DEVICE)
                                rc = iscsi_r2t_rsp(conn, ctask);
                        else
                                rc = ISCSI_ERR_PROTO;
                        break;
-               case ISCSI_OP_NOOP_IN:
-               case ISCSI_OP_TEXT_RSP:
-               case ISCSI_OP_LOGOUT_RSP:
-               case ISCSI_OP_ASYNC_EVENT:
-               case ISCSI_OP_REJECT:
-                       rc = iscsi_check_assign_cmdsn(session,
-                                                (struct iscsi_nopin*)hdr);
-                       if (rc)
-                               break;
-
-                       /* update ExpStatSN */
-                       conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
-                       if (!conn->in.datalen) {
-                               struct iscsi_mgmt_task *mtask;
-
-                               rc = iscsi_recv_pdu(iscsi_handle(conn), hdr,
-                                                   NULL, 0);
-                               mtask = (struct iscsi_mgmt_task *)
-                                       session->mgmt_cmds[conn->in.itt -
-                                               ISCSI_MGMT_ITT_OFFSET];
-                               if (conn->login_mtask != mtask) {
-                                       spin_lock(&session->lock);
-                                       __kfifo_put(session->mgmtpool.queue,
-                                           (void*)&mtask, sizeof(void*));
-                                       spin_unlock(&session->lock);
-                               }
-                       }
-                       break;
                default:
                        rc = ISCSI_ERR_BAD_OPCODE;
                        break;
@@ -721,6 +685,7 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
                switch(conn->in.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)
@@ -754,27 +719,66 @@ iscsi_hdr_recv(struct iscsi_conn *conn)
                                                (void*)&mtask, sizeof(void*));
                                conn->tmabort_state =
                                        ((struct iscsi_tm_rsp *)hdr)->
-                                       response == SCSI_TCP_TM_RESP_COMPLETE ?
+                                       response == ISCSI_TMF_RSP_COMPLETE ?
                                                TMABORT_SUCCESS:TMABORT_FAILED;
                                /* unblock eh_abort() */
                                wake_up(&conn->ehwait);
                        }
                        spin_unlock(&session->lock);
                        break;
+               case ISCSI_OP_NOOP_IN:
+                       if (hdr->ttt != ISCSI_RESERVED_TAG) {
+                               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;
+
+                       if (!conn->in.datalen) {
+                               struct iscsi_mgmt_task *mtask;
+
+                               rc = iscsi_recv_pdu(iscsi_handle(conn), hdr,
+                                                   NULL, 0);
+                               mtask = (struct iscsi_mgmt_task *)
+                                       session->mgmt_cmds[conn->in.itt -
+                                                       ISCSI_MGMT_ITT_OFFSET];
+                               if (conn->login_mtask != mtask) {
+                                       spin_lock(&session->lock);
+                                       __kfifo_put(session->mgmtpool.queue,
+                                                 (void*)&mtask, sizeof(void*));
+                                       spin_unlock(&session->lock);
+                               }
+                       }
+                       break;
                default:
                        rc = ISCSI_ERR_BAD_OPCODE;
                        break;
                }
        } else if (conn->in.itt == ISCSI_RESERVED_TAG) {
-               if (conn->in.opcode == ISCSI_OP_NOOP_IN && !conn->in.datalen) {
-                       rc = iscsi_check_assign_cmdsn(session,
+               switch(conn->in.opcode) {
+               case ISCSI_OP_NOOP_IN:
+                       if (!conn->in.datalen) {
+                               rc = iscsi_check_assign_cmdsn(session,
                                                 (struct iscsi_nopin*)hdr);
-                       if (!rc)
-                               rc = iscsi_recv_pdu(iscsi_handle(conn),
-                                                   hdr, NULL, 0);
-               }
-               else
+                               if (!rc && hdr->ttt != ISCSI_RESERVED_TAG)
+                                       rc = iscsi_recv_pdu(iscsi_handle(conn),
+                                                           hdr, NULL, 0);
+                       } else
+                               rc = ISCSI_ERR_PROTO;
+                       break;
+               case ISCSI_OP_REJECT:
+                       /* we need sth like iscsi_reject_rsp()*/
+               case ISCSI_OP_ASYNC_EVENT:
+                       /* we need sth like iscsi_async_event_rsp() */
+                       rc = ISCSI_ERR_BAD_OPCODE;
+                       break;
+               default:
                        rc = ISCSI_ERR_BAD_OPCODE;
+                       break;
+               }
        } else
                rc = ISCSI_ERR_BAD_ITT;
 
@@ -897,11 +901,20 @@ partial_sg_digest_update(struct iscsi_conn *conn, struct scatterlist *sg,
        crypto_digest_update(conn->data_rx_tfm, &temp, 1);
 }
 
+static void
+iscsi_recv_digest_update(struct iscsi_conn *conn, char* buf, int len)
+{
+       struct scatterlist tmp;
+
+       sg_init_one(&tmp, buf, len);
+       crypto_digest_update(conn->data_rx_tfm, &tmp, 1);
+}
+
 static int iscsi_scsi_data_in(struct iscsi_conn *conn)
 {
        struct iscsi_cmd_task *ctask = conn->in.ctask;
        struct scsi_cmnd *sc = ctask->sc;
-       struct scatterlist tmp, *sg;
+       struct scatterlist *sg;
        int i, offset, rc = 0;
 
        BUG_ON((void*)ctask != sc->SCp.ptr);
@@ -915,10 +928,8 @@ static int iscsi_scsi_data_in(struct iscsi_conn *conn)
                                      sc->request_bufflen, ctask->data_offset);
                if (rc == -EAGAIN)
                        return rc;
-               if (conn->datadgst_en) {
-                       sg_init_one(&tmp, sc->request_buffer, i);
-                       crypto_digest_update(conn->data_rx_tfm, &tmp, 1);
-               }
+               if (conn->datadgst_en)
+                       iscsi_recv_digest_update(conn, sc->request_buffer, i);
                rc = 0;
                goto done;
        }
@@ -1012,6 +1023,9 @@ iscsi_data_recv(struct iscsi_conn *conn)
                conn->in.hdr = &conn->hdr;
                conn->senselen = (conn->data[0] << 8) | conn->data[1];
                rc = iscsi_cmd_rsp(conn, conn->in.ctask);
+               if (!rc && conn->datadgst_en)
+                       iscsi_recv_digest_update(conn, conn->data,
+                                                conn->in.datalen);
        }
        break;
        case ISCSI_OP_TEXT_RSP:
@@ -1036,6 +1050,11 @@ iscsi_data_recv(struct iscsi_conn *conn)
                rc = iscsi_recv_pdu(iscsi_handle(conn), conn->in.hdr,
                                    conn->data, conn->in.datalen);
 
+               if (!rc && conn->datadgst_en &&
+                       conn->in.opcode != ISCSI_OP_LOGIN_RSP)
+                       iscsi_recv_digest_update(conn, conn->data,
+                                               conn->in.datalen);
+
                if (mtask && conn->login_mtask != mtask) {
                        spin_lock(&session->lock);
                        __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
@@ -1044,6 +1063,8 @@ iscsi_data_recv(struct iscsi_conn *conn)
                }
        }
        break;
+       case ISCSI_OP_ASYNC_EVENT:
+       case ISCSI_OP_REJECT:
        default:
                BUG_ON(1);
        }
@@ -1064,7 +1085,6 @@ iscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
 {
        int rc;
        struct iscsi_conn *conn = rd_desc->arg.data;
-       int start = skb_headlen(skb);
        int processed;
        char pad[ISCSI_PAD_LEN];
        struct scatterlist sg;
@@ -1073,7 +1093,7 @@ iscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
         * Save current SKB and its offset in the corresponding
         * connection context.
         */
-       conn->in.copy = start - offset;
+       conn->in.copy = skb->len - offset;
        conn->in.offset = offset;
        conn->in.skb = skb;
        conn->in.len = conn->in.copy;
@@ -1106,8 +1126,7 @@ more:
                 */
                rc = iscsi_hdr_recv(conn);
                if (!rc && conn->in.datalen) {
-                       if (conn->datadgst_en &&
-                           conn->in.opcode == ISCSI_OP_SCSI_DATA_IN) {
+                       if (conn->datadgst_en) {
                                BUG_ON(!conn->data_rx_tfm);
                                crypto_digest_init(conn->data_rx_tfm);
                        }
@@ -1119,26 +1138,24 @@ more:
        }
 
        if (conn->in_progress == IN_PROGRESS_DDIGEST_RECV) {
+               uint32_t recv_digest;
                debug_tcp("extra data_recv offset %d copy %d\n",
                          conn->in.offset, conn->in.copy);
-               if (conn->in.opcode == ISCSI_OP_SCSI_DATA_IN) {
-                       uint32_t recv_digest;
-                       skb_copy_bits(conn->in.skb, conn->in.offset,
-                                     &recv_digest, 4);
-                       conn->in.offset += 4;
-                       conn->in.copy -= 4;
-                       if (recv_digest != conn->in.datadgst) {
-                               debug_tcp("iscsi_tcp: data digest error!"
-                                         "0x%x != 0x%x\n", recv_digest,
-                                         conn->in.datadgst);
-                               iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
-                               return 0;
-                       } else {
-                               debug_tcp("iscsi_tcp: data digest match!"
-                                         "0x%x == 0x%x\n", recv_digest,
-                                         conn->in.datadgst);
-                               conn->in_progress = IN_PROGRESS_WAIT_HEADER;
-                       }
+               skb_copy_bits(conn->in.skb, conn->in.offset,
+                               &recv_digest, 4);
+               conn->in.offset += 4;
+               conn->in.copy -= 4;
+               if (recv_digest != conn->in.datadgst) {
+                       debug_tcp("iscsi_tcp: data digest error!"
+                                 "0x%x != 0x%x\n", recv_digest,
+                                 conn->in.datadgst);
+                       iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
+                       return 0;
+               } else {
+                       debug_tcp("iscsi_tcp: data digest match!"
+                                 "0x%x == 0x%x\n", recv_digest,
+                                 conn->in.datadgst);
+                       conn->in_progress = IN_PROGRESS_WAIT_HEADER;
                }
        }
 
@@ -1159,8 +1176,7 @@ more:
                }
                conn->in.copy -= conn->in.padding;
                conn->in.offset += conn->in.padding;
-               if (conn->datadgst_en &&
-                   conn->in.opcode == ISCSI_OP_SCSI_DATA_IN) {
+               if (conn->datadgst_en) {
                        if (conn->in.padding) {
                                debug_tcp("padding -> %d\n", conn->in.padding);
                                memset(pad, 0, conn->in.padding);
@@ -1229,8 +1245,9 @@ iscsi_tcp_state_change(struct sock *sk)
        conn = (struct iscsi_conn*)sk->sk_user_data;
        session = conn->session;
 
-       if (sk->sk_state == TCP_CLOSE_WAIT ||
-           sk->sk_state == TCP_CLOSE) {
+       if ((sk->sk_state == TCP_CLOSE_WAIT ||
+            sk->sk_state == TCP_CLOSE) &&
+           !atomic_read(&sk->sk_rmem_alloc)) {
                debug_tcp("iscsi_tcp_state_change: TCP_CLOSE|TCP_CLOSE_WAIT\n");
                iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
        }
@@ -1385,7 +1402,7 @@ iscsi_sendpage(struct iscsi_conn *conn, struct iscsi_buf *buf,
        BUG_ON(buf->sent + size > buf->sg.length);
        if (size > *count)
                size = *count;
-       if (buf->sent + size != buf->sg.length)
+       if (buf->sent + size != buf->sg.length || *count != size)
                flags |= MSG_MORE;
 
        res = iscsi_send(sk, buf, size, flags);
@@ -1494,8 +1511,8 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
        }
        conn->dataout_pdus_cnt++;
 
-       iscsi_buf_init_hdr(conn, &r2t->headbuf, (char*)hdr,
-                          (u8 *)dtask->hdrext);
+       iscsi_buf_init_virt(&r2t->headbuf, (char*)hdr,
+                          sizeof(struct iscsi_hdr));
 
        r2t->dtask = dtask;
 
@@ -1541,8 +1558,8 @@ iscsi_unsolicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
                hdr->flags = ISCSI_FLAG_CMD_FINAL;
        }
 
-       iscsi_buf_init_hdr(conn, &ctask->headbuf, (char*)hdr,
-                          (u8 *)dtask->hdrext);
+       iscsi_buf_init_virt(&ctask->headbuf, (char*)hdr,
+                          sizeof(struct iscsi_hdr));
 
        list_add(&dtask->item, &ctask->dataqueue);
 
@@ -1662,8 +1679,8 @@ iscsi_cmd_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
                zero_data(ctask->hdr.dlength);
        }
 
-       iscsi_buf_init_hdr(conn, &ctask->headbuf, (char*)&ctask->hdr,
-                           (u8 *)ctask->hdrext);
+       iscsi_buf_init_virt(&ctask->headbuf, (char*)&ctask->hdr,
+                           sizeof(struct iscsi_hdr));
        conn->scsicmd_pdus_cnt++;
 }
 
@@ -1692,6 +1709,11 @@ iscsi_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
                mtask->xmstate &= ~XMSTATE_IMM_HDR;
                if (mtask->data_count)
                        mtask->xmstate |= XMSTATE_IMM_DATA;
+               if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE &&
+                   conn->stop_stage != STOP_CONN_RECOVER &&
+                   conn->hdrdgst_en)
+                       iscsi_hdr_digest(conn, &mtask->headbuf,
+                                       (u8*)mtask->hdrext);
                if (iscsi_sendhdr(conn, &mtask->headbuf, mtask->data_count)) {
                        mtask->xmstate |= XMSTATE_IMM_HDR;
                        if (mtask->data_count)
@@ -1723,6 +1745,8 @@ static inline int
 handle_xmstate_r_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 {
        ctask->xmstate &= ~XMSTATE_R_HDR;
+       if (conn->hdrdgst_en)
+               iscsi_hdr_digest(conn, &ctask->headbuf, (u8*)ctask->hdrext);
        if (!iscsi_sendhdr(conn, &ctask->headbuf, 0)) {
                BUG_ON(ctask->xmstate != XMSTATE_IDLE);
                return 0; /* wait for Data-In */
@@ -1735,6 +1759,8 @@ static inline int
 handle_xmstate_w_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
 {
        ctask->xmstate &= ~XMSTATE_W_HDR;
+       if (conn->hdrdgst_en)
+               iscsi_hdr_digest(conn, &ctask->headbuf, (u8*)ctask->hdrext);
        if (iscsi_sendhdr(conn, &ctask->headbuf, ctask->imm_count)) {
                ctask->xmstate |= XMSTATE_W_HDR;
                return -EAGAIN;
@@ -1813,7 +1839,9 @@ handle_xmstate_uns_hdr(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
                iscsi_unsolicit_data_init(conn, ctask);
                BUG_ON(!ctask->dtask);
                dtask = ctask->dtask;
-
+               if (conn->hdrdgst_en)
+                       iscsi_hdr_digest(conn, &ctask->headbuf,
+                                       (u8*)dtask->hdrext);
                ctask->xmstate &= ~XMSTATE_UNS_INIT;
        }
        if (iscsi_sendhdr(conn, &ctask->headbuf, ctask->data_count)) {
@@ -2118,7 +2146,9 @@ unsolicit_head_again:
                                    sizeof(void*));
 solicit_head_again:
                r2t = ctask->r2t;
-
+               if (conn->hdrdgst_en)
+                       iscsi_hdr_digest(conn, &r2t->headbuf,
+                                       (u8*)r2t->dtask->hdrext);
                if (iscsi_sendhdr(conn, &r2t->headbuf, r2t->data_count)) {
                        ctask->xmstate &= ~XMSTATE_SOL_DATA;
                        ctask->xmstate |= XMSTATE_SOL_HDR;
@@ -2269,10 +2299,10 @@ iscsi_xmitworker(void *data)
        /*
         * serialize Xmit worker on a per-connection basis.
         */
-       down(&conn->xmitsema);
+       mutex_lock(&conn->xmitmutex);
        if (iscsi_data_xmit(conn))
                schedule_work(&conn->xmitwork);
-       up(&conn->xmitsema);
+       mutex_unlock(&conn->xmitmutex);
 }
 
 #define FAILURE_BAD_HOST               1
@@ -2336,11 +2366,11 @@ iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
                session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
        spin_unlock(&session->lock);
 
-        if (!in_interrupt() && !down_trylock(&conn->xmitsema)) {
+        if (!in_interrupt() && mutex_trylock(&conn->xmitmutex)) {
                spin_unlock_irq(host->host_lock);
                if (iscsi_data_xmit(conn))
                        schedule_work(&conn->xmitwork);
-               up(&conn->xmitsema);
+               mutex_unlock(&conn->xmitmutex);
                spin_lock_irq(host->host_lock);
        } else
                schedule_work(&conn->xmitwork);
@@ -2367,6 +2397,15 @@ fault:
        return 0;
 }
 
+static int
+iscsi_change_queue_depth(struct scsi_device *sdev, int depth)
+{
+       if (depth > ISCSI_MAX_CMD_PER_LUN)
+               depth = ISCSI_MAX_CMD_PER_LUN;
+       scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth);
+       return sdev->queue_depth;
+}
+
 static int
 iscsi_pool_init(struct iscsi_queue *q, int max, void ***items, int item_size)
 {
@@ -2491,7 +2530,7 @@ iscsi_conn_create(iscsi_sessionh_t sessionh, uint32_t conn_idx)
                goto max_recv_dlenght_alloc_fail;
 
        init_timer(&conn->tmabort_timer);
-       init_MUTEX(&conn->xmitsema);
+       mutex_init(&conn->xmitmutex);
        init_waitqueue_head(&conn->ehwait);
 
        return iscsi_handle(conn);
@@ -2521,7 +2560,7 @@ iscsi_conn_destroy(iscsi_connh_t connh)
        struct iscsi_conn *conn = iscsi_ptr(connh);
        struct iscsi_session *session = conn->session;
 
-       down(&conn->xmitsema);
+       mutex_lock(&conn->xmitmutex);
        set_bit(SUSPEND_BIT, &conn->suspend_tx);
        if (conn->c_stage == ISCSI_CONN_INITIAL_STAGE && conn->sock) {
                struct sock *sk = conn->sock->sk;
@@ -2552,7 +2591,7 @@ iscsi_conn_destroy(iscsi_connh_t connh)
        }
        spin_unlock_bh(&session->lock);
 
-       up(&conn->xmitsema);
+       mutex_unlock(&conn->xmitmutex);
 
        /*
         * Block until all in-progress commands for this connection
@@ -2756,7 +2795,7 @@ iscsi_conn_stop(iscsi_connh_t connh, int flag)
        set_bit(SUSPEND_BIT, &conn->suspend_rx);
        write_unlock_bh(&sk->sk_callback_lock);
 
-       down(&conn->xmitsema);
+       mutex_lock(&conn->xmitmutex);
 
        spin_lock_irqsave(session->host->host_lock, flags);
        spin_lock(&session->lock);
@@ -2832,10 +2871,13 @@ iscsi_conn_stop(iscsi_connh_t connh, int flag)
                 * in hdr_extract() and will be re-negotiated at
                 * set_param() time.
                 */
-               if (flag == STOP_CONN_RECOVER)
+               if (flag == STOP_CONN_RECOVER) {
                        conn->hdr_size = sizeof(struct iscsi_hdr);
+                       conn->hdrdgst_en = 0;
+                       conn->datadgst_en = 0;
+               }
        }
-       up(&conn->xmitsema);
+       mutex_unlock(&conn->xmitmutex);
 }
 
 static int
@@ -2889,14 +2931,8 @@ iscsi_conn_send_generic(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
 
        memcpy(&mtask->hdr, hdr, sizeof(struct iscsi_hdr));
 
-       if (conn->c_stage == ISCSI_CONN_INITIAL_STAGE ||
-           conn->stop_stage == STOP_CONN_RECOVER)
-               iscsi_buf_init_virt(&mtask->headbuf, (char*)&mtask->hdr,
+       iscsi_buf_init_virt(&mtask->headbuf, (char*)&mtask->hdr,
                                    sizeof(struct iscsi_hdr));
-       else
-               /* this will update header digest */
-               iscsi_buf_init_hdr(conn, &mtask->headbuf, (char*)&mtask->hdr,
-                                   (u8 *)mtask->hdrext);
 
        spin_unlock_bh(&session->lock);
 
@@ -2992,12 +3028,12 @@ iscsi_eh_abort(struct scsi_cmnd *sc)
         * 1) connection-level failure;
         * 2) recovery due protocol error;
         */
-       down(&conn->xmitsema);
+       mutex_lock(&conn->xmitmutex);
        spin_lock_bh(&session->lock);
        if (session->state != ISCSI_STATE_LOGGED_IN) {
                if (session->state == ISCSI_STATE_TERMINATE) {
                        spin_unlock_bh(&session->lock);
-                       up(&conn->xmitsema);
+                       mutex_unlock(&conn->xmitmutex);
                        goto failed;
                }
                spin_unlock_bh(&session->lock);
@@ -3015,7 +3051,7 @@ iscsi_eh_abort(struct scsi_cmnd *sc)
                         * 2) session was re-open during time out of ctask.
                         */
                        spin_unlock_bh(&session->lock);
-                       up(&conn->xmitsema);
+                       mutex_unlock(&conn->xmitmutex);
                        goto success;
                }
                conn->tmabort_state = TMABORT_INITIAL;
@@ -3070,7 +3106,7 @@ iscsi_eh_abort(struct scsi_cmnd *sc)
                                    conn->tmabort_state == TMABORT_SUCCESS) {
                                        conn->tmabort_state = TMABORT_INITIAL;
                                        spin_unlock_bh(&session->lock);
-                                       up(&conn->xmitsema);
+                                       mutex_unlock(&conn->xmitmutex);
                                        goto success;
                                }
                                conn->tmabort_state = TMABORT_INITIAL;
@@ -3079,7 +3115,7 @@ iscsi_eh_abort(struct scsi_cmnd *sc)
                        spin_unlock_bh(&session->lock);
                }
        }
-       up(&conn->xmitsema);
+       mutex_unlock(&conn->xmitmutex);
 
 
        /*
@@ -3145,7 +3181,7 @@ failed:
 exit:
        del_timer_sync(&conn->tmabort_timer);
 
-       down(&conn->xmitsema);
+       mutex_lock(&conn->xmitmutex);
        if (conn->sock) {
                struct sock *sk = conn->sock->sk;
 
@@ -3153,7 +3189,7 @@ exit:
                iscsi_ctask_cleanup(conn, ctask);
                write_unlock_bh(&sk->sk_callback_lock);
        }
-       up(&conn->xmitsema);
+       mutex_unlock(&conn->xmitmutex);
        return rc;
 }
 
@@ -3232,13 +3268,14 @@ iscsi_r2tpool_free(struct iscsi_session *session)
 static struct scsi_host_template iscsi_sht = {
        .name                   = "iSCSI Initiator over TCP/IP, v."
                                  ISCSI_VERSION_STR,
-        .queuecommand           = iscsi_queuecommand,
+       .queuecommand           = iscsi_queuecommand,
+       .change_queue_depth     = iscsi_change_queue_depth,
        .can_queue              = ISCSI_XMIT_CMDS_MAX - 1,
        .sg_tablesize           = ISCSI_SG_TABLESIZE,
-       .cmd_per_lun            = ISCSI_CMD_PER_LUN,
-        .eh_abort_handler       = iscsi_eh_abort,
-        .eh_host_reset_handler = iscsi_eh_host_reset,
-        .use_clustering         = DISABLE_CLUSTERING,
+       .cmd_per_lun            = ISCSI_DEF_CMD_PER_LUN,
+       .eh_abort_handler       = iscsi_eh_abort,
+       .eh_host_reset_handler  = iscsi_eh_host_reset,
+       .use_clustering         = DISABLE_CLUSTERING,
        .proc_name              = "iscsi_tcp",
        .this_id                = -1,
 };
@@ -3353,7 +3390,7 @@ iscsi_conn_set_param(iscsi_connh_t connh, enum iscsi_param param,
        switch(param) {
        case ISCSI_PARAM_MAX_RECV_DLENGTH: {
                char *saveptr = conn->data;
-               int flags = GFP_KERNEL;
+               gfp_t flags = GFP_KERNEL;
 
                if (conn->data_size >= value) {
                        conn->max_recv_dlength = value;
@@ -3563,9 +3600,9 @@ iscsi_conn_send_pdu(iscsi_connh_t connh, struct iscsi_hdr *hdr, char *data,
        struct iscsi_conn *conn = iscsi_ptr(connh);
        int rc;
 
-       down(&conn->xmitsema);
+       mutex_lock(&conn->xmitmutex);
        rc = iscsi_conn_send_generic(conn, hdr, data, data_size);
-       up(&conn->xmitsema);
+       mutex_unlock(&conn->xmitmutex);
 
        return rc;
 }