On Thu, 2010-10-28 at 11:48 -0700, Kiran Patil wrote:
> 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),"
Wow, good catch on this one for the BIDI operation case in
transport_calc_sg_num().
I will go ahead and merge this one as a seperate bugfix patch
> 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) {
I think that checkpatch.pl will complain w/ the extra () around the
inidividual conditionals here.. I will fix this up, but just a FYI..
> + 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) {
Fixing up.
> + 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:
>
Aside from the minor nit+fixup, I think this code all looks very good.
Joe, please have a look at the libfc and tcm_fc pieces and ACK
accordingly and I will get this series pushed into lio-core.2.6.git and
the bugfix into my mainline tree.
Many thanks for your contribution Kiran!
--nab
_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel