Signed-off-by: Anish Bhatt <an...@chelsio.com>
Signed-off by: Manoj Malvia <manojmalv...@chelsio.com>
Signed-off by: Karen Xie <k...@chelsio.com>
---
 drivers/scsi/libiscsi.c     |  61 +++++++--
 drivers/scsi/libiscsi_tcp.c | 296 ++++++++++++++++++++++++++++++++++++++++----
 include/scsi/libiscsi.h     |   6 +
 include/scsi/libiscsi_tcp.h |  22 ++++
 4 files changed, 349 insertions(+), 36 deletions(-)

diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 26dc005b..8cfecec 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -233,7 +233,7 @@ static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
                                                  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);
+       rlen_ahdr->read_length = cpu_to_be32(iscsi_scsi_in_total_length(sc));
 
        ISCSI_DBG_SESSION(task->conn->session,
                          "bidi-in rlen_ahdr->read_length(%d) "
@@ -325,6 +325,32 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task 
*task, int opcode)
        return 0;
 }
 
+inline int iscsi_scsi_out_total_length(struct scsi_cmnd *sc)
+{
+       int sector_size = sc->device->sector_size;
+       int len = scsi_out(sc)->length;
+
+       if ((scsi_get_prot_op(sc) == SCSI_PROT_WRITE_INSERT) ||
+           (scsi_get_prot_op(sc) == SCSI_PROT_WRITE_PASS))
+               len += (len / sector_size) << ISCSI_PI_LEN_PER_SECTOR_SHIFT;
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_scsi_out_total_length);
+
+inline int iscsi_scsi_in_total_length(struct scsi_cmnd *sc)
+{
+       int sector_size = sc->device->sector_size;
+       int len = scsi_in(sc)->length;
+
+       if ((scsi_get_prot_op(sc) == SCSI_PROT_READ_STRIP) ||
+           (scsi_get_prot_op(sc) == SCSI_PROT_READ_PASS))
+               len += (len / sector_size) << ISCSI_PI_LEN_PER_SECTOR_SHIFT;
+
+       return len;
+}
+EXPORT_SYMBOL_GPL(iscsi_scsi_in_total_length);
+
 /**
  * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
  * @task: iscsi task
@@ -392,8 +418,11 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                task->protected = true;
 
        if (sc->sc_data_direction == DMA_TO_DEVICE) {
-               unsigned out_len = scsi_out(sc)->length;
+               unsigned out_len = iscsi_scsi_out_total_length(sc);
                struct iscsi_r2t_info *r2t = &task->unsol_r2t;
+               unsigned int pi_sector_size = sc->device->sector_size +
+                       ((scsi_get_prot_op(sc) == SCSI_PROT_WRITE_STRIP) ?
+                       0 : ISCSI_PI_LEN_PER_SECTOR);
 
                hdr->data_length = cpu_to_be32(out_len);
                hdr->flags |= ISCSI_FLAG_CMD_WRITE;
@@ -420,6 +449,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                        else
                                task->imm_count = min(out_len,
                                                        conn->max_xmit_dlength);
+                       if (scsi_get_prot_op(sc))
+                               task->imm_count =
+                                       (task->imm_count/pi_sector_size) *
+                                       pi_sector_size;
                        hton24(hdr->dlength, task->imm_count);
                } else
                        zero_data(hdr->dlength);
@@ -428,6 +461,10 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                        r2t->data_length = min(session->first_burst, out_len) -
                                               task->imm_count;
                        r2t->data_offset = task->imm_count;
+                       if (scsi_get_prot_op(sc))
+                               r2t->data_length =
+                                       (r2t->data_length/pi_sector_size) *
+                                       pi_sector_size;
                        r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
                        r2t->exp_statsn = cpu_to_be32(conn->exp_statsn);
                }
@@ -436,12 +473,12 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task 
*task)
                        /* No unsolicit Data-Out's */
                        hdr->flags |= ISCSI_FLAG_CMD_FINAL;
        } else {
+               unsigned in_len = iscsi_scsi_in_total_length(sc);
                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;
+               hdr->data_length = cpu_to_be32(in_len);
        }
 
        /* calculate size of additional header segments (AHSs) */
@@ -467,8 +504,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
                          sc->sc_data_direction == DMA_TO_DEVICE ?
                          "write" : "read", conn->id, sc, sc->cmnd[0],
                          task->itt, scsi_bufflen(sc),
