mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-23 21:51:27 +00:00
git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@1765 d57e44dd-8a1f-0410-8b47-8ef2f437770f
488 lines
15 KiB
Plaintext
488 lines
15 KiB
Plaintext
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
|
|
*****************************/
|