[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:
parent
5868287460
commit
a51ab39606
|
@ -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 = {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
*****************************/
|
||||
|
|
Loading…
Reference in New Issue