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

Reply via email to