-                         scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
-                         session->cmdsn,
+                         scsi_bidi_cmnd(sc) ? iscsi_scsi_in_total_length(sc)
+                         : 0, session->cmdsn,
                          session->max_cmdsn - session->exp_cmdsn + 1);
        return 0;
 }
@@ -635,8 +672,8 @@ static void fail_scsi_task(struct iscsi_task *task, int err)
        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;
+               scsi_out(sc)->resid = iscsi_scsi_out_total_length(sc);
+               scsi_in(sc)->resid = iscsi_scsi_in_total_length(sc);
        }
 
        /* regular RX path uses back_lock */
@@ -887,7 +924,7 @@ invalid_datalen:
 
                if (scsi_bidi_cmnd(sc) && res_count > 0 &&
                                (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW ||
-                                res_count <= scsi_in(sc)->length))
+                                res_count <= iscsi_scsi_in_total_length(sc)))
                        scsi_in(sc)->resid = res_count;
                else
                        sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
@@ -937,7 +974,7 @@ iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr 
*hdr,
 
                if (res_count > 0 &&
                    (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW ||
-                    res_count <= scsi_in(sc)->length))
+                    res_count <= iscsi_scsi_in_total_length(sc)))
                        scsi_in(sc)->resid = res_count;
                else
                        sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;
@@ -1753,8 +1790,8 @@ fault:
        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;
+               scsi_out(sc)->resid = iscsi_scsi_out_total_length(sc);
+               scsi_in(sc)->resid = iscsi_scsi_in_total_length(sc);
        }
        sc->scsi_done(sc);
        return 0;
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index 60cb6dc..aba3319 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -24,6 +24,7 @@
  *     FUJITA Tomonori
  *     Arne Redlich
  *     Zhenyu Wang
+ *     Manoj Malviya
  */
 
 #include <linux/types.h>
@@ -259,7 +260,7 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
         * Set us up for transferring the data digest. hdr digest
         * is completely handled in hdr done function.
         */
