fcst: Remove support for kernel versions <= 2.6.36

git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@8028 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2019-03-10 02:28:44 +00:00
parent 759bdca73a
commit b87aa0f004
30 changed files with 0 additions and 3073 deletions

View File

@@ -1,241 +0,0 @@
libfc: recode incoming PRLI handling
Reduce indentation in fc_rport_recv_prli_req() using gotos.
Also add payload length checks.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_rport.c | 195 ++++++++++++++++++-----------------------
1 files changed, 87 insertions(+), 108 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 97923bb..09ec635 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -1441,136 +1441,115 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
struct fc_els_spp *spp; /* response spp */
unsigned int len;
unsigned int plen;
- enum fc_els_rjt_reason reason = ELS_RJT_UNAB;
- enum fc_els_rjt_explan explan = ELS_EXPL_NONE;
enum fc_els_spp_resp resp;
struct fc_seq_els_data rjt_data;
u32 f_ctl;
u32 fcp_parm;
u32 roles = FC_RPORT_ROLE_UNKNOWN;
- rjt_data.fp = NULL;
+ rjt_data.fp = NULL;
fh = fc_frame_header_get(rx_fp);
FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
fc_rport_state(rdata));
- switch (rdata->rp_state) {
- case RPORT_ST_PRLI:
- case RPORT_ST_RTV:
- case RPORT_ST_READY:
- case RPORT_ST_ADISC:
- reason = ELS_RJT_NONE;
- break;
- default:
- fc_frame_free(rx_fp);
- return;
- break;
- }
len = fr_len(rx_fp) - sizeof(*fh);
pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
- if (pp == NULL) {
- reason = ELS_RJT_PROT;
- explan = ELS_EXPL_INV_LEN;
- } else {
- plen = ntohs(pp->prli.prli_len);
- if ((plen % 4) != 0 || plen > len) {
- reason = ELS_RJT_PROT;
- explan = ELS_EXPL_INV_LEN;
- } else if (plen < len) {
- len = plen;
- }
- plen = pp->prli.prli_spp_len;
- if ((plen % 4) != 0 || plen < sizeof(*spp) ||
- plen > len || len < sizeof(*pp)) {
- reason = ELS_RJT_PROT;
- explan = ELS_EXPL_INV_LEN;
- }
- rspp = &pp->spp;
+ if (!pp)
+ goto reject_len;
+ plen = ntohs(pp->prli.prli_len);
+ if ((plen % 4) != 0 || plen > len || plen < 16)
+ goto reject_len;
+ if (plen < len)
+ len = plen;
+ plen = pp->prli.prli_spp_len;
+ if ((plen % 4) != 0 || plen < sizeof(*spp) ||
+ plen > len || len < sizeof(*pp) || plen < 12)
+ goto reject_len;
+ rspp = &pp->spp;
+
+ fp = fc_frame_alloc(lport, len);
+ if (!fp) {
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
+ goto reject;
}
- if (reason != ELS_RJT_NONE ||
- (fp = fc_frame_alloc(lport, len)) == NULL) {
- rjt_data.reason = reason;
- rjt_data.explan = explan;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- } else {
- sp = lport->tt.seq_start_next(sp);
- WARN_ON(!sp);
- pp = fc_frame_payload_get(fp, len);
- WARN_ON(!pp);
- memset(pp, 0, len);
- pp->prli.prli_cmd = ELS_LS_ACC;
- pp->prli.prli_spp_len = plen;
- pp->prli.prli_len = htons(len);
- len -= sizeof(struct fc_els_prli);
-
- /* reinitialize remote port roles */
- rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
- /*
- * Go through all the service parameter pages and build
- * response. If plen indicates longer SPP than standard,
- * use that. The entire response has been pre-cleared above.
- */
- spp = &pp->spp;
- while (len >= plen) {
- spp->spp_type = rspp->spp_type;
- spp->spp_type_ext = rspp->spp_type_ext;
- spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
- resp = FC_SPP_RESP_ACK;
- if (rspp->spp_flags & FC_SPP_RPA_VAL)
- resp = FC_SPP_RESP_NO_PA;
- switch (rspp->spp_type) {
- case 0: /* common to all FC-4 types */
- break;
- case FC_TYPE_FCP:
- fcp_parm = ntohl(rspp->spp_params);
- if (fcp_parm & FCP_SPPF_RETRY)
- rdata->flags |= FC_RP_FLAGS_RETRY;
- rdata->supported_classes = FC_COS_CLASS3;
- if (fcp_parm & FCP_SPPF_INIT_FCN)
- roles |= FC_RPORT_ROLE_FCP_INITIATOR;
- if (fcp_parm & FCP_SPPF_TARG_FCN)
- roles |= FC_RPORT_ROLE_FCP_TARGET;
- rdata->ids.roles = roles;
-
- spp->spp_params =
- htonl(lport->service_params);
- break;
- default:
- resp = FC_SPP_RESP_INVL;
- break;
- }
- spp->spp_flags |= resp;
- len -= plen;
- rspp = (struct fc_els_spp *)((char *)rspp + plen);
- spp = (struct fc_els_spp *)((char *)spp + plen);
- }
+ sp = lport->tt.seq_start_next(sp);
+ WARN_ON(!sp);
+ pp = fc_frame_payload_get(fp, len);
+ WARN_ON(!pp);
+ memset(pp, 0, len);
+ pp->prli.prli_cmd = ELS_LS_ACC;
+ pp->prli.prli_spp_len = plen;
+ pp->prli.prli_len = htons(len);
+ len -= sizeof(struct fc_els_prli);
- /*
- * Send LS_ACC. If this fails, the originator should retry.
- */
- f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
- f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
- ep = fc_seq_exch(sp);
- fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
- FC_TYPE_ELS, f_ctl, 0);
- lport->tt.seq_send(lport, sp, fp);
+ /* reinitialize remote port roles */
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
- /*
- * Get lock and re-check state.
- */
- switch (rdata->rp_state) {
- case RPORT_ST_PRLI:
- fc_rport_enter_ready(rdata);
+ /*
+ * Go through all the service parameter pages and build
+ * response. If plen indicates longer SPP than standard,
+ * use that. The entire response has been pre-cleared above.
+ */
+ spp = &pp->spp;
+ while (len >= plen) {
+ spp->spp_type = rspp->spp_type;
+ spp->spp_type_ext = rspp->spp_type_ext;
+ spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+ resp = FC_SPP_RESP_ACK;
+
+ switch (rspp->spp_type) {
+ case 0: /* common to all FC-4 types */
break;
- case RPORT_ST_READY:
- case RPORT_ST_ADISC:
+ case FC_TYPE_FCP:
+ fcp_parm = ntohl(rspp->spp_params);
+ if (fcp_parm & FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ rdata->supported_classes = FC_COS_CLASS3;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ roles |= FC_RPORT_ROLE_FCP_TARGET;
+ rdata->ids.roles = roles;
+
+ spp->spp_params = htonl(lport->service_params);
break;
default:
+ resp = FC_SPP_RESP_INVL;
break;
}
+ spp->spp_flags |= resp;
+ len -= plen;
+ rspp = (struct fc_els_spp *)((char *)rspp + plen);
+ spp = (struct fc_els_spp *)((char *)spp + plen);
+ }
+
+ /*
+ * Send LS_ACC. If this fails, the originator should retry.
+ */
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
+ f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ fc_rport_enter_ready(rdata);
+ break;
+ default:
+ break;
}
+ goto drop;
+
+reject_len:
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+reject:
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+drop:
fc_frame_free(rx_fp);
}

View File

@@ -1,487 +0,0 @@
libfc: add hook for FC-4 provider registration
Allow FC-4 provider modules to hook into libfc, mostly for targets.
This should allow any FC-4 module to handle PRLI requests and maintain
process-association states.
Each provider registers its ops with libfc and then will be called for
any incoming PRLI for that FC-4 type on any instance. The provider
can decide whether to handle that particular instance using any method
it likes, such as ACLs or other configuration information.
A count is kept of the number of successful PRLIs from the remote port.
Providers are called back with an implicit PRLO when the remote port
is about to be deleted or has been reset.
fc_lport_recv_req() now sends incoming FC-4 requests to FC-4 providers,
and there is a built-in provider always registered for handling
incoming ELS requests.
The call to provider recv() routines uses rcu_read_lock()
so that providers aren't removed during the call. That lock is very
cheap and shouldn't affect any performance on ELS requests.
Providers can rely on the RCU lock to protect a session lookup as well.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_libfc.c | 60 ++++++++++++++++++
drivers/scsi/libfc/fc_libfc.h | 11 +++
drivers/scsi/libfc/fc_lport.c | 63 ++++++++++++++++---
drivers/scsi/libfc/fc_rport.c | 133 +++++++++++++++++++++++++++++++++--------
include/scsi/libfc.h | 26 ++++++++
5 files changed, 255 insertions(+), 38 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index 39f4b6a..ce0de44 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -34,6 +34,23 @@ unsigned int fc_debug_logging;
module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+DEFINE_MUTEX(fc_prov_mutex);
+
+/*
+ * Providers which primarily send requests and PRLIs.
+ */
+struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = {
+ [0] = &fc_rport_t0_prov,
+ [FC_TYPE_FCP] = &fc_rport_fcp_init,
+};
+
+/*
+ * Providers which receive requests.
+ */
+struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = {
+ [FC_TYPE_ELS] = &fc_lport_els_prov,
+};
+
/**
* libfc_init() - Initialize libfc.ko
*/
@@ -132,3 +149,46 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
}
return copy_len;
}
+
+/**
+ * fc_fc4_register_provider() - register FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ *
+ * Returns 0 on success, negative error otherwise.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+ struct fc4_prov **prov_entry;
+ int ret = 0;
+
+ if (type >= FC_FC4_PROV_SIZE)
+ return -EINVAL;
+ mutex_lock(&fc_prov_mutex);
+ prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type;
+ if (*prov_entry)
+ ret = -EBUSY;
+ else
+ *prov_entry = prov;
+ mutex_unlock(&fc_prov_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(fc_fc4_register_provider);
+
+/**
+ * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ */
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+ BUG_ON(type >= FC_FC4_PROV_SIZE);
+ mutex_lock(&fc_prov_mutex);
+ if (prov->recv)
+ rcu_assign_pointer(fc_passive_prov[type], NULL);
+ else
+ rcu_assign_pointer(fc_active_prov[type], NULL);
+ mutex_unlock(&fc_prov_mutex);
+ synchronize_rcu();
+}
+EXPORT_SYMBOL(fc_fc4_deregister_provider);
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index 741fd5c..c3d740c 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -82,6 +82,17 @@ extern unsigned int fc_debug_logging;
(lport)->host->host_no, ##args))
/*
+ * FC-4 Providers.
+ */
+extern struct fc4_prov *fc_active_prov[]; /* providers without recv */
+extern struct fc4_prov *fc_passive_prov[]; /* providers with recv */
+extern struct mutex fc_prov_mutex; /* lock over table changes */
+
+extern struct fc4_prov fc_rport_t0_prov; /* type 0 provider */
+extern struct fc4_prov fc_lport_els_prov; /* ELS provider */
+extern struct fc4_prov fc_rport_fcp_init; /* FCP initiator provider */
+
+/*
* Set up direct-data placement for this I/O request
*/
void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 7ec8ce7..35149f0 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -847,7 +847,7 @@ out:
}
/**
- * fc_lport_recv_req() - The generic lport request handler
+ * fc_lport_recv_els_req() - The generic lport ELS request handler
* @lport: The local port that received the request
* @sp: The sequence the request is on
* @fp: The request frame
@@ -858,8 +858,8 @@ out:
* Locking Note: This function should not be called with the lport
* lock held becuase it will grab the lock.
*/
-static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
- struct fc_frame *fp)
+static void fc_lport_recv_els_req(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
@@ -873,8 +873,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
*/
if (!lport->link_up)
fc_frame_free(fp);
- else if (fh->fh_type == FC_TYPE_ELS &&
- fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+ else {
/*
* Check opcode.
*/
@@ -903,17 +902,59 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
}
recv(sp, fp, lport);
- } else {
- FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
- fr_eof(fp));
- fc_frame_free(fp);
}
mutex_unlock(&lport->lp_mutex);
+ lport->tt.exch_done(sp);
+}
+
+static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *spp_in,
+ struct fc_els_spp *spp_out)
+{
+ return FC_SPP_RESP_INVL;
+}
+
+struct fc4_prov fc_lport_els_prov = {
+ .prli = fc_lport_els_prli,
+ .recv = fc_lport_recv_els_req,
+};
+
+/**
+ * fc_lport_recv_req() - The generic lport request handler
+ * @lport: The lport that received the request
+ * @sp: The sequence the request is on
+ * @fp: The frame the request is in
+ *
+ * Locking Note: This function should not be called with the lport
+ * lock held becuase it may grab the lock.
+ */
+static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc4_prov *prov;
/*
- * The common exch_done for all request may not be good
- * if any request requires longer hold on exhange. XXX
+ * Use RCU read lock and module_lock to be sure module doesn't
+ * deregister and get unloaded while we're calling it.
+ * try_module_get() is inlined and accepts a NULL parameter.
+ * Only ELSes and FCP target ops should come through here.
+ * The locking is unfortunate, and a better scheme is being sought.
*/
+ rcu_read_lock();
+ if (fh->fh_type >= FC_FC4_PROV_SIZE)
+ goto drop;
+ prov = rcu_dereference(fc_passive_prov[fh->fh_type]);
+ if (!prov || !try_module_get(prov->module))
+ goto drop;
+ rcu_read_unlock();
+ prov->recv(lport, sp, fp);
+ module_put(prov->module);
+ return;
+drop:
+ rcu_read_unlock();
+ FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
+ fc_frame_free(fp);
lport->tt.exch_done(sp);
}
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 09ec635..3713060 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -245,6 +245,8 @@ static void fc_rport_work(struct work_struct *work)
struct fc_rport_operations *rport_ops;
struct fc_rport_identifiers ids;
struct fc_rport *rport;
+ struct fc4_prov *prov;
+ u8 type;
int restart = 0;
mutex_lock(&rdata->rp_mutex);
@@ -294,6 +296,15 @@ static void fc_rport_work(struct work_struct *work)
case RPORT_EV_FAILED:
case RPORT_EV_LOGO:
case RPORT_EV_STOP:
+ if (rdata->prli_count) {
+ mutex_lock(&fc_prov_mutex);
+ for (type = 1; type < FC_FC4_PROV_SIZE; type++) {
+ prov = fc_passive_prov[type];
+ if (prov && prov->prlo)
+ prov->prlo(rdata);
+ }
+ mutex_unlock(&fc_prov_mutex);
+ }
port_id = rdata->ids.port_id;
mutex_unlock(&rdata->rp_mutex);
@@ -1433,6 +1444,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
struct fc_exch *ep;
struct fc_frame *fp;
struct fc_frame_header *fh;
+ struct fc4_prov *prov;
struct {
struct fc_els_prli prli;
struct fc_els_spp spp;
@@ -1442,10 +1454,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
unsigned int len;
unsigned int plen;
enum fc_els_spp_resp resp;
+ enum fc_els_spp_resp passive;
struct fc_seq_els_data rjt_data;
u32 f_ctl;
- u32 fcp_parm;
- u32 roles = FC_RPORT_ROLE_UNKNOWN;
rjt_data.fp = NULL;
fh = fc_frame_header_get(rx_fp);
@@ -1484,46 +1495,41 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
pp->prli.prli_len = htons(len);
len -= sizeof(struct fc_els_prli);
- /* reinitialize remote port roles */
- rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
/*
* Go through all the service parameter pages and build
* response. If plen indicates longer SPP than standard,
* use that. The entire response has been pre-cleared above.
*/
spp = &pp->spp;
+ mutex_lock(&fc_prov_mutex);
while (len >= plen) {
spp->spp_type = rspp->spp_type;
spp->spp_type_ext = rspp->spp_type_ext;
- spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
- resp = FC_SPP_RESP_ACK;
-
- switch (rspp->spp_type) {
- case 0: /* common to all FC-4 types */
- break;
- case FC_TYPE_FCP:
- fcp_parm = ntohl(rspp->spp_params);
- if (fcp_parm & FCP_SPPF_RETRY)
- rdata->flags |= FC_RP_FLAGS_RETRY;
- rdata->supported_classes = FC_COS_CLASS3;
- if (fcp_parm & FCP_SPPF_INIT_FCN)
- roles |= FC_RPORT_ROLE_FCP_INITIATOR;
- if (fcp_parm & FCP_SPPF_TARG_FCN)
- roles |= FC_RPORT_ROLE_FCP_TARGET;
- rdata->ids.roles = roles;
-
- spp->spp_params = htonl(lport->service_params);
- break;
- default:
- resp = FC_SPP_RESP_INVL;
- break;
+ resp = 0;
+
+ if (rspp->spp_type < FC_FC4_PROV_SIZE) {
+ prov = fc_active_prov[rspp->spp_type];
+ if (prov)
+ resp = prov->prli(rdata, plen, rspp, spp);
+ prov = fc_passive_prov[rspp->spp_type];
+ if (prov) {
+ passive = prov->prli(rdata, plen, rspp, spp);
+ if (!resp || passive == FC_SPP_RESP_ACK)
+ resp = passive;
+ }
+ }
+ if (!resp) {
+ if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
+ resp |= FC_SPP_RESP_CONF;
+ else
+ resp |= FC_SPP_RESP_INVL;
}
spp->spp_flags |= resp;
len -= plen;
rspp = (struct fc_els_spp *)((char *)rspp + plen);
spp = (struct fc_els_spp *)((char *)spp + plen);
}
+ mutex_unlock(&fc_prov_mutex);
/*
* Send LS_ACC. If this fails, the originator should retry.
@@ -1668,6 +1674,79 @@ int fc_rport_init(struct fc_lport *lport)
EXPORT_SYMBOL(fc_rport_init);
/**
+ * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ *
+ * Returns the value for the response code to be placed in spp_flags;
+ * Returns 0 if not an initiator.
+ */
+static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp,
+ struct fc_els_spp *spp)
+{
+ struct fc_lport *lport = rdata->local_port;
+ u32 fcp_parm;
+
+ fcp_parm = ntohl(rspp->spp_params);
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+ if (fcp_parm & FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ rdata->supported_classes = FC_COS_CLASS3;
+
+ if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR))
+ return 0;
+
+ spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+
+ /*
+ * OR in our service parameters with other providers (target), if any.
+ */
+ fcp_parm = ntohl(spp->spp_params);
+ spp->spp_params = htonl(fcp_parm | lport->service_params);
+ return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for FCP initiator.
+ */
+struct fc4_prov fc_rport_fcp_init = {
+ .prli = fc_rport_fcp_prli,
+};
+
+/**
+ * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ */
+static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp,
+ struct fc_els_spp *spp)
+{
+ if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR)
+ return FC_SPP_RESP_INVL;
+ return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for type 0 service parameters.
+ *
+ * This handles the special case of type 0 which is always successful
+ * but doesn't do anything otherwise.
+ */
+struct fc4_prov fc_rport_t0_prov = {
+ .prli = fc_rport_t0_prli,
+};
+
+/**
* fc_setup_rport() - Initialize the rport_event_queue
*/
int fc_setup_rport()
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 4b912ee..9403d4e 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -35,6 +35,8 @@
#include <scsi/fc_frame.h>
+#define FC_FC4_PROV_SIZE (FC_TYPE_FCP + 1) /* size of tables */
+
/*
* libfc error codes
*/
@@ -190,6 +192,7 @@ struct fc_rport_libfc_priv {
* @rp_mutex: The mutex that protects the remote port
* @retry_work: Handle for retries
* @event_callback: Callback when READY, FAILED or LOGO states complete
+ * @prli_count: Count of open PRLI sessions in providers
*/
struct fc_rport_priv {
struct fc_lport *local_port;
@@ -211,6 +214,7 @@ struct fc_rport_priv {
struct list_head peers;
struct work_struct event_work;
u32 supported_classes;
+ u16 prli_count;
};
/**
@@ -850,6 +854,28 @@ struct fc_lport {
struct delayed_work retry_work;
};
+/**
+ * struct fc4_prov - FC-4 provider registration
+ * @prli: Handler for incoming PRLI
+ * @prlo: Handler for session reset
+ * @recv: Handler for incoming request
+ * @module: Pointer to module. May be NULL.
+ */
+struct fc4_prov {
+ int (*prli)(struct fc_rport_priv *, u32 spp_len,
+ const struct fc_els_spp *spp_in,
+ struct fc_els_spp *spp_out);
+ void (*prlo)(struct fc_rport_priv *);
+ void (*recv)(struct fc_lport *, struct fc_seq *, struct fc_frame *);
+ struct module *module;
+};
+
+/*
+ * Register FC-4 provider with libfc.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *);
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *);
+
/*
* FC_LPORT HELPER FUNCTIONS
*****************************/

View File

@@ -1,31 +0,0 @@
libfc: fix sequence-initiative WARN in fc_seq_start_next
The target code was getting a warning that sequence initiative
wasn't held when starting the response sequence after sending
the data sequence.
The bug was that sequence initiative was cleared due to the
END_SEQ flag being on. The intent may have been to check LAST_SEQ.
Change just to check SEQ_INIT.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_exch.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 7f43647..1ad1ded 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -488,7 +488,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
*/
spin_lock_bh(&ep->ex_lock);
ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ; /* not first seq */
- if (f_ctl & (FC_FC_END_SEQ | FC_FC_SEQ_INIT))
+ if (f_ctl & FC_FC_SEQ_INIT)
ep->esb_stat &= ~ESB_ST_SEQ_INIT;
spin_unlock_bh(&ep->ex_lock);
return error;

View File

@@ -1,84 +0,0 @@
libfc: add method for setting handler for incoming exchange
Add a method for setting handler for incoming exchange.
For multi-sequence exchanges, this allows the target driver
to add a response handler for handling subsequent sequences,
and exchange manager resets.
The new function is called fc_seq_set_resp().
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_exch.c | 19 +++++++++++++++++++
include/scsi/libfc.h | 11 ++++++++++-
2 files changed, 29 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 1ad1ded..aee3c59 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -545,6 +545,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
return sp;
}
+/*
+ * Set the response handler for the exchange associated with a sequence.
+ */
+static void fc_seq_set_resp(struct fc_seq *sp,
+ void (*resp)(struct fc_seq *, struct fc_frame *,
+ void *),
+ void *arg)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ ep->resp = resp;
+ ep->arg = arg;
+ spin_unlock_bh(&ep->ex_lock);
+}
+
/**
* fc_seq_exch_abort() - Abort an exchange and sequence
* @req_sp: The sequence to be aborted
@@ -2280,6 +2296,9 @@ int fc_exch_init(struct fc_lport *lport)
if (!lport->tt.seq_start_next)
lport->tt.seq_start_next = fc_seq_start_next;
+ if (!lport->tt.seq_set_resp)
+ lport->tt.seq_set_resp = fc_seq_set_resp;
+
if (!lport->tt.exch_seq_send)
lport->tt.exch_seq_send = fc_exch_seq_send;
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 9403d4e..87204da 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -566,6 +566,16 @@ struct libfc_function_template {
struct fc_seq *(*seq_start_next)(struct fc_seq *);
/*
+ * Set a response handler for the exchange of the sequence.
+ *
+ * STATUS: OPTIONAL
+ */
+ void (*seq_set_resp)(struct fc_seq *sp,
+ void (*resp)(struct fc_seq *, struct fc_frame *,
+ void *),
+ void *arg);
+
+ /*
* Reset an exchange manager, completing all sequences and exchanges.
* If s_id is non-zero, reset only exchanges originating from that FID.
* If d_id is non-zero, reset only exchanges sending to that FID.
@@ -1058,7 +1068,6 @@ struct fc_seq *fc_elsct_send(struct fc_lport *, u32 did,
void fc_lport_flogi_resp(struct fc_seq *, struct fc_frame *, void *);
void fc_lport_logo_resp(struct fc_seq *, struct fc_frame *, void *);
-
/*
* EXCHANGE MANAGER LAYER
*****************************/

