diff --git a/fcst/linux-patches/19-fc4-v2 b/fcst/linux-patches/19-fc4-v2 new file mode 100644 index 000000000..b21a6be8f --- /dev/null +++ b/fcst/linux-patches/19-fc4-v2 @@ -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 + +--- + 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 + ++#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 + *****************************/ diff --git a/fcst/linux-patches/20-seq-set-resp b/fcst/linux-patches/20-seq-set-resp new file mode 100644 index 000000000..28b7070ed --- /dev/null +++ b/fcst/linux-patches/20-seq-set-resp @@ -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 + +--- + 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 + *****************************/ diff --git a/fcst/linux-patches/21-lport-hook b/fcst/linux-patches/21-lport-hook new file mode 100644 index 000000000..8dd8e81b9 --- /dev/null +++ b/fcst/linux-patches/21-lport-hook @@ -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 + +--- + 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]; + }; + + /** diff --git a/fcst/linux-patches/22-lport-notify b/fcst/linux-patches/22-lport-notify new file mode 100644 index 000000000..33b35f95c --- /dev/null +++ b/fcst/linux-patches/22-lport-notify @@ -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 + +--- + 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]; + }; + diff --git a/fcst/linux-patches/24-softirq b/fcst/linux-patches/24-softirq new file mode 100644 index 000000000..a02f25453 --- /dev/null +++ b/fcst/linux-patches/24-softirq @@ -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 + +--- + 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);