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

Reply via email to