View File

@@ -1,36 +0,0 @@
libfc: add local port hook for provider session lookup
The target provider needs a per-instance lookup table
or other way to lookup sessions quickly without going through
a linear list or serializing too much.
Add a simple void * array indexed by FC-4 type to the fc_lport.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
include/scsi/libfc.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
---
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 87204da..6817fe3 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -810,6 +810,7 @@ struct fc_disc {
* @lp_mutex: Mutex to protect the local port
* @list: Handle for list of local ports
* @retry_work: Handle to local port for delayed retry context
+ * @prov: Pointers available for use by passive FC-4 providers
*/
struct fc_lport {
/* Associations */
@@ -862,6 +863,7 @@ struct fc_lport {
struct mutex lp_mutex;
struct list_head list;
struct delayed_work retry_work;
+ void *prov[FC_FC4_PROV_SIZE];
};
/**

View File

@@ -1,167 +0,0 @@
libfc: add hook to notify providers of local port changes
When an SCST provider is registered, it needs to know what
local ports are available for configuration as targets.
Add a notifier chain that is invoked when any local port
that is added or deleted.
Maintain a global list of local ports and add an
interator function that calls a given function for
every existing local port. This is used when first
loading a provider.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_libfc.c | 41 +++++++++++++++++++++++++++++++++++++++++
drivers/scsi/libfc/fc_libfc.h | 2 ++
drivers/scsi/libfc/fc_lport.c | 2 ++
include/scsi/libfc.h | 14 +++++++++++++-
4 files changed, 58 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index ce0de44..abd108a 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -35,6 +35,10 @@ module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
DEFINE_MUTEX(fc_prov_mutex);
+static LIST_HEAD(fc_local_ports);
+struct blocking_notifier_head fc_lport_notifier_head =
+ BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head);
+EXPORT_SYMBOL(fc_lport_notifier_head);
/*
* Providers which primarily send requests and PRLIs.
@@ -150,6 +154,17 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
return copy_len;
}
+void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg)
+{
+ struct fc_lport *lport;
+
+ mutex_lock(&fc_prov_mutex);
+ list_for_each_entry(lport, &fc_local_ports, lport_list)
+ notify(lport, arg);
+ mutex_unlock(&fc_prov_mutex);
+}
+EXPORT_SYMBOL(fc_lport_iterate);
+
/**
* fc_fc4_register_provider() - register FC-4 upper-level provider.
* @type: FC-4 type, such as FC_TYPE_FCP
@@ -192,3 +207,29 @@ void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
synchronize_rcu();
}
EXPORT_SYMBOL(fc_fc4_deregister_provider);
+
+/**
+ * fc_fc4_add_lport() - add new local port to list and run notifiers.
+ * @lport: The new local port.
+ */
+void fc_fc4_add_lport(struct fc_lport *lport)
+{
+ mutex_lock(&fc_prov_mutex);
+ list_add_tail(&lport->lport_list, &fc_local_ports);
+ blocking_notifier_call_chain(&fc_lport_notifier_head,
+ FC_LPORT_EV_ADD, lport);
+ mutex_unlock(&fc_prov_mutex);
+}
+
+/**
+ * fc_fc4_del_lport() - remove local port from list and run notifiers.
+ * @lport: The new local port.
+ */
+void fc_fc4_del_lport(struct fc_lport *lport)
+{
+ mutex_lock(&fc_prov_mutex);
+ list_del(&lport->lport_list);
+ blocking_notifier_call_chain(&fc_lport_notifier_head,
+ FC_LPORT_EV_DEL, lport);
+ mutex_unlock(&fc_prov_mutex);
+}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index c3d740c..1bde8e2 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -111,6 +111,8 @@ void fc_destroy_fcp(void);
* Internal libfc functions
*/
const char *fc_els_resp_type(struct fc_frame *);
+extern void fc_fc4_add_lport(struct fc_lport *);
+extern void fc_fc4_del_lport(struct fc_lport *);
/*
* Copies a buffer into an sg list
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 35149f0..507b6c5 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -654,6 +654,7 @@ int fc_lport_destroy(struct fc_lport *lport)
lport->tt.fcp_abort_io(lport);
lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
+ fc_fc4_del_lport(lport);
return 0;
}
EXPORT_SYMBOL(fc_lport_destroy);
@@ -1638,6 +1639,7 @@ int fc_lport_init(struct fc_lport *lport)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+ fc_fc4_add_lport(lport);
return 0;
}
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 6817fe3..12fe787 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -770,6 +770,15 @@ struct fc_disc {
enum fc_disc_event);
};
+/*
+ * Local port notifier and events.
+ */
+extern struct blocking_notifier_head fc_lport_notifier_head;
+enum fc_lport_event {
+ FC_LPORT_EV_ADD,
+ FC_LPORT_EV_DEL,
+};
+
/**
* struct fc_lport - Local port
* @host: The SCSI host associated with a local port
@@ -808,8 +817,9 @@ struct fc_disc {
* @lso_max: The maximum large offload send size
* @fcts: FC-4 type mask
* @lp_mutex: Mutex to protect the local port
- * @list: Handle for list of local ports
+ * @list: Linkage on list of vport peers
* @retry_work: Handle to local port for delayed retry context
+ * @lport_list: Linkage on module-wide list of local ports
* @prov: Pointers available for use by passive FC-4 providers
*/
struct fc_lport {
@@ -863,6 +873,7 @@ struct fc_lport {
struct mutex lp_mutex;
struct list_head list;
struct delayed_work retry_work;
+ struct list_head lport_list;
void *prov[FC_FC4_PROV_SIZE];
};
@@ -1026,6 +1037,7 @@ int fc_set_mfs(struct fc_lport *, u32 mfs);
struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
int fc_lport_bsg_request(struct fc_bsg_job *);
+void fc_lport_iterate(void (*func)(struct fc_lport *, void *), void *);
/*
* REMOTE PORT LAYER

View File

@@ -1,25 +0,0 @@
libfc: add definition for task attribute mask
The FCP command header definition should define a mask for
the task attribute field. This adds that #define.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
include/scsi/fc/fc_fcp.h | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
---
diff --git a/include/scsi/fc/fc_fcp.h b/include/scsi/fc/fc_fcp.h
index 747e2c7..8e9b222 100644
--- a/include/scsi/fc/fc_fcp.h
+++ b/include/scsi/fc/fc_fcp.h
@@ -76,6 +76,7 @@ struct fcp_cmnd32 {
#define FCP_PTA_HEADQ 1 /* head of queue task attribute */
#define FCP_PTA_ORDERED 2 /* ordered task attribute */
#define FCP_PTA_ACA 4 /* auto. contigent allegiance */
+#define FCP_PTA_MASK 7 /* mask for task attribute field */
#define FCP_PRI_SHIFT 3 /* priority field starts in bit 3 */
#define FCP_PRI_RESVD_MASK 0x80 /* reserved bits in priority field */

View File

@@ -1,49 +0,0 @@
libfc: fix oops in point-to-point mode
In point-to-point mode, if the PLOGI to the remote port times
out, it can get deleted by the remote port module. Since there's
no reference by the local port, lport->ptp_data points to a freed
rport, and when the local port is reset and tries to logout again,
an oops occurs in mutex_lock_nested().
Hold a reference count on the point-to-point rdata.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_lport.c | 11 +++++++++--
1 files changed, 9 insertions(+), 2 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 507b6c5..a728e99 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -227,9 +227,12 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
u64 remote_wwnn)
{
mutex_lock(&lport->disc.disc_mutex);
- if (lport->ptp_rdata)
+ if (lport->ptp_rdata) {
lport->tt.rport_logoff(lport->ptp_rdata);
+ kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
+ }
lport->ptp_rdata = lport->tt.rport_create(lport, remote_fid);
+ kref_get(&lport->ptp_rdata->kref);
lport->ptp_rdata->ids.port_name = remote_wwpn;
lport->ptp_rdata->ids.node_name = remote_wwnn;
mutex_unlock(&lport->disc.disc_mutex);
@@ -988,7 +991,11 @@ static void fc_lport_reset_locked(struct fc_lport *lport)
if (lport->dns_rdata)
lport->tt.rport_logoff(lport->dns_rdata);
- lport->ptp_rdata = NULL;
+ if (lport->ptp_rdata) {
+ lport->tt.rport_logoff(lport->ptp_rdata);
+ kref_put(&lport->ptp_rdata->kref, lport->tt.rport_destroy);
+ lport->ptp_rdata = NULL;
+ }
lport->tt.disc_stop(lport);

View File

@@ -1,30 +0,0 @@
fcoe: call fcoe_ctlr_els_send even for ELS responses
In point-to-point mode, the destination MAC address for
the FLOGI response was zero because the LS_ACC for the FLOGI
wasn't getting intercepted by FIP.
Change to call fcoe_ctlr_els_send when sending any ELS,
not just requests.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/fcoe/fcoe.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 2f47ae7..2f3b73c 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1443,7 +1443,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
return 0;
}
- if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
+ if (unlikely(fh->fh_type == FC_TYPE_ELS) &&
fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))
return 0;

View File

@@ -1,33 +0,0 @@
libfcoe: fix debug message entering non-FIP mode
The debug message that indicated we are using non-FIP mode was
being printed only if we were already in non-FIP mode.
Also changed the message text to make it more clear the mode
is being set, not that the message is indicating how FLOGI
was received.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/fcoe/libfcoe.c | 6 +++---
1 files changed, 3 insertions(+), 3 deletions(-)
---
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 511cb6b..b2af53f 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -1333,9 +1333,9 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) {
memcpy(fip->dest_addr, sa, ETH_ALEN);
fip->map_dest = 0;
- if (fip->state == FIP_ST_NON_FIP)
- LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, "
- "using non-FIP mode\n");
+ if (fip->state == FIP_ST_AUTO)
+ LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. "
+ "Setting non-FIP mode\n");
fip->state = FIP_ST_NON_FIP;
}
spin_unlock_bh(&fip->lock);

