[SCSI] libfc, fcoe: Add FC passthrough support

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>
This commit is contained in:
Steve Ma 2009-11-03 11:47:34 -08:00 committed by James Bottomley
parent 5868287460
commit a51ab39606
3 changed files with 278 additions and 0 deletions

View 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 = {

View 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);

View 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
*****************************/