Allow FC-4 provider modules to hook into libfc. A FC-4 provider is an upper level driver for a specific FC-4 type, such as ELS, FCP, and possibly someday even IP.
This allows any FC-4 module to handle PRLI requests for its type and maintain process-association states (sessions). There are two possible types for providers, passive and active. An active provider is like the FCP initiator. It normally doesn't accept requests. A passive provider is like FCP targets, which accept requests, and don't normally do discovery or log into other remote ports. Each provider registers its ops with libfc, including prli, prlo, recv, and abts. The provder will be called for any incoming PRLI for that FC-4 type on any rport. It may decide to accept or reject the PRLI. If it accepts the PRLI, it may keep some private state (a session or process association) that helps it handle incoming requests. The provider prlo op is called on each incoming PRLO and also whenever the remote port becomes non-ready (implicit PRLO). The provider can use that to remove its private state. Type 0 is also handled this way, just for the special cases in PRLI. Normal ELS requests are also handled through this mechanism, as fc_lport registers a passive provider for FC_TYPE_ELS. Initiator PRLIs are handled through the passive provider for FC_TYPE_FCP. Target PRLIs and incoming FCP requests would be handled through an active provider for FC_TYPE_FCP. A target module would depend on libfc by calling fc_fc4_register_provider(), and could depend on other modules (e.g., TCM or SCST) to implement target functionality. One issue TBD: there's no lock taken on incoming FC4-requests, and should be that way for good performance. There's nothing to protect against the FC-4 provider being removed. Perhaps an RCU mechanism can help here at low performance impact. Signed-off-by: Joe Eykholt <[email protected]> --- drivers/scsi/libfc/fc_libfc.h | 7 ++ drivers/scsi/libfc/fc_lport.c | 70 +++++++++++++--- drivers/scsi/libfc/fc_rport.c | 182 +++++++++++++++++++++++++++++++++++------ include/scsi/libfc.h | 26 ++++++ 4 files changed, 245 insertions(+), 40 deletions(-) diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index 741fd5c..fabdf83 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -82,6 +82,13 @@ extern unsigned int fc_debug_logging; (lport)->host->host_no, ##args)) /* + * Tables of providers, internal to libfc. + */ +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 */ + +/* * 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 c841d54..fb2be17 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 * @sp: The sequence the request is on * @fp: The request frame @@ -856,8 +856,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 *); @@ -871,8 +871,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. */ @@ -901,17 +900,62 @@ 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); +} - /* - * The common exch_done for all request may not be good - * if any request requires longer hold on exhange. XXX - */ +/** + * fc_lport_els_prli() - Handle PRLI parameters for type FC_TYPE_ELS. + * @rdata: The remote port that received the request. + * @spp_len: service parameter page length. + * @spp_in: incoming service parameter page. + * @spp_out: service parameter page for response. + */ +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; +} + +static struct fc4_prov fc_lport_els_prov = { + .prli = fc_lport_els_prli, + .recv = fc_lport_recv_els_req, +}; + +struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = { + [FC_TYPE_ELS] = &fc_lport_els_prov, +}; + +/** + * 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; + + /* XXX doesn't check link for incoming FC-4 requests */ + /* XXX TBD fix race with removal/rmmod of providers */ + + if (fh->fh_type >= FC_FC4_PROV_SIZE) + goto drop; + prov = fc_passive_prov[fh->fh_type]; + if (!prov) + goto drop; + prov->recv(lport, sp, fp); + return; +drop: + FC_LPORT_DBG(lport, "dropping invalid 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 7c9d34d..42f82a1 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -59,6 +59,8 @@ struct workqueue_struct *rport_event_queue; +DEFINE_MUTEX(fc_prov_mutex); + static void fc_rport_enter_plogi(struct fc_rport_priv *); static void fc_rport_enter_prli(struct fc_rport_priv *); static void fc_rport_enter_rtv(struct fc_rport_priv *); @@ -246,6 +248,8 @@ static void fc_rport_work(struct work_struct *work) struct fc_rport_identifiers ids; struct fc_rport *rport; int restart = 0; + struct fc4_prov *prov; + u8 type; mutex_lock(&rdata->rp_mutex); event = rdata->event; @@ -294,6 +298,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(rdata); + } + mutex_unlock(&fc_prov_mutex); + } port_id = rdata->ids.port_id; mutex_unlock(&rdata->rp_mutex); @@ -1356,6 +1369,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; @@ -1365,10 +1379,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); @@ -1407,46 +1420,42 @@ 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. @@ -1591,6 +1600,88 @@ 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. + */ +static 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. + */ +static struct fc4_prov fc_rport_t0_prov = { + .prli = fc_rport_t0_prli, +}; + +/* + * FC-4 provider tables. + */ +struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = { + [0] = &fc_rport_t0_prov, + [FC_TYPE_FCP] = &fc_rport_fcp_init, +}; + +/** * fc_setup_rport() - Initialize the rport_event_queue */ int fc_setup_rport() @@ -1622,3 +1713,40 @@ void fc_rport_terminate_io(struct fc_rport *rport) lport->tt.exch_mgr_reset(lport, rport->port_id, 0); } EXPORT_SYMBOL(fc_rport_terminate_io); + +/** + * 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. + * The caller assures us there are no conflicting providers. + */ +int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov) +{ + if (type >= FC_FC4_PROV_SIZE) + return -EINVAL; + mutex_lock(&fc_prov_mutex); + if (prov->recv) + fc_passive_prov[type] = prov; + else + fc_active_prov[type] = prov; + mutex_unlock(&fc_prov_mutex); + return 0; +} +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) + fc_passive_prov[type] = NULL; + else + fc_active_prov[type] = NULL; + mutex_unlock(&fc_prov_mutex); +} +EXPORT_SYMBOL(fc_fc4_deregister_provider); diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index abc9b8a..b272184 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 FC-4 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; }; /** @@ -840,6 +844,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 + * @abts: Handler for aborting request + */ +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 *); + void (*abts)(struct fc_lport *, struct fc_seq *, struct fc_frame *); +}; + +/* + * 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