View File

@@ -1,63 +0,0 @@
fcoe: save gateway address when receiving FLOGI request
In point-to-point mode, we need to save the source MAC
from received FLOGI requests to use as the destination MAC
for all outgoing frames. We stopped doing that at some point.
Use the lport_set_port_id method to catch incoming FLOGI frames
and pass them to fcoe_ctlr_recv_flogi() so it can save the source MAC.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/fcoe/fcoe.c | 24 ++++++++++++++++++++++++
1 files changed, 24 insertions(+), 0 deletions(-)
---
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 2f3b73c..546e69a 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -145,6 +145,7 @@ static int fcoe_vport_destroy(struct fc_vport *);
static int fcoe_vport_create(struct fc_vport *, bool disabled);
static int fcoe_vport_disable(struct fc_vport *, bool disable);
static void fcoe_set_vport_symbolic_name(struct fc_vport *);
+static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);
static struct libfc_function_template fcoe_libfc_fcn_templ = {
.frame_send = fcoe_xmit,
@@ -152,6 +153,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
.ddp_done = fcoe_ddp_done,
.elsct_send = fcoe_elsct_send,
.get_lesb = fcoe_get_lesb,
+ .lport_set_port_id = fcoe_set_port_id,
};
struct fc_function_template fcoe_transport_function = {
@@ -2630,3 +2632,25 @@ static void fcoe_get_lesb(struct fc_lport *lport,
lesb->lesb_miss_fka = htonl(mdac);
lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
}
+
+/**
+ * fcoe_set_port_id() - Callback from libfc when Port_ID is set.
+ * @lport: the local port
+ * @port_id: the port ID
+ * @fp: the received frame, if any, that caused the port_id to be set.
+ *
+ * This routine handles the case where we received a FLOGI and are
+ * entering point-to-point mode. We need to call fcoe_ctlr_recv_flogi()
+ * so it can set the non-mapped mode and gateway address.
+ *
+ * The FLOGI LS_ACC is handled by fcoe_flogi_resp().
+ */
+static void fcoe_set_port_id(struct fc_lport *lport,
+ u32 port_id, struct fc_frame *fp)
+{
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->fcoe;
+
+ if (fp && fc_frame_payload_op(fp) == ELS_FLOGI)
+ fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp);
+}

