mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-18 21:26:31 +00:00
Adding patches for 2.6.36. These are similar to the 2.6.35 patches
except for line number changes and a few interface changes imposed by differences in libfc between 2.6.35 and 2.6.36, mostly due to the change which doesn't allocate an exchange and sequence for incoming requests. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@2804 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -0,0 +1,483 @@
|
||||
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
|
||||
*****************************/
|
||||
@@ -0,0 +1,84 @@
|
||||
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
|
||||
*****************************/
|
||||
@@ -0,0 +1,36 @@
|
||||
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];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,159 @@
|
||||
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 | 13 ++++++++++++-
|
||||
4 files changed, 57 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..50a6e87 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];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
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);
|
||||
Reference in New Issue
Block a user