This is the Open-FCoE implementation of the FC pass-through support via bsg interface. This RFC includes fixes of the previous RFC sent to open-fcoe.org in 1) supporting multi-frame responses. 2) correcting the receved data length, job->reply->reply_payload_rcv_len.
Signed-off-by: Steve Ma <[email protected]> --- drivers/scsi/fcoe/fcoe_sw.c | 1 drivers/scsi/libfc/fc_lport.c | 247 +++++++++++++++++++++++++++++++++++++++++ include/scsi/Kbuild | 2 include/scsi/fc/Kbuild | 4 + include/scsi/libfc.h | 6 + 5 files changed, 260 insertions(+), 0 deletions(-) create mode 100644 include/scsi/fc/Kbuild diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c index 7d5ac6e..0c92cb7 100644 --- a/drivers/scsi/fcoe/fcoe_sw.c +++ b/drivers/scsi/fcoe/fcoe_sw.c @@ -86,6 +86,7 @@ struct fc_function_template fcoe_sw_transport_function = { .issue_fc_host_lip = fcoe_reset, .terminate_rport_io = fc_rport_terminate_io, + .bsg_request = fc_lport_bsg_request, }; static struct scsi_host_template fcoe_sw_shost_template = { diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index a6ab692..d41c764 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -94,6 +94,7 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> +#include <linux/scatterlist.h> /* Fabric IDs to use for point-to-point mode, chosen on whims. */ #define FC_LOCAL_PTP_FID_LO 0x010101 @@ -1578,3 +1579,249 @@ int fc_lport_init(struct fc_lport *lport) return 0; } EXPORT_SYMBOL(fc_lport_init); + +struct fc_bsg_info { + struct fc_bsg_job *job; /* pointer to pass-thru job */ + struct fc_lport *lport; /* pointer to local port */ + u16 rsp_code; /* expected response code */ + struct scatterlist *sg; /* job->reply_payload.sg_list */ + u32 nents; /* job->reply_payload.sg_cnt */ + size_t offset; /* offset in response data */ +}; + +/** + * fc_lport_bsg_resp() - The common response handler for fc pass-thru requests + * @sp: current sequence in the fc pass-thru request exchange + * @fp: received response frame + * @rp_arg: pointer to struct fc_bsg_info + */ +static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp, + void *rp_arg) +{ + struct fc_bsg_info *info = (struct fc_bsg_info *)rp_arg; + struct fc_bsg_job *job = info->job; + struct fc_lport *lport = info->lport; + struct fc_frame_header *fh; + size_t len; + void *buf; + + if (IS_ERR(fp)) { + job->reply->reply_payload_rcv_len = 0; + job->reply->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ? + -ECONNABORTED : -ETIMEDOUT; + job->state_flags = FC_RQST_STATE_DONE; + job->job_done(job); + kfree(info); + return; + } + + mutex_lock(&lport->lp_mutex); + + fh = fc_frame_header_get(fp); + len = fr_len(fp) - sizeof(*fh); + buf = fc_frame_payload_get(fp, 0); + + if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) { + /* Get the response code from the first frame payload */ + unsigned short cmd = (info->rsp_code == FC_FS_ACC) ? + ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) : + (unsigned short)fc_frame_payload_op(fp); + + /* Save the reply status of the job */ + job->reply->reply_data.ctels_reply.status = + (cmd == info->rsp_code) ? + FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT; + } + + job->reply->reply_payload_rcv_len += + copy_buffer_to_sglist(buf, len, &info->sg, &info->nents, + &info->offset, KM_BIO_SRC_IRQ, NULL); + + if ((ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) != + (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) { + /* This is not the last response frame */ + fc_frame_free(fp); + mutex_unlock(&lport->lp_mutex); + return; + } + + fc_frame_free(fp); + mutex_unlock(&lport->lp_mutex); + + if (job->state_flags != FC_RQST_STATE_DONE) { + if (job->reply->reply_payload_rcv_len > + job->reply_payload.payload_len) + job->reply->reply_payload_rcv_len = + job->reply_payload.payload_len; + job->reply->result = 0; + job->state_flags = FC_RQST_STATE_DONE; + job->job_done(job); + kfree(info); + } +} + +/** + * fc_lport_els_request() - Send ELS pass-thru request + * @job: The bsg fc pass-thru job structure + * @lport: The local port sending the request + * @did: The destination port id. + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static int fc_lport_els_request(struct fc_bsg_job *job, + struct fc_lport *lport, u32 did) +{ + struct fc_bsg_info *info; + struct fc_frame *fp; + struct fc_frame_header *fh; + char *pp; + int len; + + fp = fc_frame_alloc(lport, sizeof(struct fc_frame_header) + + job->request_payload.payload_len); + if (!fp) + return -EINVAL; + + len = job->request_payload.payload_len; + pp = fc_frame_payload_get(fp, len); + memset(pp, 0, len); + + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + pp, len); + + fh = fc_frame_header_get(fp); + fh->fh_r_ctl = FC_RCTL_ELS_REQ; + hton24(fh->fh_d_id, did); + hton24(fh->fh_s_id, fc_host_port_id(lport->host)); + fh->fh_type = FC_TYPE_ELS; + hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | + FC_FC_END_SEQ | FC_FC_SEQ_INIT); + fh->fh_cs_ctl = 0; + fh->fh_df_ctl = 0; + fh->fh_parm_offset = 0; + + info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); + if (!info) { + fc_frame_free(fp); + return -EINVAL; + } + + info->job = job; + info->lport = lport; + info->rsp_code = ELS_LS_ACC; + info->nents = job->reply_payload.sg_cnt; + info->sg = job->reply_payload.sg_list; + + if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, + NULL, (void *)info, lport->e_d_tov)) + return -ECOMM; + return 0; +} + +/** + * fc_lport_ct_request() - Send CT pass-thru request + * @job: The bsg fc pass-thru job structure + * @lport: The local port sending the request + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static int fc_lport_ct_request(struct fc_bsg_job *job, struct fc_lport *lport) +{ + struct fc_bsg_info *info; + struct fc_frame *fp; + struct fc_frame_header *fh; + struct fc_ct_req *ct; + struct fc_ct_hdr *ct_hdr; + size_t len; + u32 did; + + fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) + + job->request_payload.payload_len); + if (!fp) + return -EINVAL; + + len = job->request_payload.payload_len; + ct = fc_frame_payload_get(fp, len); + memset(ct, 0, len); + + sg_copy_to_buffer(job->request_payload.sg_list, + job->request_payload.sg_cnt, + ct, len); + + ct_hdr = &ct->hdr; + ct_hdr->ct_cmd = htons((u16)ct_hdr->ct_cmd); + ct_hdr->ct_mr_size = htons((u16)ct_hdr->ct_mr_size); + did = ntoh24(ct->hdr.ct_in_id); + + fh = fc_frame_header_get(fp); + fh->fh_r_ctl = FC_RCTL_DD_UNSOL_CTL; + hton24(fh->fh_d_id, did); + hton24(fh->fh_s_id, fc_host_port_id(lport->host)); + fh->fh_type = FC_TYPE_CT; + hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ | + FC_FC_END_SEQ | FC_FC_SEQ_INIT); + fh->fh_cs_ctl = 0; + fh->fh_df_ctl = 0; + fh->fh_parm_offset = 0; + + info = kzalloc(sizeof(struct fc_bsg_info), GFP_KERNEL); + if (!info) { + fc_frame_free(fp); + return -EINVAL; + } + + info->job = job; + info->lport = lport; + info->rsp_code = FC_FS_ACC; + info->nents = job->reply_payload.sg_cnt; + info->sg = job->reply_payload.sg_list; + + if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, + NULL, (void *)info, lport->e_d_tov)) + return -ECOMM; + return 0; +} + +/** + * fc_lport_bsg_request() - The common entry point for sending + * fc pass-thru requests + * @job: The fc pass-thru job structure + */ +int fc_lport_bsg_request(struct fc_bsg_job *job) +{ + struct fc_rport *rport = job->rport; + struct Scsi_Host *shost; + struct fc_lport *lport; + u32 did; + int rc = -EINVAL; + + shost = rport ? rport_to_shost(rport) : job->shost; + lport = shost_priv(shost); + job->reply->reply_payload_rcv_len = 0; + + mutex_lock(&lport->lp_mutex); + + switch (job->request->msgcode) { + case FC_BSG_RPT_ELS: + if (rport) { + did = rport->port_id; + rc = fc_lport_els_request(job, lport, did); + } + break; + case FC_BSG_HST_ELS_NOLOGIN: + did = ntoh24(job->request->rqst_data.h_els.port_id); + rc = fc_lport_els_request(job, lport, did); + break; + case FC_BSG_RPT_CT: + case FC_BSG_HST_CT: + rc = fc_lport_ct_request(job, lport); + break; + } + + mutex_unlock(&lport->lp_mutex); + return rc; +} +EXPORT_SYMBOL(fc_lport_bsg_request); diff --git a/include/scsi/Kbuild b/include/scsi/Kbuild index 33b2750..ad06df5 100644 --- a/include/scsi/Kbuild +++ b/include/scsi/Kbuild @@ -1,3 +1,5 @@ +header-y += fc/ + header-y += scsi.h header-y += scsi_netlink.h header-y += scsi_netlink_fc.h diff --git a/include/scsi/fc/Kbuild b/include/scsi/fc/Kbuild new file mode 100644 index 0000000..5660381 --- /dev/null +++ b/include/scsi/fc/Kbuild @@ -0,0 +1,4 @@ +header-y += fc_els.h +header-y += fc_fs.h +header-y += fc_gs.h +header-y += fc_ns.h diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index f0977a9..e107413 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -26,6 +26,7 @@ #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_fc.h> +#include <scsi/scsi_bsg_fc.h> #include <scsi/fc/fc_fcp.h> #include <scsi/fc/fc_ns.h> @@ -744,6 +745,11 @@ int fc_lport_reset(struct fc_lport *); */ int fc_set_mfs(struct fc_lport *lp, u32 mfs); +/* + * Issue fc pass-thru request via bsg interface + */ +int fc_lport_bsg_request(struct fc_bsg_job *job); + /** * REMOTE PORT LAYER _______________________________________________ devel mailing list [email protected] http://www.open-fcoe.org/mailman/listinfo/devel