View File

@@ -1,30 +0,0 @@
libfc: recognize incoming FLOGI for point-to-point mode
When receiving a FLOGI request from a point-to-point peer,
the D_ID of 0xfffffe was not recognized as belonging to one
of the lports, so it was dropped.
Change fc_vport_id_lookup() to treat d_id 0xfffffe as a match.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_npiv.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c
index c68f6c7..45b6f1e 100644
--- a/drivers/scsi/libfc/fc_npiv.c
+++ b/drivers/scsi/libfc/fc_npiv.c
@@ -72,6 +72,9 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)
if (fc_host_port_id(n_port->host) == port_id)
return n_port;
+ if (port_id == FC_FID_FLOGI)
+ return n_port; /* for point-to-point */
+
mutex_lock(&n_port->lp_mutex);
list_for_each_entry(vn_port, &n_port->vports, list) {
if (fc_host_port_id(vn_port->host) == port_id) {

View File

@@ -1,32 +0,0 @@
libfc: point-to-point: send FLOGI LS_ACC to assigned D_DID
The method we've been using for point-to-point mode requires
that the LS_ACC for the FLOGI uses the D_ID and S_ID assigned
to the remote port and local port, not those in the exchange.
This is not the correct method, but for now, it's what works
with the old target, as well as with new targets based on libfc.
This patch changes the addresses used accordingly.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_lport.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index a728e99..a2a49a2 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -835,7 +835,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
*/
f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
ep = fc_seq_exch(sp);
- fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, remote_fid, local_fid,
FC_TYPE_ELS, f_ctl, 0);
lport->tt.seq_send(lport, sp, fp);

View File

@@ -1,13 +0,0 @@
--- a/include/scsi/fc_frame.h 2010-06-13 11:08:26.000000000 +0200
+++ b/include/scsi/fc_frame.h 2010-06-13 11:08:53.000000000 +0200
@@ -66,8 +66,8 @@ struct fcoe_rcv_info {
struct fc_fcp_pkt *fr_fsp; /* for the corresponding fcp I/O */
u32 fr_crc;
u16 fr_max_payload; /* max FC payload */
- enum fc_sof fr_sof; /* start of frame delimiter */
- enum fc_eof fr_eof; /* end of frame delimiter */
+ u8 fr_sof; /* start of frame delimiter */
+ u8 fr_eof; /* end of frame delimiter */
u8 fr_flags; /* flags - see below */
u8 granted_mac[ETH_ALEN]; /* FCoE MAC address */
};

View File

@@ -1,487 +0,0 @@
libfc: add hook for FC-4 provider registration
Allow FC-4 provider modules to hook into libfc, mostly for targets.
This should allow any FC-4 module to handle PRLI requests and maintain
process-association states.
Each provider registers its ops with libfc and then will be called for
any incoming PRLI for that FC-4 type on any instance. The provider
can decide whether to handle that particular instance using any method
it likes, such as ACLs or other configuration information.
A count is kept of the number of successful PRLIs from the remote port.
Providers are called back with an implicit PRLO when the remote port
is about to be deleted or has been reset.
fc_lport_recv_req() now sends incoming FC-4 requests to FC-4 providers,
and there is a built-in provider always registered for handling
incoming ELS requests.
The call to provider recv() routines uses rcu_read_lock()
so that providers aren't removed during the call. That lock is very
cheap and shouldn't affect any performance on ELS requests.
Providers can rely on the RCU lock to protect a session lookup as well.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_libfc.c | 60 ++++++++++++++++++
drivers/scsi/libfc/fc_libfc.h | 11 +++
drivers/scsi/libfc/fc_lport.c | 63 ++++++++++++++++---
drivers/scsi/libfc/fc_rport.c | 133 +++++++++++++++++++++++++++++++++--------
include/scsi/libfc.h | 26 ++++++++
5 files changed, 255 insertions(+), 38 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index 39f4b6a..ce0de44 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -34,6 +34,23 @@ unsigned int fc_debug_logging;
module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+DEFINE_MUTEX(fc_prov_mutex);
+
+/*
+ * Providers which primarily send requests and PRLIs.
+ */
+struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = {
+ [0] = &fc_rport_t0_prov,
+ [FC_TYPE_FCP] = &fc_rport_fcp_init,
+};
+
+/*
+ * Providers which receive requests.
+ */
+struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = {
+ [FC_TYPE_ELS] = &fc_lport_els_prov,
+};
+
/**
* libfc_init() - Initialize libfc.ko
*/
@@ -132,3 +149,46 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
}
return copy_len;
}
+
+/**
+ * fc_fc4_register_provider() - register FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ *
+ * Returns 0 on success, negative error otherwise.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+ struct fc4_prov **prov_entry;
+ int ret = 0;
+
+ if (type >= FC_FC4_PROV_SIZE)
+ return -EINVAL;
+ mutex_lock(&fc_prov_mutex);
+ prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type;
+ if (*prov_entry)
+ ret = -EBUSY;
+ else
+ *prov_entry = prov;
+ mutex_unlock(&fc_prov_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(fc_fc4_register_provider);
+
+/**
+ * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ */
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+ BUG_ON(type >= FC_FC4_PROV_SIZE);
+ mutex_lock(&fc_prov_mutex);
+ if (prov->recv)
+ rcu_assign_pointer(fc_passive_prov[type], NULL);
+ else
+ rcu_assign_pointer(fc_active_prov[type], NULL);
+ mutex_unlock(&fc_prov_mutex);
+ synchronize_rcu();
+}
+EXPORT_SYMBOL(fc_fc4_deregister_provider);
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index f5c0ca4..2323c80 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -82,6 +82,17 @@ extern unsigned int fc_debug_logging;
(lport)->host->host_no, ##args))
/*
+ * FC-4 Providers.
+ */
+extern struct fc4_prov *fc_active_prov[]; /* providers without recv */
+extern struct fc4_prov *fc_passive_prov[]; /* providers with recv */
+extern struct mutex fc_prov_mutex; /* lock over table changes */
+
+extern struct fc4_prov fc_rport_t0_prov; /* type 0 provider */
+extern struct fc4_prov fc_lport_els_prov; /* ELS provider */
+extern struct fc4_prov fc_rport_fcp_init; /* FCP initiator provider */
+
+/*
* Set up direct-data placement for this I/O request
*/
void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 79c9e3c..b05329c 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -844,7 +844,7 @@ out:
}
/**
- * fc_lport_recv_req() - The generic lport request handler
+ * fc_lport_recv_els_req() - The generic lport ELS request handler
* @lport: The local port that received the request
* @sp: The sequence the request is on
* @fp: The request frame
@@ -855,8 +855,8 @@ out:
* Locking Note: This function should not be called with the lport
* lock held becuase it will grab the lock.
*/
-static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
- struct fc_frame *fp)
+static void fc_lport_recv_els_req(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
@@ -870,8 +870,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
*/
if (!lport->link_up)
fc_frame_free(fp);
- else if (fh->fh_type == FC_TYPE_ELS &&
- fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+ else {
/*
* Check opcode.
*/
@@ -900,17 +899,59 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
}
recv(sp, fp, lport);
- } else {
- FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
- fr_eof(fp));
- fc_frame_free(fp);
}
mutex_unlock(&lport->lp_mutex);
+ lport->tt.exch_done(sp);
+}
+
+static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *spp_in,
+ struct fc_els_spp *spp_out)
+{
+ return FC_SPP_RESP_INVL;
+}
+
+struct fc4_prov fc_lport_els_prov = {
+ .prli = fc_lport_els_prli,
+ .recv = fc_lport_recv_els_req,
+};
+
+/**
+ * fc_lport_recv_req() - The generic lport request handler
+ * @lport: The lport that received the request
+ * @sp: The sequence the request is on
+ * @fp: The frame the request is in
+ *
+ * Locking Note: This function should not be called with the lport
+ * lock held becuase it may grab the lock.
+ */
+static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
+ struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc4_prov *prov;
/*
- * The common exch_done for all request may not be good
- * if any request requires longer hold on exhange. XXX
+ * Use RCU read lock and module_lock to be sure module doesn't
+ * deregister and get unloaded while we're calling it.
+ * try_module_get() is inlined and accepts a NULL parameter.
+ * Only ELSes and FCP target ops should come through here.
+ * The locking is unfortunate, and a better scheme is being sought.
*/
+ rcu_read_lock();
+ if (fh->fh_type >= FC_FC4_PROV_SIZE)
+ goto drop;
+ prov = rcu_dereference(fc_passive_prov[fh->fh_type]);
+ if (!prov || !try_module_get(prov->module))
+ goto drop;
+ rcu_read_unlock();
+ prov->recv(lport, sp, fp);
+ module_put(prov->module);
+ return;
+drop:
+ rcu_read_unlock();
+ FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
+ fc_frame_free(fp);
lport->tt.exch_done(sp);
}
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 39e440f..3ec4aa5 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -246,6 +246,8 @@ static void fc_rport_work(struct work_struct *work)
struct fc_rport_operations *rport_ops;
struct fc_rport_identifiers ids;
struct fc_rport *rport;
+ struct fc4_prov *prov;
+ u8 type;
int restart = 0;
mutex_lock(&rdata->rp_mutex);
@@ -295,6 +297,15 @@ static void fc_rport_work(struct work_struct *work)
case RPORT_EV_FAILED:
case RPORT_EV_LOGO:
case RPORT_EV_STOP:
+ if (rdata->prli_count) {
+ mutex_lock(&fc_prov_mutex);
+ for (type = 1; type < FC_FC4_PROV_SIZE; type++) {
+ prov = fc_passive_prov[type];
+ if (prov && prov->prlo)
+ prov->prlo(rdata);
+ }
+ mutex_unlock(&fc_prov_mutex);
+ }
port_id = rdata->ids.port_id;
mutex_unlock(&rdata->rp_mutex);
@@ -1434,6 +1445,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
struct fc_exch *ep;
struct fc_frame *fp;
struct fc_frame_header *fh;
+ struct fc4_prov *prov;
struct {
struct fc_els_prli prli;
struct fc_els_spp spp;
@@ -1443,10 +1455,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
unsigned int len;
unsigned int plen;
enum fc_els_spp_resp resp;
+ enum fc_els_spp_resp passive;
struct fc_seq_els_data rjt_data;
u32 f_ctl;
- u32 fcp_parm;
- u32 roles = FC_RPORT_ROLE_UNKNOWN;
rjt_data.fp = NULL;
fh = fc_frame_header_get(rx_fp);
@@ -1485,46 +1496,41 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
pp->prli.prli_len = htons(len);
len -= sizeof(struct fc_els_prli);
- /* reinitialize remote port roles */
- rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
/*
* Go through all the service parameter pages and build
* response. If plen indicates longer SPP than standard,
* use that. The entire response has been pre-cleared above.
*/
spp = &pp->spp;
+ mutex_lock(&fc_prov_mutex);
while (len >= plen) {
spp->spp_type = rspp->spp_type;
spp->spp_type_ext = rspp->spp_type_ext;
- spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
- resp = FC_SPP_RESP_ACK;
-
- switch (rspp->spp_type) {
- case 0: /* common to all FC-4 types */
- break;
- case FC_TYPE_FCP:
- fcp_parm = ntohl(rspp->spp_params);
- if (fcp_parm & FCP_SPPF_RETRY)
- rdata->flags |= FC_RP_FLAGS_RETRY;
- rdata->supported_classes = FC_COS_CLASS3;
- if (fcp_parm & FCP_SPPF_INIT_FCN)
- roles |= FC_RPORT_ROLE_FCP_INITIATOR;
- if (fcp_parm & FCP_SPPF_TARG_FCN)
- roles |= FC_RPORT_ROLE_FCP_TARGET;
- rdata->ids.roles = roles;
-
- spp->spp_params = htonl(lport->service_params);
- break;
- default:
- resp = FC_SPP_RESP_INVL;
- break;
+ resp = 0;
+
+ if (rspp->spp_type < FC_FC4_PROV_SIZE) {
+ prov = fc_active_prov[rspp->spp_type];
+ if (prov)
+ resp = prov->prli(rdata, plen, rspp, spp);
+ prov = fc_passive_prov[rspp->spp_type];
+ if (prov) {
+ passive = prov->prli(rdata, plen, rspp, spp);
+ if (!resp || passive == FC_SPP_RESP_ACK)
+ resp = passive;
+ }
+ }
+ if (!resp) {
+ if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
+ resp |= FC_SPP_RESP_CONF;
+ else
+ resp |= FC_SPP_RESP_INVL;
}
spp->spp_flags |= resp;
len -= plen;
rspp = (struct fc_els_spp *)((char *)rspp + plen);
spp = (struct fc_els_spp *)((char *)spp + plen);
}
+ mutex_unlock(&fc_prov_mutex);
/*
* Send LS_ACC. If this fails, the originator should retry.
@@ -1669,6 +1675,79 @@ int fc_rport_init(struct fc_lport *lport)
EXPORT_SYMBOL(fc_rport_init);
/**
+ * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ *
+ * Returns the value for the response code to be placed in spp_flags;
+ * Returns 0 if not an initiator.
+ */
+static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp,
+ struct fc_els_spp *spp)
+{
+ struct fc_lport *lport = rdata->local_port;
+ u32 fcp_parm;
+
+ fcp_parm = ntohl(rspp->spp_params);
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+ if (fcp_parm & FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ rdata->supported_classes = FC_COS_CLASS3;
+
+ if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR))
+ return 0;
+
+ spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+
+ /*
+ * OR in our service parameters with other providers (target), if any.
+ */
+ fcp_parm = ntohl(spp->spp_params);
+ spp->spp_params = htonl(fcp_parm | lport->service_params);
+ return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for FCP initiator.
+ */
+struct fc4_prov fc_rport_fcp_init = {
+ .prli = fc_rport_fcp_prli,
+};
+
+/**
+ * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ */
+static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp,
+ struct fc_els_spp *spp)
+{
+ if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR)
+ return FC_SPP_RESP_INVL;
+ return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for type 0 service parameters.
+ *
+ * This handles the special case of type 0 which is always successful
+ * but doesn't do anything otherwise.
+ */
+struct fc4_prov fc_rport_t0_prov = {
+ .prli = fc_rport_t0_prli,
+};
+
+/**
* fc_setup_rport() - Initialize the rport_event_queue
*/
int fc_setup_rport()
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 7495c0b..4ac290f 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -35,6 +35,8 @@
#include <scsi/fc_frame.h>
+#define FC_FC4_PROV_SIZE (FC_TYPE_FCP + 1) /* size of tables */
+
/*
* libfc error codes
*/
@@ -195,6 +197,7 @@ struct fc_rport_libfc_priv {
* @rp_mutex: The mutex that protects the remote port
* @retry_work: Handle for retries
* @event_callback: Callback when READY, FAILED or LOGO states complete
+ * @prli_count: Count of open PRLI sessions in providers
*/
struct fc_rport_priv {
struct fc_lport *local_port;
@@ -216,6 +219,7 @@ struct fc_rport_priv {
struct list_head peers;
struct work_struct event_work;
u32 supported_classes;
+ u16 prli_count;
};
/**
@@ -857,6 +861,28 @@ struct fc_lport {
struct delayed_work retry_work;
};
+/**
+ * struct fc4_prov - FC-4 provider registration
+ * @prli: Handler for incoming PRLI
+ * @prlo: Handler for session reset
+ * @recv: Handler for incoming request
+ * @module: Pointer to module. May be NULL.
+ */
+struct fc4_prov {
+ int (*prli)(struct fc_rport_priv *, u32 spp_len,
+ const struct fc_els_spp *spp_in,
+ struct fc_els_spp *spp_out);
+ void (*prlo)(struct fc_rport_priv *);
+ void (*recv)(struct fc_lport *, struct fc_seq *, struct fc_frame *);
+ struct module *module;
+};
+
+/*
+ * Register FC-4 provider with libfc.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *);
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *);
+
/*
* FC_LPORT HELPER FUNCTIONS
*****************************/

View File

@@ -1,84 +0,0 @@
libfc: add method for setting handler for incoming exchange
Add a method for setting handler for incoming exchange.
For multi-sequence exchanges, this allows the target driver
to add a response handler for handling subsequent sequences,
and exchange manager resets.
The new function is called fc_seq_set_resp().
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_exch.c | 19 +++++++++++++++++++
include/scsi/libfc.h | 11 ++++++++++-
2 files changed, 29 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 104e0fb..1828d1d 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -545,6 +545,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
return sp;
}
+/*
+ * Set the response handler for the exchange associated with a sequence.
+ */
+static void fc_seq_set_resp(struct fc_seq *sp,
+ void (*resp)(struct fc_seq *, struct fc_frame *,
+ void *),
+ void *arg)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ ep->resp = resp;
+ ep->arg = arg;
+ spin_unlock_bh(&ep->ex_lock);
+}
+
/**
* fc_seq_exch_abort() - Abort an exchange and sequence
* @req_sp: The sequence to be aborted
@@ -2263,6 +2279,9 @@ int fc_exch_init(struct fc_lport *lport)
if (!lport->tt.seq_start_next)
lport->tt.seq_start_next = fc_seq_start_next;
+ if (!lport->tt.seq_set_resp)
+ lport->tt.seq_set_resp = fc_seq_set_resp;
+
if (!lport->tt.exch_seq_send)
lport->tt.exch_seq_send = fc_exch_seq_send;
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 4ac290f..64a4756 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -571,6 +571,16 @@ struct libfc_function_template {
struct fc_seq *(*seq_start_next)(struct fc_seq *);
/*
+ * Set a response handler for the exchange of the sequence.
+ *
+ * STATUS: OPTIONAL
+ */
+ void (*seq_set_resp)(struct fc_seq *sp,
+ void (*resp)(struct fc_seq *, struct fc_frame *,
+ void *),
+ void *arg);
+
+ /*
* Reset an exchange manager, completing all sequences and exchanges.
* If s_id is non-zero, reset only exchanges originating from that FID.
* If d_id is non-zero, reset only exchanges sending to that FID.
@@ -1056,7 +1066,6 @@ struct fc_seq *fc_elsct_send(struct fc_lport *, u32 did,
void fc_lport_flogi_resp(struct fc_seq *, struct fc_frame *, void *);
void fc_lport_logo_resp(struct fc_seq *, struct fc_frame *, void *);
-
/*
* EXCHANGE MANAGER LAYER
*****************************/

