This is an implementation of the FC pass-through support in FCoE
via bsg interface according to the RFC published by James Smart
on 11/18/2008 with the title "[RFC] FC pass thru - Rev IV"
---
drivers/scsi/fcoe/fcoe_sw.c | 2
drivers/scsi/libfc/fc_rport.c | 232 +++++++++++++++++++++++++++++++++++++++++
include/scsi/libfc.h | 2
3 files changed, 236 insertions(+), 0 deletions(-)
diff --git a/drivers/scsi/fcoe/fcoe_sw.c b/drivers/scsi/fcoe/fcoe_sw.c
index 765a388..04f38af 100644
--- a/drivers/scsi/fcoe/fcoe_sw.c
+++ b/drivers/scsi/fcoe/fcoe_sw.c
@@ -83,6 +83,8 @@ struct fc_function_template fcoe_sw_transport_function = {
.issue_fc_host_lip = fcoe_reset,
.terminate_rport_io = fc_rport_terminate_io,
+
+ .bsg_request = fcoe_bsg_request,
};
static struct scsi_host_template fcoe_sw_shost_template = {
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index dae6513..41d1519 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -1314,3 +1314,235 @@ void fc_rport_terminate_io(struct fc_rport *rport)
lport->tt.exch_mgr_reset(lport, rport->port_id, 0);
}
EXPORT_SYMBOL(fc_rport_terminate_io);
+
+struct fcoe_bsg_info {
+ struct fc_bsg_job *job;
+ struct fc_lport *lport;
+};
+
+static void fc_bsg_els_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fcoe_bsg_info *info = (struct fcoe_bsg_info *)rp_arg;
+ struct fc_bsg_job *job = info->job;
+ struct fc_lport *lport = info->lport;
+ unsigned int len;
+ void *pp;
+
+ mutex_lock(&lport->lp_mutex);
+
+ if (IS_ERR(fp))
+ goto err;
+
+ len = fr_len(fp) - sizeof(struct fc_frame_header);
+ pp = fc_frame_payload_get(fp, len);
+
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ pp, len);
+ job->reply->reply_payload_rcv_len = job->reply_payload.payload_len;
+
+ if (fc_frame_payload_op(fp) == ELS_LS_ACC)
+ job->reply->reply_data.ctels_reply.status
+ = FC_CTELS_STATUS_OK;
+ else
+ job->reply->reply_data.ctels_reply.status
+ = FC_CTELS_STATUS_REJECT;
+err:
+ fc_frame_free(fp);
+ mutex_unlock(&lport->lp_mutex);
+ kfree(info);
+ job->state_flags = FC_RQST_STATE_DONE;
+ job->job_done(job);
+}
+
+static int fcoe_bsg_els_request(struct fc_bsg_job *job)
+{
+ struct fcoe_bsg_info *info;
+ struct fc_rport *rport = job->rport;
+ struct Scsi_Host *shost;
+ struct fc_lport *lport;
+ struct fc_frame *fp;
+ struct fc_frame_header *fh;
+ char *pp;
+ int len;
+ u32 did;
+
+ shost = rport ? rport_to_shost(rport) : job->shost;
+ lport = shost_priv(shost);
+
+ if (!((lport->state == LPORT_ST_READY) &&
+ lport->link_up && !lport->qfull))
+ return -EINVAL;
+
+
+ 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);
+
+ switch (pp[0]) {
+ case ELS_RNID:
+ case ELS_RLS:
+ case ELS_RCS:
+ did = job->request->rqst_data.h_els.port_id[0]<<16 |
+ job->request->rqst_data.h_els.port_id[1]<<8 |
+ job->request->rqst_data.h_els.port_id[2];
+ break;
+ default:
+ if (!rport) {
+ fc_frame_free(fp);
+ return -EINVAL;
+ }
+ did = rport->port_id;
+ break;
+ }
+
+ 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(shost));
+ 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 fcoe_bsg_info), GFP_KERNEL);
+ if (!info) {
+ fc_frame_free(fp);
+ return -EINVAL;
+ }
+
+ info->job = job;
+ info->lport = lport;
+
+ lport->tt.exch_seq_send(lport, fp, fc_bsg_els_resp,
+ NULL, (void *)info, lport->e_d_tov);
+ return 0;
+}
+
+static void fc_bsg_ct_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rp_arg)
+{
+ struct fcoe_bsg_info *info = (struct fcoe_bsg_info *)rp_arg;
+ struct fc_bsg_job *job = info->job;
+ struct fc_lport *lport = info->lport;
+ struct fc_ct_hdr *ct;
+ unsigned int len;
+ void *pp;
+
+ mutex_lock(&lport->lp_mutex);
+
+ if (IS_ERR(fp))
+ goto err;
+
+ len = fr_len(fp) - sizeof(struct fc_frame_header);
+ pp = fc_frame_payload_get(fp, len);
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ pp, len);
+ job->reply->reply_payload_rcv_len = job->reply_payload.payload_len;
+
+ ct = (struct fc_ct_hdr *)pp;
+ if (ntohs(ct->ct_cmd) == FC_FS_ACC)
+ job->reply->reply_data.ctels_reply.status
+ = FC_CTELS_STATUS_OK;
+ else
+ job->reply->reply_data.ctels_reply.status
+ = FC_CTELS_STATUS_REJECT;
+err:
+ fc_frame_free(fp);
+ mutex_unlock(&lport->lp_mutex);
+ kfree(info);
+ job->state_flags = FC_RQST_STATE_DONE;
+ job->job_done(job);
+}
+
+static int fcoe_bsg_ct_request(struct fc_bsg_job *job)
+{
+ struct fcoe_bsg_info *info;
+ struct fc_rport *rport = job->rport;
+ struct Scsi_Host *shost;
+ struct fc_lport *lport;
+ 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;
+
+ shost = rport ? rport_to_shost(rport) : job->shost;
+ lport = shost_priv(shost);
+
+ if (!((lport->state == LPORT_ST_READY) &&
+ lport->link_up && !lport->qfull))
+ return -EINVAL;
+
+ 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 = ct->hdr.ct_in_id[0]<<16 |
+ ct->hdr.ct_in_id[1]<<8 |
+ ct->hdr.ct_in_id[2];
+
+ 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(shost));
+ 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 fcoe_bsg_info), GFP_KERNEL);
+ if (!info) {
+ fc_frame_free(fp);
+ return -EINVAL;
+ }
+
+ info->job = job;
+ info->lport = lport;
+
+ lport->tt.exch_seq_send(lport, fp, fc_bsg_ct_resp,
+ NULL, (void *)info, lport->e_d_tov);
+ return 0;
+}
+
+int fcoe_bsg_request(struct fc_bsg_job *job)
+{
+ switch (job->request->msgcode) {
+ case FC_BSG_RPT_ELS:
+ case FC_BSG_HST_ELS_NOLOGIN:
+ return fcoe_bsg_els_request(job);
+ case FC_BSG_RPT_CT:
+ return fcoe_bsg_ct_request(job);
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(fcoe_bsg_request);
+
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index a70eafa..221dff0 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -25,6 +25,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>
@@ -778,6 +779,7 @@ int fc_set_mfs(struct fc_lport *lp, u32 mfs);
*****************************/
int fc_rport_init(struct fc_lport *lp);
void fc_rport_terminate_io(struct fc_rport *rp);
+int fcoe_bsg_request(struct fc_bsg_job *job);
/*
* DISCOVERY LAYER
_______________________________________________
devel mailing list
[email protected]
http://www.open-fcoe.org/mailman/listinfo/devel