-       if (segment->hash) {
+       if (segment->hash && !tcp_conn->in.pi_ctx.pi_pending) {
                crypto_hash_final(segment->hash, segment->digest);
                iscsi_tcp_segment_splice_digest(segment,
                                 recv ? segment->recv_digest : segment->digest);
@@ -270,6 +271,73 @@ int iscsi_tcp_segment_done(struct iscsi_tcp_conn *tcp_conn,
 }
 EXPORT_SYMBOL_GPL(iscsi_tcp_segment_done);
 
+static int
+iscsi_tcp_segment_interleve_data_pi_recv(struct iscsi_tcp_conn *tcp_conn,
+                                        struct iscsi_segment *data_segment,
+                                        const void *ptr, unsigned int len)
+{
+       struct iscsi_segment *pi_segment = &tcp_conn->in.pi_ctx.pi_segment;
+       struct iscsi_segment *segment;
+       unsigned int copy = 0, copied = 0;
+       unsigned int copy_state = tcp_conn->in.pi_ctx.copy_state;
+
+restart:
+       copy = 0;
+       if (copy_state == COPY_DATA)
+               segment = data_segment;
+       else /* if (copy_state == COPY_PI) */
+               segment = pi_segment;
+
+       if (copy_state == COPY_PI &&
+           tcp_conn->in.pi_ctx.prot_op == SCSI_PROT_READ_STRIP) {
+               /* We need to drop pi */
+               copy = min(len - copied, segment->remaining_bytes_in_block);
+               segment->remaining_bytes_in_block -= copy;
+               copied += copy;
+               goto next_state;
+       }
+
+       while (!iscsi_tcp_segment_done(tcp_conn, segment, 1, copy)) {
+               if (!segment->remaining_bytes_in_block) {
+                       /* change the state */
+                       copy_state = ~copy_state;
+                       /* reset remianing block size */
+                       segment->remaining_bytes_in_block =
+                                       ((segment == data_segment) ?
+                                        data_segment->segment_size :
+                                        ISCSI_PI_LEN_PER_SECTOR);
+                       break;
+               }
+               if (copied == len) {
+                       ISCSI_DBG_TCP(tcp_conn->iscsi_conn,
+                                     "copied %d bytes\n", len);
+                       break;
+               }
+
+               copy = min(len - copied, segment->size - segment->copied);
+               copy = min(copy, segment->remaining_bytes_in_block);
+               segment->remaining_bytes_in_block -= copy;
+               memcpy(segment->data + segment->copied, ptr + copied, copy);
+               copied += copy;
+       }
+
+next_state:
+       if (!segment->remaining_bytes_in_block) {
+               /* change the state */
+               copy_state = ~copy_state;
+               /* reset remianing block size */
+               segment->remaining_bytes_in_block = ((segment == data_segment) ?
+                                                    data_segment->segment_size
+                                                    : ISCSI_PI_LEN_PER_SECTOR);
+       }
+       if (copied != len)
+               goto restart;
+
+       tcp_conn->in.pi_ctx.copy_state = copy_state;
+       return copied;
+}
+
+
 /**
  * iscsi_tcp_segment_recv - copy data to segment
  * @tcp_conn: the iSCSI TCP connection
@@ -402,6 +470,9 @@ void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn 
*tcp_conn)
        iscsi_segment_init_linear(&tcp_conn->in.segment,
                                tcp_conn->in.hdr_buf, sizeof(struct iscsi_hdr),
                                iscsi_tcp_hdr_recv_done, NULL);
+
+       tcp_conn->in.pi_inline = 0;
+       memset(&tcp_conn->in.pi_ctx, 0, sizeof(tcp_conn->in.pi_ctx));
 }
 EXPORT_SYMBOL_GPL(iscsi_tcp_hdr_recv_prep);
 
@@ -442,6 +513,71 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
                                iscsi_tcp_data_recv_done, rx_hash);
 }
 
+static int
+iscsi_tcp_process_pi(struct iscsi_tcp_conn *tcp_conn,
+                    struct iscsi_segment *segment)
+{
+       struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+       struct iscsi_hdr *hdr = tcp_conn->in.hdr;
+       int rc;
+
+       if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
+               return ISCSI_ERR_DATA_DGST;
+
+       /* check for non-exceptional status */
+       if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+               rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
+               if (rc)
+                       return rc;
+       }
+
+       iscsi_tcp_hdr_recv_prep(tcp_conn);
+       return 0;
+}
+
+static int
+iscsi_tcp_pi_recv_prep(struct iscsi_tcp_conn *tcp_conn)
+{
+       struct iscsi_conn *conn = tcp_conn->iscsi_conn;
+       struct iscsi_hdr *hdr = (struct iscsi_hdr *)tcp_conn->in.hdr_buf;
+       struct iscsi_segment *segment = &tcp_conn->in.segment;
+       struct hash_desc *hash = segment->hash;
+       struct scsi_data_buffer *prot_sdb = NULL;
+       struct iscsi_task *task;
+       int rc = 0;
+
+       if (tcp_conn->in.pi_inline) {
+               /* all done. need to calculate the final hash and complete */
+               if (segment->hash) {
+                       crypto_hash_final(segment->hash, segment->digest);
+                       iscsi_tcp_segment_splice_digest(segment,
+                                                       segment->recv_digest);
+               }
+               iscsi_tcp_process_pi(tcp_conn, segment);
+               return rc;
+       }
+
+       spin_lock(&conn->session->back_lock);
+       task = iscsi_itt_to_ctask(conn, hdr->itt);
+       if (task) {
+               prot_sdb = scsi_prot(task->sc);
+               rc = iscsi_segment_seek_sg(segment, prot_sdb->table.sgl,
+                                          prot_sdb->table.nents,
+                                          tcp_conn->in.pi_ctx.pi_offset,
+                                          tcp_conn->in.pi_ctx.pi_len,
+                                          iscsi_tcp_process_pi,
+                               /* We don't want to initialize hash again */
+                                           NULL);
+       } else {
+               /* TODO : better error handling */
+               rc = ISCSI_ERR_BAD_ITT;
+       }
+       spin_unlock(&conn->session->back_lock);
+       segment->hash = hash;
+       tcp_conn->in.pi_ctx.pi_pending = 0;
+       return rc;
+}
+
 /**
  * iscsi_tcp_cleanup_task - free tcp_task resources
  * @task: iscsi task
@@ -486,7 +622,11 @@ static int iscsi_tcp_data_in(struct iscsi_conn *conn, 
struct iscsi_task *task)
        struct iscsi_tcp_task *tcp_task = task->dd_data;
        struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
        int datasn = be32_to_cpu(rhdr->datasn);
-       unsigned total_in_length = scsi_in(task->sc)->length;
+       /* unsigned total_in_length = scsi_in(task->sc)->length; */
+       unsigned total_in_length = iscsi_scsi_in_total_length(task->sc);
+
+       /* tcp_task->data_offset and tcp_conn->in.datalen will always have the
+        * values received from target */
 
        /*
         * lib iscsi will update this in the completion handling if there
@@ -571,11 +711,12 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, 
struct iscsi_task *task)
                              data_length, session->max_burst);
 
        data_offset = be32_to_cpu(rhdr->data_offset);
-       if (data_offset + data_length > scsi_out(task->sc)->length) {
+       if (data_offset + data_length > iscsi_scsi_out_total_length(task->sc)) {
                iscsi_conn_printk(KERN_ERR, conn,
                                  "invalid R2T with data len %u at offset %u "
                                  "and total length %d\n", data_length,
-                                 data_offset, scsi_out(task->sc)->length);
+                                 data_offset,
+                                 iscsi_scsi_out_total_length(task->sc));
                return ISCSI_ERR_DATALEN;
        }
 
@@ -617,6 +758,11 @@ iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
        struct iscsi_hdr *hdr = tcp_conn->in.hdr;
        int rc;
 
+       if (tcp_conn->in.pi_ctx.pi_pending) {
+               rc = iscsi_tcp_pi_recv_prep(tcp_conn);
+               return rc;
+       }
+
        if (!iscsi_tcp_dgst_verify(tcp_conn, segment))
                return ISCSI_ERR_DATA_DGST;
 
@@ -631,6 +777,18 @@ iscsi_tcp_process_data_in(struct iscsi_tcp_conn *tcp_conn,
        return 0;
 }
 
+static int pi_len_in_data(int prot_op, int total_len,
+                         unsigned int sector_size)
+{
+       int num_sector = total_len/(sector_size + ISCSI_PI_LEN_PER_SECTOR);
+       int pi_len = 0;
+
+       if (prot_op == SCSI_PROT_READ_PASS || prot_op == SCSI_PROT_READ_STRIP)
+               pi_len = num_sector << ISCSI_PI_LEN_PER_SECTOR_SHIFT;
+
+       return pi_len;
+}
+
 /**
  * iscsi_tcp_hdr_dissect - process PDU header
  * @conn: iSCSI connection
@@ -645,6 +803,7 @@ static int
 iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
 {
        int rc = 0, opcode, ahslen;
+       int prot_op;
        struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
        struct iscsi_task *task;
 
@@ -688,6 +847,11 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct 
iscsi_hdr *hdr)
                        struct iscsi_tcp_task *tcp_task = task->dd_data;
                        struct hash_desc *rx_hash = NULL;
                        struct scsi_data_buffer *sdb = scsi_in(task->sc);
+                       struct scsi_data_buffer *prot_sdb = scsi_prot(task->sc);
+                       unsigned int data_offset = tcp_task->data_offset;
+                       unsigned int datalen = tcp_conn->in.datalen;
+                       unsigned int sector_size = 
task->sc->device->sector_size;
+                       unsigned int pi_len, pi_offset;
 
                        /*
                         * Setup copy of Data-In into the Scsi_Cmnd
@@ -701,18 +865,74 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct 
iscsi_hdr *hdr)
                            !(conn->session->tt->caps & CAP_DIGEST_OFFLOAD))
                                rx_hash = tcp_conn->rx_hash;
 
-                       ISCSI_DBG_TCP(conn, "iscsi_tcp_begin_data_in( "
-                                    "offset=%d, datalen=%d)\n",
-                                     tcp_task->data_offset,
-                                     tcp_conn->in.datalen);
+                       prot_op = scsi_get_prot_op(task->sc);
+                       tcp_conn->in.pi_ctx.prot_op = prot_op;
+
+                       if (prot_op) {
+                               /* Expecting pi, pi bytes in datalen bytes */
+                               pi_len = pi_len_in_data(prot_op,
+                                                       tcp_conn->in.datalen,
+                                                       sector_size);
+                               pi_offset = pi_len_in_data(prot_op,
+                                                          
tcp_task->data_offset,
+                                                          sector_size);
+
+                               ISCSI_DBG_TCP(conn,
+                                             "iscsi_tcp_hdr_dissect: pi_len 
%u, pi_offset %u, tcp_conn->in.datalen %u, tcp_task->data_offset %u\n",
+                                             pi_len, pi_offset,
+                                             tcp_conn->in.datalen,
+                                             tcp_task->data_offset);
+
+                               datalen = tcp_conn->in.datalen - pi_len;
+                               data_offset =  tcp_task->data_offset - 
pi_offset;
+
+                               if (prot_op == SCSI_PROT_READ_INSERT) {
+                                       pi_len = (datalen/sector_size) <<
+                                                ISCSI_PI_LEN_PER_SECTOR_SHIFT;
+                                       pi_offset = (data_offset/sector_size) <<
+                                                ISCSI_PI_LEN_PER_SECTOR_SHIFT;
+                               }
+                               tcp_conn->in.pi_ctx.pi_len = pi_len;
+                               tcp_conn->in.pi_ctx.pi_offset = pi_offset;
+
+                               if (prot_op == SCSI_PROT_READ_STRIP)
+                                       tcp_conn->in.pi_ctx.pi_pending = 0;
+                               else
+                                       tcp_conn->in.pi_ctx.pi_pending = 1;
+                       }
+
+                       ISCSI_DBG_TCP(conn,
+                                     "iscsi_tcp_begin_data_in( offset=%d, 
datalen=%d)\n",
+                                     data_offset, datalen);
                        task->last_xfer = jiffies;
                        rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
                                                   sdb->table.sgl,
                                                   sdb->table.nents,