View File

@@ -1,36 +0,0 @@
libfc: add local port hook for provider session lookup
The target provider needs a per-instance lookup table
or other way to lookup sessions quickly without going through
a linear list or serializing too much.
Add a simple void * array indexed by FC-4 type to the fc_lport.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
include/scsi/libfc.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
---
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 64a4756..9d7c8e3 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -816,6 +816,7 @@ struct fc_disc {
* @lp_mutex: Mutex to protect the local port
* @list: Handle for list of local ports
* @retry_work: Handle to local port for delayed retry context
+ * @prov: Pointers available for use by passive FC-4 providers
*/
struct fc_lport {
/* Associations */
@@ -869,6 +870,7 @@ struct fc_lport {
struct mutex lp_mutex;
struct list_head list;
struct delayed_work retry_work;
+ void *prov[FC_FC4_PROV_SIZE];
};
/**

View File

@@ -1,167 +0,0 @@
libfc: add hook to notify providers of local port changes
When an SCST provider is registered, it needs to know what
local ports are available for configuration as targets.
Add a notifier chain that is invoked when any local port
that is added or deleted.
Maintain a global list of local ports and add an
interator function that calls a given function for
every existing local port. This is used when first
loading a provider.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_libfc.c | 41 +++++++++++++++++++++++++++++++++++++++++
drivers/scsi/libfc/fc_libfc.h | 2 ++
drivers/scsi/libfc/fc_lport.c | 2 ++
include/scsi/libfc.h | 14 +++++++++++++-
4 files changed, 58 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index ce0de44..abd108a 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -35,6 +35,10 @@ module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
DEFINE_MUTEX(fc_prov_mutex);
+static LIST_HEAD(fc_local_ports);
+struct blocking_notifier_head fc_lport_notifier_head =
+ BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head);
+EXPORT_SYMBOL(fc_lport_notifier_head);
/*
* Providers which primarily send requests and PRLIs.
@@ -150,6 +154,17 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
return copy_len;
}
+void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg)
+{
+ struct fc_lport *lport;
+
+ mutex_lock(&fc_prov_mutex);
+ list_for_each_entry(lport, &fc_local_ports, lport_list)
+ notify(lport, arg);
+ mutex_unlock(&fc_prov_mutex);
+}
+EXPORT_SYMBOL(fc_lport_iterate);
+
/**
* fc_fc4_register_provider() - register FC-4 upper-level provider.
* @type: FC-4 type, such as FC_TYPE_FCP
@@ -192,3 +207,29 @@ void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
synchronize_rcu();
}
EXPORT_SYMBOL(fc_fc4_deregister_provider);
+
+/**
+ * fc_fc4_add_lport() - add new local port to list and run notifiers.
+ * @lport: The new local port.
+ */
+void fc_fc4_add_lport(struct fc_lport *lport)
+{
+ mutex_lock(&fc_prov_mutex);
+ list_add_tail(&lport->lport_list, &fc_local_ports);
+ blocking_notifier_call_chain(&fc_lport_notifier_head,
+ FC_LPORT_EV_ADD, lport);
+ mutex_unlock(&fc_prov_mutex);
+}
+
+/**
+ * fc_fc4_del_lport() - remove local port from list and run notifiers.
+ * @lport: The new local port.
+ */
+void fc_fc4_del_lport(struct fc_lport *lport)
+{
+ mutex_lock(&fc_prov_mutex);
+ list_del(&lport->lport_list);
+ blocking_notifier_call_chain(&fc_lport_notifier_head,
+ FC_LPORT_EV_DEL, lport);
+ mutex_unlock(&fc_prov_mutex);
+}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index 2323c80..85ce01e 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -111,6 +111,8 @@ void fc_destroy_fcp(void);
* Internal libfc functions
*/
const char *fc_els_resp_type(struct fc_frame *);
+extern void fc_fc4_add_lport(struct fc_lport *);
+extern void fc_fc4_del_lport(struct fc_lport *);
/*
* Copies a buffer into an sg list
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index b05329c..375a2a7 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -647,6 +647,7 @@ int fc_lport_destroy(struct fc_lport *lport)
lport->tt.fcp_abort_io(lport);
lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
+ fc_fc4_del_lport(lport);
return 0;
}
EXPORT_SYMBOL(fc_lport_destroy);
@@ -1639,6 +1640,7 @@ int fc_lport_init(struct fc_lport *lport)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+ fc_fc4_add_lport(lport);
return 0;
}
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 9d7c8e3..2fa3538 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -775,6 +775,15 @@ struct fc_disc {
enum fc_disc_event);
};
+/*
+ * Local port notifier and events.
+ */
+extern struct blocking_notifier_head fc_lport_notifier_head;
+enum fc_lport_event {
+ FC_LPORT_EV_ADD,
+ FC_LPORT_EV_DEL,
+};
+
/**
* struct fc_lport - Local port
* @host: The SCSI host associated with a local port
@@ -814,8 +823,9 @@ struct fc_disc {
* @lso_max: The maximum large offload send size
* @fcts: FC-4 type mask
* @lp_mutex: Mutex to protect the local port
- * @list: Handle for list of local ports
+ * @list: Linkage on list of vport peers
* @retry_work: Handle to local port for delayed retry context
+ * @lport_list: Linkage on module-wide list of local ports
* @prov: Pointers available for use by passive FC-4 providers
*/
struct fc_lport {
@@ -870,6 +880,7 @@ struct fc_lport {
struct mutex lp_mutex;
struct list_head list;
struct delayed_work retry_work;
+ struct list_head lport_list;
void *prov[FC_FC4_PROV_SIZE];
};
@@ -1024,6 +1035,7 @@ int fc_set_mfs(struct fc_lport *, u32 mfs);
struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
int fc_lport_bsg_request(struct fc_bsg_job *);
+void fc_lport_iterate(void (*func)(struct fc_lport *, void *), void *);
/*
* REMOTE PORT LAYER

View File

@@ -1,483 +0,0 @@
libfc: add hook for FC-4 provider registration
Allow FC-4 provider modules to hook into libfc, mostly for targets.
This should allow any FC-4 module to handle PRLI requests and maintain
process-association states.
Each provider registers its ops with libfc and then will be called for
any incoming PRLI for that FC-4 type on any instance. The provider
can decide whether to handle that particular instance using any method
it likes, such as ACLs or other configuration information.
A count is kept of the number of successful PRLIs from the remote port.
Providers are called back with an implicit PRLO when the remote port
is about to be deleted or has been reset.
fc_lport_recv_req() now sends incoming FC-4 requests to FC-4 providers,
and there is a built-in provider always registered for handling
incoming ELS requests.
The call to provider recv() routines uses rcu_read_lock()
so that providers aren't removed during the call. That lock is very
cheap and shouldn't affect any performance on ELS requests.
Providers can rely on the RCU lock to protect a session lookup as well.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_libfc.c | 60 ++++++++++++++++++
drivers/scsi/libfc/fc_libfc.h | 11 +++
drivers/scsi/libfc/fc_lport.c | 60 ++++++++++++++++--
drivers/scsi/libfc/fc_rport.c | 133 +++++++++++++++++++++++++++++++++--------
include/scsi/libfc.h | 26 ++++++++
5 files changed, 254 insertions(+), 36 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index 6a48c28..ae3abef 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -35,6 +35,23 @@ unsigned int fc_debug_logging;
module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
+DEFINE_MUTEX(fc_prov_mutex);
+
+/*
+ * Providers which primarily send requests and PRLIs.
+ */
+struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = {
+ [0] = &fc_rport_t0_prov,
+ [FC_TYPE_FCP] = &fc_rport_fcp_init,
+};
+
+/*
+ * Providers which receive requests.
+ */
+struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = {
+ [FC_TYPE_ELS] = &fc_lport_els_prov,
+};
+
/**
* libfc_init() - Initialize libfc.ko
*/
@@ -210,3 +227,46 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset);
}
EXPORT_SYMBOL(fc_fill_reply_hdr);
+
+/**
+ * fc_fc4_register_provider() - register FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ *
+ * Returns 0 on success, negative error otherwise.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+ struct fc4_prov **prov_entry;
+ int ret = 0;
+
+ if (type >= FC_FC4_PROV_SIZE)
+ return -EINVAL;
+ mutex_lock(&fc_prov_mutex);
+ prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type;
+ if (*prov_entry)
+ ret = -EBUSY;
+ else
+ *prov_entry = prov;
+ mutex_unlock(&fc_prov_mutex);
+ return ret;
+}
+EXPORT_SYMBOL(fc_fc4_register_provider);
+
+/**
+ * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider.
+ * @type: FC-4 type, such as FC_TYPE_FCP
+ * @prov: structure describing provider including ops vector.
+ */
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
+{
+ BUG_ON(type >= FC_FC4_PROV_SIZE);
+ mutex_lock(&fc_prov_mutex);
+ if (prov->recv)
+ rcu_assign_pointer(fc_passive_prov[type], NULL);
+ else
+ rcu_assign_pointer(fc_active_prov[type], NULL);
+ mutex_unlock(&fc_prov_mutex);
+ synchronize_rcu();
+}
+EXPORT_SYMBOL(fc_fc4_deregister_provider);
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index 16d2162..a01b80d 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -82,6 +82,17 @@ extern unsigned int fc_debug_logging;
(lport)->host->host_no, ##args))
/*
+ * FC-4 Providers.
+ */
+extern struct fc4_prov *fc_active_prov[]; /* providers without recv */
+extern struct fc4_prov *fc_passive_prov[]; /* providers with recv */
+extern struct mutex fc_prov_mutex; /* lock over table changes */
+
+extern struct fc4_prov fc_rport_t0_prov; /* type 0 provider */
+extern struct fc4_prov fc_lport_els_prov; /* ELS provider */
+extern struct fc4_prov fc_rport_fcp_init; /* FCP initiator provider */
+
+/*
* Set up direct-data placement for this I/O request
*/
void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index d9b6e11..eec9d31 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -845,7 +845,7 @@ out:
}
/**
- * fc_lport_recv_req() - The generic lport request handler
+ * fc_lport_recv_els_req() - The generic lport ELS request handler
* @lport: The local port that received the request
* @fp: The request frame
*
@@ -855,9 +855,8 @@ out:
* Locking Note: This function should not be called with the lport
* lock held becuase it will grab the lock.
*/
-static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
+static void fc_lport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
{
- struct fc_frame_header *fh = fc_frame_header_get(fp);
void (*recv)(struct fc_lport *, struct fc_frame *);
mutex_lock(&lport->lp_mutex);
@@ -869,8 +868,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
*/
if (!lport->link_up)
fc_frame_free(fp);
- else if (fh->fh_type == FC_TYPE_ELS &&
- fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+ else {
/*
* Check opcode.
*/
@@ -899,14 +897,58 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
}
recv(lport, fp);
- } else {
- FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
- fr_eof(fp));
- fc_frame_free(fp);
}
mutex_unlock(&lport->lp_mutex);
}
+static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *spp_in,
+ struct fc_els_spp *spp_out)
+{
+ return FC_SPP_RESP_INVL;
+}
+
+struct fc4_prov fc_lport_els_prov = {
+ .prli = fc_lport_els_prli,
+ .recv = fc_lport_recv_els_req,
+};
+
+/**
+ * fc_lport_recv_req() - The generic lport request handler
+ * @lport: The lport that received the request
+ * @fp: The frame the request is in
+ *
+ * Locking Note: This function should not be called with the lport
+ * lock held becuase it may grab the lock.
+ */
+static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_frame_header *fh = fc_frame_header_get(fp);
+ struct fc4_prov *prov;
+
+ /*
+ * Use RCU read lock and module_lock to be sure module doesn't
+ * deregister and get unloaded while we're calling it.
+ * try_module_get() is inlined and accepts a NULL parameter.
+ * Only ELSes and FCP target ops should come through here.
+ * The locking is unfortunate, and a better scheme is being sought.
+ */
+ rcu_read_lock();
+ if (fh->fh_type >= FC_FC4_PROV_SIZE)
+ goto drop;
+ prov = rcu_dereference(fc_passive_prov[fh->fh_type]);
+ if (!prov || !try_module_get(prov->module))
+ goto drop;
+ rcu_read_unlock();
+ prov->recv(lport, fp);
+ module_put(prov->module);
+ return;
+drop:
+ rcu_read_unlock();
+ FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
+ fc_frame_free(fp);
+}
+
/**
* fc_lport_reset() - Reset a local port
* @lport: The local port which should be reset
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index b9f2286..9a3f1d3 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -257,6 +257,8 @@ static void fc_rport_work(struct work_struct *work)
struct fc_rport_operations *rport_ops;
struct fc_rport_identifiers ids;
struct fc_rport *rport;
+ struct fc4_prov *prov;
+ u8 type;
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
@@ -306,6 +308,15 @@ static void fc_rport_work(struct work_struct *work)
case RPORT_EV_FAILED:
case RPORT_EV_LOGO:
case RPORT_EV_STOP:
+ if (rdata->prli_count) {
+ mutex_lock(&fc_prov_mutex);
+ for (type = 1; type < FC_FC4_PROV_SIZE; type++) {
+ prov = fc_passive_prov[type];
+ if (prov && prov->prlo)
+ prov->prlo(rdata);
+ }
+ mutex_unlock(&fc_prov_mutex);
+ }
port_id = rdata->ids.port_id;
mutex_unlock(&rdata->rp_mutex);
@@ -1632,6 +1643,7 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
{
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
+ struct fc4_prov *prov;
struct {
struct fc_els_prli prli;
struct fc_els_spp spp;
@@ -1641,9 +1653,8 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
unsigned int len;
unsigned int plen;
enum fc_els_spp_resp resp;
+ enum fc_els_spp_resp passive;
struct fc_seq_els_data rjt_data;
- u32 fcp_parm;
- u32 roles = FC_RPORT_ROLE_UNKNOWN;
FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
fc_rport_state(rdata));
@@ -1677,46 +1688,41 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
pp->prli.prli_len = htons(len);
len -= sizeof(struct fc_els_prli);
- /* reinitialize remote port roles */
- rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
/*
* Go through all the service parameter pages and build
* response. If plen indicates longer SPP than standard,
* use that. The entire response has been pre-cleared above.
*/
spp = &pp->spp;
+ mutex_lock(&fc_prov_mutex);
while (len >= plen) {
spp->spp_type = rspp->spp_type;
spp->spp_type_ext = rspp->spp_type_ext;
- spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
- resp = FC_SPP_RESP_ACK;
-
- switch (rspp->spp_type) {
- case 0: /* common to all FC-4 types */
- break;
- case FC_TYPE_FCP:
- fcp_parm = ntohl(rspp->spp_params);
- if (fcp_parm & FCP_SPPF_RETRY)
- rdata->flags |= FC_RP_FLAGS_RETRY;
- rdata->supported_classes = FC_COS_CLASS3;
- if (fcp_parm & FCP_SPPF_INIT_FCN)
- roles |= FC_RPORT_ROLE_FCP_INITIATOR;
- if (fcp_parm & FCP_SPPF_TARG_FCN)
- roles |= FC_RPORT_ROLE_FCP_TARGET;
- rdata->ids.roles = roles;
-
- spp->spp_params = htonl(lport->service_params);
- break;
- default:
- resp = FC_SPP_RESP_INVL;
- break;
+ resp = 0;
+
+ if (rspp->spp_type < FC_FC4_PROV_SIZE) {
+ prov = fc_active_prov[rspp->spp_type];
+ if (prov)
+ resp = prov->prli(rdata, plen, rspp, spp);
+ prov = fc_passive_prov[rspp->spp_type];
+ if (prov) {
+ passive = prov->prli(rdata, plen, rspp, spp);
+ if (!resp || passive == FC_SPP_RESP_ACK)
+ resp = passive;
+ }
+ }
+ if (!resp) {
+ if (spp->spp_flags & FC_SPP_EST_IMG_PAIR)
+ resp |= FC_SPP_RESP_CONF;
+ else
+ resp |= FC_SPP_RESP_INVL;
}
spp->spp_flags |= resp;
len -= plen;
rspp = (struct fc_els_spp *)((char *)rspp + plen);
spp = (struct fc_els_spp *)((char *)spp + plen);
}
+ mutex_unlock(&fc_prov_mutex);
/*
* Send LS_ACC. If this fails, the originator should retry.
@@ -1886,6 +1892,79 @@ int fc_rport_init(struct fc_lport *lport)
EXPORT_SYMBOL(fc_rport_init);
/**
+ * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator.
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ *
+ * Returns the value for the response code to be placed in spp_flags;
+ * Returns 0 if not an initiator.
+ */
+static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp,
+ struct fc_els_spp *spp)
+{
+ struct fc_lport *lport = rdata->local_port;
+ u32 fcp_parm;
+
+ fcp_parm = ntohl(rspp->spp_params);
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ if (fcp_parm & FCP_SPPF_INIT_FCN)
+ rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcp_parm & FCP_SPPF_TARG_FCN)
+ rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+ if (fcp_parm & FCP_SPPF_RETRY)
+ rdata->flags |= FC_RP_FLAGS_RETRY;
+ rdata->supported_classes = FC_COS_CLASS3;
+
+ if (!(lport->service_params & FC_RPORT_ROLE_FCP_INITIATOR))
+ return 0;
+
+ spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR;
+
+ /*
+ * OR in our service parameters with other providers (target), if any.
+ */
+ fcp_parm = ntohl(spp->spp_params);
+ spp->spp_params = htonl(fcp_parm | lport->service_params);
+ return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for FCP initiator.
+ */
+struct fc4_prov fc_rport_fcp_init = {
+ .prli = fc_rport_fcp_prli,
+};
+
+/**
+ * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0
+ * @rdata: remote port private
+ * @spp_len: service parameter page length
+ * @rspp: received service parameter page
+ * @spp: response service parameter page
+ */
+static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len,
+ const struct fc_els_spp *rspp,
+ struct fc_els_spp *spp)
+{
+ if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR)
+ return FC_SPP_RESP_INVL;
+ return FC_SPP_RESP_ACK;
+}
+
+/*
+ * FC-4 provider ops for type 0 service parameters.
+ *
+ * This handles the special case of type 0 which is always successful
+ * but doesn't do anything otherwise.
+ */
+struct fc4_prov fc_rport_t0_prov = {
+ .prli = fc_rport_t0_prli,
+};
+
+/**
* fc_setup_rport() - Initialize the rport_event_queue
*/
int fc_setup_rport()
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 14be49b..8b907e1 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -35,6 +35,8 @@
#include <scsi/fc_frame.h>
+#define FC_FC4_PROV_SIZE (FC_TYPE_FCP + 1) /* size of tables */
+
/*
* libfc error codes
*/
@@ -179,6 +181,7 @@ struct fc_rport_libfc_priv {
* @rp_mutex: The mutex that protects the remote port
* @retry_work: Handle for retries
* @event_callback: Callback when READY, FAILED or LOGO states complete
+ * @prli_count: Count of open PRLI sessions in providers
* @rcu: Structure used for freeing in an RCU-safe manner
*/
struct fc_rport_priv {
@@ -202,6 +205,7 @@ struct fc_rport_priv {
struct list_head peers;
struct work_struct event_work;
u32 supported_classes;
+ u16 prli_count;
struct rcu_head rcu;
};
@@ -850,6 +854,28 @@ struct fc_lport {
struct delayed_work retry_work;
};
+/**
+ * struct fc4_prov - FC-4 provider registration
+ * @prli: Handler for incoming PRLI
+ * @prlo: Handler for session reset
+ * @recv: Handler for incoming request
+ * @module: Pointer to module. May be NULL.
+ */
+struct fc4_prov {
+ int (*prli)(struct fc_rport_priv *, u32 spp_len,
+ const struct fc_els_spp *spp_in,
+ struct fc_els_spp *spp_out);
+ void (*prlo)(struct fc_rport_priv *);
+ void (*recv)(struct fc_lport *, struct fc_frame *);
+ struct module *module;
+};
+
+/*
+ * Register FC-4 provider with libfc.
+ */
+int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *);
+void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *);
+
/*
* FC_LPORT HELPER FUNCTIONS
*****************************/

