[SCSI] libfc, fcoe: Add FC passthrough support
authorSteve Ma <steve.ma@intel.com>
Tue, 3 Nov 2009 19:47:34 +0000 (11:47 -0800)
committerJames Bottomley <James.Bottomley@suse.de>
Fri, 4 Dec 2009 18:01:06 +0000 (12:01 -0600)
This is the Open-FCoE implementation of the FC
passthrough support via bsg interface.

Passthrough support is added to both N_Ports and
VN_Ports.

Signed-off-by: Steve Ma <steve.ma@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/fcoe/fcoe.c
drivers/scsi/libfc/fc_lport.c
include/scsi/libfc.h

index f1c126b798af98aa0f6fdcf3ebf72d81fd00e278..8f078d306a0a192bf3d28521d1b88543a8054ac1 100644 (file)
@@ -134,6 +134,8 @@ struct fc_function_template fcoe_transport_function = {
        .vport_delete = fcoe_vport_destroy,
        .vport_disable = fcoe_vport_disable,
        .set_vport_symbolic_name = fcoe_set_vport_symbolic_name,
+
+       .bsg_request = fc_lport_bsg_request,
 };
 
 struct fc_function_template fcoe_vport_transport_function = {
@@ -167,6 +169,8 @@ struct fc_function_template fcoe_vport_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_shost_template = {
index dfea6c572dfb827007f66cd0330906e10ccdbc00..2162e6b0f43e3334c100d4b5abbd5557e89bcd02 100644 (file)
@@ -94,6 +94,7 @@
 
 #include <scsi/libfc.h>
 #include <scsi/fc_encode.h>
+#include <linux/scatterlist.h>
 
 #include "fc_libfc.h"
 
@@ -127,6 +128,24 @@ static const char *fc_lport_state_names[] = {
        [LPORT_ST_RESET] =    "reset",
 };
 
+/**
+ * struct fc_bsg_info - FC Passthrough managemet structure
+ * @job:      The passthrough job
+ * @lport:    The local port to pass through a command
+ * @rsp_code: The expected response code
+ * @sg:       job->reply_payload.sg_list
+ * @nents:    job->reply_payload.sg_cnt
+ * @offset:   The offset into the response data
+ */
+struct fc_bsg_info {
+       struct fc_bsg_job *job;
+       struct fc_lport *lport;
+       u16 rsp_code;
+       struct scatterlist *sg;
+       u32 nents;
+       size_t offset;
+};
+
 static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
 {
        fc_frame_free(fp);
@@ -1512,3 +1531,251 @@ int fc_lport_init(struct fc_lport *lport)
        return 0;
 }
 EXPORT_SYMBOL(fc_lport_init);
+
+/**
+ * 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
+ * @info_arg: pointer to struct fc_bsg_info
+ */
+static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
+                             void *info_arg)
+{
+       struct fc_bsg_info *info = info_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->result = (PTR_ERR(fp) == -FC_EX_CLOSED) ?
+                       -ECONNABORTED : -ETIMEDOUT;
+               job->reply_len = sizeof(uint32_t);
+               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 +=
+               fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents,
+                                        &info->offset, KM_BIO_SRC_IRQ, NULL);
+
+       if (fr_eof(fp) == FC_EOF_T &&
+           (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) ==
+           (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) {
+               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_frame_free(fp);
+       mutex_unlock(&lport->lp_mutex);
+}
+
+/**
+ * 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, u32 tov)
+{
+       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 -ENOMEM;
+
+       len = job->request_payload.payload_len;
+       pp = fc_frame_payload_get(fp, 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 -ENOMEM;
+       }
+
+       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, info, 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
+ * @did:   The destination FC-ID
+ * @tov:   The time to wait for a response
+ *
+ * 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, u32 did, u32 tov)
+{
+       struct fc_bsg_info *info;
+       struct fc_frame *fp;
+       struct fc_frame_header *fh;
+       struct fc_ct_req *ct;
+       size_t len;
+
+       fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+                           job->request_payload.payload_len);
+       if (!fp)
+               return -ENOMEM;
+
+       len = job->request_payload.payload_len;
+       ct = fc_frame_payload_get(fp, len);
+
+       sg_copy_to_buffer(job->request_payload.sg_list,
+                         job->request_payload.sg_cnt,
+                         ct, len);
+
+       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 -ENOMEM;
+       }
+
+       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, info, 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 request *rsp = job->req->next_rq;
+       struct Scsi_Host *shost = job->shost;
+       struct fc_lport *lport = shost_priv(shost);
+       struct fc_rport *rport;
+       struct fc_rport_priv *rdata;
+       int rc = -EINVAL;
+       u32 did;
+
+       job->reply->reply_payload_rcv_len = 0;
+       rsp->resid_len = job->reply_payload.payload_len;
+
+       mutex_lock(&lport->lp_mutex);
+
+       switch (job->request->msgcode) {
+       case FC_BSG_RPT_ELS:
+               rport = job->rport;
+               if (!rport)
+                       break;
+
+               rdata = rport->dd_data;
+               rc = fc_lport_els_request(job, lport, rport->port_id,
+                                         rdata->e_d_tov);
+               break;
+
+       case FC_BSG_RPT_CT:
+               rport = job->rport;
+               if (!rport)
+                       break;
+
+               rdata = rport->dd_data;
+               rc = fc_lport_ct_request(job, lport, rport->port_id,
+                                        rdata->e_d_tov);
+               break;
+
+       case FC_BSG_HST_CT:
+               did = ntoh24(job->request->rqst_data.h_ct.port_id);
+               if (did == FC_FID_DIR_SERV)
+                       rdata = lport->dns_rp;
+               else
+                       rdata = lport->tt.rport_lookup(lport, did);
+
+               if (!rdata)
+                       break;
+
+               rc = fc_lport_ct_request(job, lport, did, rdata->e_d_tov);
+               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, lport->e_d_tov);
+               break;
+       }
+
+       mutex_unlock(&lport->lp_mutex);
+       return rc;
+}
+EXPORT_SYMBOL(fc_lport_bsg_request);
index 8258edfa328c85995152c74755545104ba2afeec..54df9fe00c14add7a54ee675519128ba8da3f899 100644 (file)
@@ -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>
@@ -830,6 +831,12 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id);
 void fc_vport_setlink(struct fc_lport *vn_port);
 void fc_vports_linkchange(struct fc_lport *n_port);
 
+/*
+ * Issue fc pass-thru request via bsg interface
+ */
+int fc_lport_bsg_request(struct fc_bsg_job *job);
+
+
 /*
  * REMOTE PORT LAYER
  *****************************/