-                                                  tcp_task->data_offset,
-                                                  tcp_conn->in.datalen,
+                                                  data_offset, datalen,
                                                   iscsi_tcp_process_data_in,
                                                   rx_hash);
+
+                       if (tcp_conn->in.pi_ctx.prot_op &&
+                           tcp_conn->in.pi_inline) {
+                               if (tcp_conn->in.pi_ctx.pi_pending) {
+                                       rc = iscsi_segment_seek_sg(
+                                                  
&tcp_conn->in.pi_ctx.pi_segment,
+                                                  prot_sdb->table.sgl,
+                                                  prot_sdb->table.nents,
+                                                  
tcp_conn->in.pi_ctx.pi_offset,
+                                                  tcp_conn->in.pi_ctx.pi_len,
+                                                  NULL,
+                                                  NULL);
+                               }
+                               tcp_conn->in.segment.segment_size = sector_size;
+                               tcp_conn->in.segment.remaining_bytes_in_block =
+                                       sector_size;
+                               tcp_conn->in.pi_ctx.copy_state = COPY_DATA;
+                               
tcp_conn->in.pi_ctx.pi_segment.remaining_bytes_in_block
+                                       = ISCSI_PI_LEN_PER_SECTOR;
+                               tcp_conn->in.pi_ctx.pi_segment.hash = rx_hash;
+                       }
+
                        spin_unlock(&conn->session->back_lock);
                        return rc;
                }