View File

@@ -1,84 +0,0 @@
libfc: add method for setting handler for incoming exchange
Add a method for setting handler for incoming exchange.
For multi-sequence exchanges, this allows the target driver
to add a response handler for handling subsequent sequences,
and exchange manager resets.
The new function is called fc_seq_set_resp().
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_exch.c | 19 +++++++++++++++++++
include/scsi/libfc.h | 11 ++++++++++-
2 files changed, 29 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index ec2a1ae..0000ddf 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -544,6 +544,22 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
return sp;
}
+/*
+ * Set the response handler for the exchange associated with a sequence.
+ */
+static void fc_seq_set_resp(struct fc_seq *sp,
+ void (*resp)(struct fc_seq *, struct fc_frame *,
+ void *),
+ void *arg)
+{
+ struct fc_exch *ep = fc_seq_exch(sp);
+
+ spin_lock_bh(&ep->ex_lock);
+ ep->resp = resp;
+ ep->arg = arg;
+ spin_unlock_bh(&ep->ex_lock);
+}
+
/**
* fc_seq_exch_abort() - Abort an exchange and sequence
* @req_sp: The sequence to be aborted
@@ -2297,6 +2313,9 @@ int fc_exch_init(struct fc_lport *lport)
if (!lport->tt.seq_start_next)
lport->tt.seq_start_next = fc_seq_start_next;
+ if (!lport->tt.seq_set_resp)
+ lport->tt.seq_set_resp = fc_seq_set_resp;
+
if (!lport->tt.exch_seq_send)
lport->tt.exch_seq_send = fc_exch_seq_send;
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 8b907e1..06cabab 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -564,6 +564,16 @@ struct libfc_function_template {
struct fc_seq *(*seq_assign)(struct fc_lport *, struct fc_frame *);
/*
+ * Set a response handler for the exchange of the sequence.
+ *
+ * STATUS: OPTIONAL
+ */
+ void (*seq_set_resp)(struct fc_seq *sp,
+ void (*resp)(struct fc_seq *, struct fc_frame *,
+ void *),
+ void *arg);
+
+ /*
* Reset an exchange manager, completing all sequences and exchanges.
* If s_id is non-zero, reset only exchanges originating from that FID.
* If d_id is non-zero, reset only exchanges sending to that FID.
@@ -1059,7 +1069,6 @@ void fc_fill_reply_hdr(struct fc_frame *, const struct fc_frame *,
void fc_fill_hdr(struct fc_frame *, const struct fc_frame *,
enum fc_rctl, u32 f_ctl, u16 seq_cnt, u32 parm_offset);
-
/*
* EXCHANGE MANAGER LAYER
*****************************/

