mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-17 02:31:27 +00:00
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:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*****************************/
|
||||
@@ -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;
|
||||
@@ -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
|
||||
*****************************/
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
+}
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
};
|
||||
@@ -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
|
||||
*****************************/
|
||||
@@ -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
|
||||
*****************************/
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
@@ -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
|
||||
*****************************/
|
||||
@@ -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
|
||||
*****************************/
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user