@@ -866,21 +1086,13 @@ inline int iscsi_tcp_recv_segment_is_hdr(struct 
iscsi_tcp_conn *tcp_conn)
 }
 EXPORT_SYMBOL_GPL(iscsi_tcp_recv_segment_is_hdr);
 
-/**
- * iscsi_tcp_recv_skb - Process skb
- * @conn: iscsi connection
- * @skb: network buffer with header and/or data segment
- * @offset: offset in skb
- * @offload: bool indicating if transfer was offloaded
- *
- * Will return status of transfer in status. And will return
- * number of bytes copied.
- */
-int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
-                      unsigned int offset, bool offloaded, int *status)
+int __iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+                        unsigned int offset, bool offloaded,
+                        bool pi_inline, int *status)
 {
        struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
        struct iscsi_segment *segment = &tcp_conn->in.segment;
+       struct iscsi_segment *pi_segment = &tcp_conn->in.pi_ctx.pi_segment;
        struct skb_seq_state seq;
        unsigned int consumed = 0;
        int rc = 0;
@@ -916,15 +1128,34 @@ int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct 
sk_buff *skb,
                        *status = ISCSI_TCP_SKB_DONE;
                        goto skb_done;
                }
-               BUG_ON(segment->copied >= segment->size);
 
                ISCSI_DBG_TCP(conn, "skb %p ptr=%p avail=%u\n", skb, ptr,
                              avail);
-               rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr, avail);
+
+               if (tcp_conn->in.pi_ctx.prot_op && pi_inline)
+                       rc = iscsi_tcp_segment_interleve_data_pi_recv(tcp_conn,
+                                                                     segment,
+                                                                     ptr,
+                                                                     avail);
+               else {
+                       BUG_ON(segment->copied >= segment->size);
+                       rc = iscsi_tcp_segment_recv(tcp_conn, segment, ptr,
+                                                   avail);
+               }
+               tcp_conn->in.pi_inline = pi_inline;
+
                BUG_ON(rc == 0);
                consumed += rc;
 
                if (segment->total_copied >= segment->total_size) {
+                       /* if pi_inline then segment done can be declared only
+                        * if pi segment is also complete
+                        */
+                       if (!iscsi_tcp_recv_segment_is_hdr(tcp_conn) &&
+                           tcp_conn->in.pi_ctx.prot_op && pi_inline &&
+                           (pi_segment->total_copied <
+                            pi_segment->total_size))
+                               continue;
                        skb_abort_seq_read(&seq);
                        goto segment_done;
                }
