Joe Eykholt wrote: > 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.
I forgot to update this patch with a locking fix. See below. I'll resend this one, perhaps after other comments. > Signed-off-by: Joe Eykholt <[email protected]> > --- > 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 | 136 > +++++++++++++++++++++++++++++++++-------- > include/scsi/libfc.h | 26 ++++++++ > 5 files changed, 258 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..c32f223 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,44 @@ 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) { > + mutex_lock(&fc_prov_mutex); Delete the above line. The lock is already held around the while loop. > + 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; > + } > + mutex_unlock(&fc_prov_mutex); Delete the above line. > + } > + 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 +1677,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 > *****************************/ > > > _______________________________________________ > devel mailing list > [email protected] > http://www.open-fcoe.org/mailman/listinfo/devel _______________________________________________ devel mailing list [email protected] http://www.open-fcoe.org/mailman/listinfo/devel