View File

@@ -1,36 +0,0 @@
libfc: add local port hook for provider session lookup
The target provider needs a per-instance lookup table
or other way to lookup sessions quickly without going through
a linear list or serializing too much.
Add a simple void * array indexed by FC-4 type to the fc_lport.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
include/scsi/libfc.h | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
---
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 06cabab..769d480 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -807,6 +807,7 @@ struct fc_disc {
* @lp_mutex: Mutex to protect the local port
* @list: Handle for list of local ports
* @retry_work: Handle to local port for delayed retry context
+ * @prov: Pointers available for use by passive FC-4 providers
*/
struct fc_lport {
/* Associations */
@@ -862,6 +863,7 @@ struct fc_lport {
struct mutex lp_mutex;
struct list_head list;
struct delayed_work retry_work;
+ void *prov[FC_FC4_PROV_SIZE];
};
/**

View File

@@ -1,167 +0,0 @@
libfc: add hook to notify providers of local port changes
When an SCST provider is registered, it needs to know what
local ports are available for configuration as targets.
Add a notifier chain that is invoked when any local port
that is added or deleted.
Maintain a global list of local ports and add an
interator function that calls a given function for
every existing local port. This is used when first
loading a provider.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_libfc.c | 41 +++++++++++++++++++++++++++++++++++++++++
drivers/scsi/libfc/fc_libfc.h | 2 ++
drivers/scsi/libfc/fc_lport.c | 2 ++
include/scsi/libfc.h | 14 +++++++++++++-
4 files changed, 58 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index ae3abef..753b7a8 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -36,6 +36,10 @@ module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
DEFINE_MUTEX(fc_prov_mutex);
+static LIST_HEAD(fc_local_ports);
+struct blocking_notifier_head fc_lport_notifier_head =
+ BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head);
+EXPORT_SYMBOL(fc_lport_notifier_head);
/*
* Providers which primarily send requests and PRLIs.
@@ -151,6 +155,17 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
return copy_len;
}
+void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg)
+{
+ struct fc_lport *lport;
+
+ mutex_lock(&fc_prov_mutex);
+ list_for_each_entry(lport, &fc_local_ports, lport_list)
+ notify(lport, arg);
+ mutex_unlock(&fc_prov_mutex);
+}
+EXPORT_SYMBOL(fc_lport_iterate);
+
/**
* fc_fill_hdr() - fill FC header fields based on request
* @fp: reply frame containing header to be filled in
@@ -270,3 +285,29 @@ void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov)
synchronize_rcu();
}
EXPORT_SYMBOL(fc_fc4_deregister_provider);
+
+/**
+ * fc_fc4_add_lport() - add new local port to list and run notifiers.
+ * @lport: The new local port.
+ */
+void fc_fc4_add_lport(struct fc_lport *lport)
+{
+ mutex_lock(&fc_prov_mutex);
+ list_add_tail(&lport->lport_list, &fc_local_ports);
+ blocking_notifier_call_chain(&fc_lport_notifier_head,
+ FC_LPORT_EV_ADD, lport);
+ mutex_unlock(&fc_prov_mutex);
+}
+
+/**
+ * fc_fc4_del_lport() - remove local port from list and run notifiers.
+ * @lport: The new local port.
+ */
+void fc_fc4_del_lport(struct fc_lport *lport)
+{
+ mutex_lock(&fc_prov_mutex);
+ list_del(&lport->lport_list);
+ blocking_notifier_call_chain(&fc_lport_notifier_head,
+ FC_LPORT_EV_DEL, lport);
+ mutex_unlock(&fc_prov_mutex);
+}
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index a01b80d..a980482 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -111,6 +111,8 @@ void fc_destroy_fcp(void);
* Internal libfc functions
*/
const char *fc_els_resp_type(struct fc_frame *);
+extern void fc_fc4_add_lport(struct fc_lport *);
+extern void fc_fc4_del_lport(struct fc_lport *);
/*
* Copies a buffer into an sg list
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index eec9d31..e2311f0 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -629,6 +629,7 @@ int fc_lport_destroy(struct fc_lport *lport)
lport->tt.fcp_abort_io(lport);
lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
+ fc_fc4_del_lport(lport);
return 0;
}
EXPORT_SYMBOL(fc_lport_destroy);
@@ -1628,6 +1629,7 @@ int fc_lport_init(struct fc_lport *lport)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+ fc_fc4_add_lport(lport);
return 0;
}
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 769d480..af8a6e8 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -765,6 +765,15 @@ struct fc_disc {
enum fc_disc_event);
};
+/*
+ * Local port notifier and events.
+ */
+extern struct blocking_notifier_head fc_lport_notifier_head;
+enum fc_lport_event {
+ FC_LPORT_EV_ADD,
+ FC_LPORT_EV_DEL,
+};
+
/**
* struct fc_lport - Local port
* @host: The SCSI host associated with a local port
@@ -805,8 +814,9 @@ struct fc_disc {
* @lso_max: The maximum large offload send size
* @fcts: FC-4 type mask
* @lp_mutex: Mutex to protect the local port
- * @list: Handle for list of local ports
+ * @list: Linkage on list of vport peers
* @retry_work: Handle to local port for delayed retry context
+ * @lport_list: Linkage on module-wide list of local ports
* @prov: Pointers available for use by passive FC-4 providers
*/
struct fc_lport {
@@ -863,6 +873,7 @@ struct fc_lport {
struct mutex lp_mutex;
struct list_head list;
struct delayed_work retry_work;
+ struct list_head lport_list;
void *prov[FC_FC4_PROV_SIZE];
};
@@ -1018,6 +1029,7 @@ struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
int fc_lport_bsg_request(struct fc_bsg_job *);
void fc_lport_set_local_id(struct fc_lport *, u32 port_id);
+void fc_lport_iterate(void (*func)(struct fc_lport *, void *), void *);
/*
* REMOTE PORT LAYER

View File

@@ -1,33 +0,0 @@
fcoe: don't deliver FCP frames to target modules in softirq context
Under certain conditions when FCP frames arrive on the correct CPU
we deliver them in the softirq context. This is unexpected for
target modules like tcm_fc and fcst, and causes them to get errors.
Add a check for the FC frame header F_CTL bit EX_CTX, and don't
deliver in softirq context if that bit is off.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/fcoe/fcoe.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
---
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 44a0759..d222ae4 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1293,8 +1293,11 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
* This lets us process completions without context switching from the
* NET_RX softirq, to our receive processing thread, and then back to
* BLOCK softirq context.
+ * Don't do this if EX_CTX is off as it causes problems for target
+ * modules that don't expect to be called in softirq context.
*/
if (fh->fh_type == FC_TYPE_FCP &&
+ (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX) &&
cpu == smp_processor_id() &&
skb_queue_empty(&fps->fcoe_rx_list)) {
spin_unlock_bh(&fps->fcoe_rx_list.lock);

View File

@@ -1,33 +0,0 @@
fcoe: don't deliver FCP frames to target modules in softirq context
Under certain conditions when FCP frames arrive on the correct CPU
we deliver them in the softirq context. This is unexpected for
target modules like tcm_fc and fcst, and causes them to get errors.
Add a check for the FC frame header F_CTL bit EX_CTX, and don't
deliver in softirq context if that bit is off.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/fcoe/fcoe.c | 3 +++
1 files changed, 3 insertions(+), 0 deletions(-)
---
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 844d618..e4c3128 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1340,8 +1340,11 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
* This lets us process completions without context switching from the
* NET_RX softirq, to our receive processing thread, and then back to
* BLOCK softirq context.
+ * Don't do this if EX_CTX is off as it causes problems for target
+ * modules that don't expect to be called in softirq context.
*/
if (fh->fh_type == FC_TYPE_FCP &&
+ (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX) &&
cpu == smp_processor_id() &&
skb_queue_empty(&fps->fcoe_rx_list)) {
spin_unlock_bh(&fps->fcoe_rx_list.lock);

View File

@@ -1,26 +0,0 @@
libfcoe: fix stack damage caused by using union instead of struct
TBD. This was already submitted by someone else
but didn't make it into 2.6.36.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/fcoe/libfcoe.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index aa503d8..bc17c71 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -2296,7 +2296,7 @@ static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
struct fip_header *fiph;
enum fip_vn2vn_subcode sub;
- union {
+ struct {
struct fc_rport_priv rdata;
struct fcoe_rport frport;
} buf;

View File

@@ -1,71 +0,0 @@
libfc: export seq_release() for users of seq_assign()
Target modules using lport->tt.seq_assign() get a hold on the
exchange but have no way of releasing it. Add that.
Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
---
drivers/scsi/libfc/fc_exch.c | 14 ++++++++++++++
include/scsi/libfc.h | 7 +++++++
2 files changed, 21 insertions(+), 0 deletions(-)
---
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 0000ddf..0deb901 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1252,6 +1252,8 @@ free:
* @fp: The request frame
*
* On success, the sequence pointer will be returned and also in fr_seq(@fp).
+ * A reference will be held on the exchange/sequence for the caller, which
+ * must call fc_seq_release().
*/
static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
{
@@ -1269,6 +1271,15 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
}
/**
+ * fc_seq_release() - Release the hold
+ * @sp: The sequence.
+ */
+static void fc_seq_release(struct fc_seq *sp)
+{
+ fc_exch_release(fc_seq_exch(sp));
+}
+
+/**
* fc_exch_recv_req() - Handler for an incoming request
* @lport: The local port that received the request
* @mp: The EM that the exchange is on
@@ -2337,6 +2348,9 @@ int fc_exch_init(struct fc_lport *lport)
if (!lport->tt.seq_assign)
lport->tt.seq_assign = fc_seq_assign;
+ if (!lport->tt.seq_release)
+ lport->tt.seq_release = fc_seq_release;
+
return 0;
}
EXPORT_SYMBOL(fc_exch_init);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index af8a6e8..875e3a8 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -574,6 +574,13 @@ struct libfc_function_template {
void *arg);
/*
+ * Release the reference on the sequence returned by seq_assign().
+ *
+ * STATUS: OPTIONAL
+ */
+ void (*seq_release)(struct fc_seq *);
+
+ /*
* Reset an exchange manager, completing all sequences and exchanges.
* If s_id is non-zero, reset only exchanges originating from that FID.
* If d_id is non-zero, reset only exchanges sending to that FID.

View File

@@ -1,15 +0,0 @@
# This series applies on GIT commit ec7b3b3aaf3b3b208140ac83dc19a754eebf648f
01-prli-clean
02-fc4
03-seq-init
04-seq-set-resp
05-lport-hook
06-lport-notify
07-pta-mask
08-ptp-ref
09-ptp-type
10-non-fip
11-ptp-flogi-recv
12-ptp-npiv
13-ptp-acc
14-fc-frame-sparse-workaround

View File

@@ -1,15 +0,0 @@
# This series applies on GIT commit ec7b3b3aaf3b3b208140ac83dc19a754eebf648f
01-prli-clean
02-fc4
03-seq-init
04-seq-set-resp
05-lport-hook
06-lport-notify
07-pta-mask
08-ptp-ref
09-ptp-type
10-non-fip
11-ptp-flogi-recv
12-ptp-npiv
13-ptp-acc
14-fc-frame-sparse-workaround

View File

@@ -1,7 +0,0 @@
# applies to 2.6.35
15-fc4
16-seq-set-resp
17-lport-hook
18-lport-notify
14-fc-frame-sparse-workaround
23-softirq

View File

@@ -1,8 +0,0 @@
# applies to linux 2.6.36
19-fc4-v2
20-seq-set-resp
21-lport-hook
22-lport-notify
24-softirq
25-fip-union
26-exch-alloc2