@@ -946,6 +1177,23 @@ skb_done:
        conn->rxdata_octets += consumed;
        return consumed;
 }
+EXPORT_SYMBOL_GPL(__iscsi_tcp_recv_skb);
+
+/**
+ * iscsi_tcp_recv_skb - Process skb
+ * @conn: iscsi connection
+ * @skb: network buffer with header and/or data segment
+ * @offset: offset in skb
+ * @offload: bool indicating if transfer was offloaded
+ *
+ * Will return status of transfer in status. And will return
+ * number of bytes copied.
+ */
+int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+                      unsigned int offset, bool offloaded, int *status)
+{
+       return __iscsi_tcp_recv_skb(conn, skb, offset, offloaded, 1, status);
+}
 EXPORT_SYMBOL_GPL(iscsi_tcp_recv_skb);
 
 /**
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 728c9ad..1af7bbd 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -76,6 +76,9 @@ enum {
 
 #define ISCSI_ADDRESS_BUF_LEN          64
 
+#define ISCSI_PI_LEN_PER_SECTOR_SHIFT  3
+#define ISCSI_PI_LEN_PER_SECTOR                8
+
 enum {
        /* this is the maximum possible storage for AHSs */
        ISCSI_MAX_AHS_SIZE = sizeof(struct iscsi_ecdb_ahdr) +
@@ -421,6 +424,9 @@ extern int iscsi_session_get_param(struct iscsi_cls_session 
*cls_session,
 #define iscsi_session_printk(prefix, _sess, fmt, a...) \
        iscsi_cls_session_printk(prefix, _sess->cls_session, fmt, ##a)
 
+extern int iscsi_scsi_out_total_length(struct scsi_cmnd *);
+extern int iscsi_scsi_in_total_length(struct scsi_cmnd *);
+
 /*
  * connection management
  */
diff --git a/include/scsi/libiscsi_tcp.h b/include/scsi/libiscsi_tcp.h
index 2a7aa75..1a1d87c 100644
--- a/include/scsi/libiscsi_tcp.h
+++ b/include/scsi/libiscsi_tcp.h
@@ -37,6 +37,7 @@ struct iscsi_segment {
        unsigned int            copied;
        unsigned int            total_size;
        unsigned int            total_copied;
+       unsigned int            segment_size;
 
        struct hash_desc        *hash;
        unsigned char           padbuf[ISCSI_PAD_LEN];
@@ -50,6 +51,20 @@ struct iscsi_segment {
        bool                    atomic_mapped;
 
        iscsi_segment_done_fn_t *done;
+       /* Used if pi is inline to data */
+       unsigned int            remaining_bytes_in_block;
+};
+
+#define COPY_DATA      0
+#define COPY_PI                ~COPY_DATA
+struct protection_info_ctx {
+       unsigned char                   prot_op;
+       unsigned int                    pi_pending;
+       unsigned int                    pi_len;
+       unsigned int                    pi_offset;
+       /* Following used only pi is inline to data */
+       unsigned int                    copy_state;
+       struct iscsi_segment    pi_segment;
 };
 
 /* Socket connection receive helper */
@@ -62,6 +77,10 @@ struct iscsi_tcp_recv {
 
        /* copied and flipped values */
        int                     datalen;
+       /* LLD should set if the current pdu has inline pi */
+       int                     pi_inline;
+       /* To handle protection bytes */
+       struct protection_info_ctx      pi_ctx;
 };
 
 struct iscsi_tcp_conn {
@@ -97,6 +116,9 @@ enum {
 extern void iscsi_tcp_hdr_recv_prep(struct iscsi_tcp_conn *tcp_conn);
 extern int iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
                              unsigned int offset, bool offloaded, int *status);
+extern int __iscsi_tcp_recv_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+                               unsigned int offset, bool offloaded,
+                               bool pi_inline, int *status);
 extern void iscsi_tcp_cleanup_task(struct iscsi_task *task);
 extern int iscsi_tcp_task_init(struct iscsi_task *task);
 extern int iscsi_tcp_task_xmit(struct iscsi_task *task);
-- 
2.0.3

--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to