From: Kiran Patil <[email protected]>

Fix: Added support in TCM/LIO target to offload large FC receives.

Technical Details:
    1. Modified TCM/LIO files (tfc*) to add support for receive offload.
    2. Setup target to support task_sg_chaining by setting bit during
       registration with TCM fabric.
    3. Fix the panic (NULL pointer access) in function transport_calc_sg_num.

Signed-off-by: Kiran Patil <[email protected]>
---

 drivers/target/target_core_transport.c |    8 ++++
 drivers/target/tcm_fc/tcm_fc.h         |    3 ++
 drivers/target/tcm_fc/tfc_cmd.c        |   33 ++++++++++++++++-
 drivers/target/tcm_fc/tfc_conf.c       |    4 ++
 drivers/target/tcm_fc/tfc_io.c         |   64 ++++++++++++++++++++++++++++++++
 5 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/drivers/target/target_core_transport.c 
b/drivers/target/target_core_transport.c
index 0a35a5c..9ad38c5 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -7324,7 +7324,13 @@ next:
         */
        if (task->task_padded_sg) {
                sg_mark_end(&task->task_sg[task->task_sg_num - 1]);
-               sg_mark_end(&task->task_sg_bidi[task->task_sg_num - 1]);
+               /*
+                * Added the 'if' check before marking end of bi-directional
+                * scatterlist (which gets created only in case of request
+                * (RD + WR).
+                */
+               if (task->task_sg_bidi)
+                       sg_mark_end(&task->task_sg_bidi[task->task_sg_num - 1]);
        }
 
        DEBUG_SC("Successfully allocated task->task_sg_num(%u),"
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index 210c33e..8468736 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -153,6 +153,9 @@ struct ft_cmd {
        u32 write_data_len;             /* data received on writes */
        struct se_queue_req se_req;
        unsigned char ft_sense_buffer[TRANSPORT_SENSE_BUFFER]; /* Local sense 
buffer */
+       u32 was_ddp_setup:1;            /* Set only if ddp is setup */
+       struct scatterlist *sg;         /* Set only if DDP is setup */
+       u32 sg_cnt;                     /* No. of item in scatterlist */
 };
 
 extern struct list_head ft_lport_list;
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index e5b6aa1..aaf9b26 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -72,9 +72,9 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
        printk(KERN_INFO "%s: cmd %p lun %d\n", caller, cmd, cmd->lun);
 
        task = T_TASK(se_cmd);
-       printk(KERN_INFO "%s: cmd %p task %p se_num %u buf %p len %u\n",
+       printk(KERN_INFO "%s: cmd %p task %p se_num %u buf %p len %u 
se_cmd_flags <0x%x>\n",
               caller, cmd, task, task->t_tasks_se_num,
-              task->t_task_buf, se_cmd->data_length);
+              task->t_task_buf, se_cmd->data_length, se_cmd->se_cmd_flags);
        if (task->t_mem_list)
                list_for_each_entry(mem, task->t_mem_list, se_list)
                        printk(KERN_INFO "%s: cmd %p mem %p page %p "
@@ -247,6 +247,8 @@ int ft_write_pending(struct se_cmd *se_cmd)
        struct fcp_txrdy *txrdy;
        struct fc_lport *lport;
        struct fc_exch *ep;
+       struct fc_frame_header *fh;
+       u32 f_ctl;
 
        ft_dump_cmd(cmd, __func__);
 
@@ -264,6 +266,33 @@ int ft_write_pending(struct se_cmd *se_cmd)
        fc_fill_fc_hdr(fp, FC_RCTL_DD_DATA_DESC, ep->did, ep->sid, FC_TYPE_FCP,
                       FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
 
+       fh = fc_frame_header_get(fp);
+       f_ctl = ntoh24(fh->fh_f_ctl);
+
+       /* Only if it is 'Exchange Responder' */
+       if (f_ctl & FC_FC_EX_CTX) {
+               /* Target is 'exchange responder' and sending XFER_READY
+                * to 'exchange initiator (initiator)'
+                */
+               if (ep->xid <= lport->lro_xid &&
+                   fh->fh_r_ctl == FC_RCTL_DD_DATA_DESC) {
+                       if (se_cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB) {
+                               /*
+                                * Map se_mem list to scatterlist, so that
+                                * DDP can be setup. DDP setup function require
+                                * scatterlist. se_mem_list is internal to
+                                * TCM/LIO target
+                                */
+                               transport_do_task_sg_chain(se_cmd);
+                               cmd->sg = T_TASK(se_cmd)->t_tasks_sg_chained;
+                               cmd->sg_cnt =
+                                       T_TASK(se_cmd)->t_tasks_sg_chained_no;
+                       }
+                       if (cmd->sg && lport->tt.ddp_setup(lport, ep->xid,
+                                                   cmd->sg, cmd->sg_cnt))
+                               cmd->was_ddp_setup = 1;
+               }
+       }
        lport->tt.seq_send(lport, cmd->seq, fp);
        return 0;
 }
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c
index 604c97d..3668c04 100644
--- a/drivers/target/tcm_fc/tfc_conf.c
+++ b/drivers/target/tcm_fc/tfc_conf.c
@@ -598,6 +598,10 @@ int ft_register_configfs(void)
                return -1;
        }
        fabric->tf_ops = ft_fabric_ops;
+
+       /* Allowing support for task_sg_chaining */
+       fabric->tf_ops.task_sg_chaining = 1;
+
        /*
         * Setup default attribute lists for various fabric->tf_cit_tmpl
         */
diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c
index 2080891..e77a45a 100644
--- a/drivers/target/tcm_fc/tfc_io.c
+++ b/drivers/target/tcm_fc/tfc_io.c
@@ -188,6 +188,9 @@ int ft_queue_data_in(struct se_cmd *se_cmd)
 void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
 {
        struct se_cmd *se_cmd = &cmd->se_cmd;
+       struct fc_seq *seq = cmd->seq;
+       struct fc_exch *ep;
+       struct fc_lport *lport;
        struct se_transport_task *task;
        struct fc_frame_header *fh;
        struct se_mem *mem;
@@ -200,6 +203,8 @@ void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame 
*fp)
        void *page_addr;
        void *from;
        void *to;
+       u32 f_ctl;
+       void *buf;
 
        task = T_TASK(se_cmd);
        BUG_ON(!task);
@@ -207,6 +212,64 @@ void ft_recv_write_data(struct ft_cmd *cmd, struct 
fc_frame *fp)
        fh = fc_frame_header_get(fp);
        if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
                goto drop;
+
+       /*
+        * Doesn't expect even single byte of payload. Payload
+        * is expected to be copied directly to user buffers
+        * due to DDP (Large Rx offload) feature, hence
+        * BUG_ON if BUF is non-NULL
+        */
+       buf = fc_frame_payload_get(fp, 1);
+       if (cmd->was_ddp_setup && buf) {
+               printk(KERN_INFO "%s: When DDP was setup, not expected to"
+                                "receive frame with payload, Payload shall be"
+                                "copied directly to buffer instead of coming "
+                                "via. legacy receive queues\n", __func__);
+               BUG_ON(buf);
+       }
+
+       /*
+        * If ft_cmd indicated 'ddp_setup', in that case only the last frame
+        * should come with 'TSI bit being set'. If 'TSI bit is not set and if
+        * data frame appears here, means error condition. In both the cases
+        * release the DDP context (ddp_put) and in error case, as well
+        * initiate error recovery mechanism.
+        */
+       ep = fc_seq_exch(seq);
+       if (cmd->was_ddp_setup) {
+               BUG_ON(!ep);
+               lport = ep->lp;
+               BUG_ON(!lport);
+       }
+       if (cmd->was_ddp_setup && ep->xid != FC_XID_UNKNOWN) {
+               f_ctl = ntoh24(fh->fh_f_ctl);
+               /*
+                * If TSI bit set in f_ctl, means last write data frame is
+                * received successfully where payload is posted directly
+                * to user buffer and only the last frame's header is posted
+                * in legacy receive queue
+                */
+               if (f_ctl & FC_FC_SEQ_INIT) { /* TSI bit set in FC frame */
+                       cmd->write_data_len = lport->tt.ddp_done(lport,
+                                                               ep->xid);
+                       goto last_frame;
+               } else {
+                       /*
+                        * Updating the write_data_len may be meaningless at
+                        * this point, but just in case if required in future
+                        * for debugging or any other purpose
+                        */
+                       printk(KERN_ERR "%s: Received frame with TSI bit not"
+                                       " being SET, dropping the frame, "
+                                       "cmd->sg <%p>, cmd->sg_cnt <0x%x>\n",
+                                       __func__, cmd->sg, cmd->sg_cnt);
+                       cmd->write_data_len = lport->tt.ddp_done(lport,
+                                                             ep->xid);
+                       lport->tt.seq_exch_abort(cmd->seq, 0);
+                       goto drop;
+               }
+       }
+
        rel_off = ntohl(fh->fh_parm_offset);
        frame_len = fr_len(fp);
        if (frame_len <= sizeof(*fh))
@@ -273,6 +336,7 @@ void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame 
*fp)
                mem_len -= tlen;
                cmd->write_data_len += tlen;
        }
+last_frame:
        if (cmd->write_data_len == se_cmd->data_length)
                transport_generic_handle_data(se_cmd);
 drop:

_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel

Reply via email to