[v6, 1/2] cxl: Add mechanism for delivering AFU driver specific events
Matthew R. Ochs
mrochs at linux.vnet.ibm.com
Sat Jun 18 02:21:52 AEST 2016
> On Jun 16, 2016, at 9:13 AM, Philippe Bergheaud <felix at linux.vnet.ibm.com> wrote:
>
> This adds an afu_driver_ops structure with fetch_event() and
> event_delivered() callbacks. An AFU driver such as cxlflash can fill
> this out and associate it with a context to enable passing custom
> AFU specific events to userspace.
>
> This also adds a new kernel API function cxl_context_pending_events(),
> that the AFU driver can use to notify the cxl driver that new specific
> events are ready to be delivered, and wake up anyone waiting on the
> context wait queue.
>
> The current count of AFU driver specific events is stored in the field
> afu_driver_events of the context structure.
>
> The cxl driver checks the afu_driver_events count during poll, select,
> read, etc. calls to check if an AFU driver specific event is pending,
> and calls fetch_event() to obtain and deliver that event. This way,
> the cxl driver takes care of all the usual locking semantics around these
> calls and handles all the generic cxl events, so that the AFU driver only
> needs to worry about it's own events.
>
> fetch_event() return a struct cxl_event_afu_driver_reserved, allocated
> by the AFU driver, and filled in with the specific event information and
> size. Data size should not be greater than CXL_MAX_EVENT_DATA_SIZE.
>
> Th cxl driver prepends an appropriate cxl event header, copies the event
> to userspace, and finally calls event_delivered() to return the status of
> the operation to the AFU driver. The event is identified by the context
> and cxl_event_afu_driver_reserved pointers.
>
> Since AFU drivers provide their own means for userspace to obtain the
> AFU file descriptor (i.e. cxlflash uses an ioctl on their scsi file
> descriptor to obtain the AFU file descriptor) and the generic cxl driver
> will never use this event, the ABI of the event is up to each individual
> AFU driver.
>
> Signed-off-by: Philippe Bergheaud <felix at linux.vnet.ibm.com>
> ---
> Changes since v5:
> - s/deliver_event/fetch_event/
> - Fixed the handling of fetch_event errors
> - Documented the return codes of event_delivered
>
> Changes since v4:
> - Addressed comments from Vaibhav:
> - Changed struct cxl_event_afu_driver_reserved from
> { __u64 reserved[4]; } to { size_t data_size; u8 data[]; }
> - Modified deliver_event to return a struct cxl_event_afu_driver_reserved
> - Added new callback event_delivered
> - Added static function afu_driver_event_copy
>
> Changes since v3:
> - Removed driver ops callback ctx_event_pending
> - Created cxl function cxl_context_pending_events
> - Created cxl function cxl_unset_driver_ops
> - Added atomic event counter afu_driver_events
>
> Changes since v2:
> - Fixed some typos spotted by Matt Ochs
>
> Changes since v1:
> - Rebased on upstream
> - Bumped cxl api version to 3
> - Addressed comments from mpe:
> - Clarified commit message & some comments
> - Mentioned 'cxlflash' as a possible user of this event
> - Check driver ops on registration and warn if missing calls
> - Remove redundant checks where driver ops is used
> - Simplified ctx_event_pending and removed underscore version
> - Changed deliver_event to take the context as the first argument
>
> drivers/misc/cxl/Kconfig | 5 +++++
> drivers/misc/cxl/api.c | 27 ++++++++++++++++++++++
> drivers/misc/cxl/cxl.h | 7 +++++-
> drivers/misc/cxl/file.c | 58 +++++++++++++++++++++++++++++++++++++++++-------
> include/misc/cxl.h | 53 +++++++++++++++++++++++++++++++++++++++++++
> include/uapi/misc/cxl.h | 21 ++++++++++++++++++
> 6 files changed, 162 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/misc/cxl/Kconfig b/drivers/misc/cxl/Kconfig
> index 8756d06..560412c 100644
> --- a/drivers/misc/cxl/Kconfig
> +++ b/drivers/misc/cxl/Kconfig
> @@ -15,12 +15,17 @@ config CXL_EEH
> bool
> default n
>
> +config CXL_AFU_DRIVER_OPS
> + bool
> + default n
> +
> config CXL
> tristate "Support for IBM Coherent Accelerators (CXL)"
> depends on PPC_POWERNV && PCI_MSI && EEH
> select CXL_BASE
> select CXL_KERNEL_API
> select CXL_EEH
> + select CXL_AFU_DRIVER_OPS
> default m
> help
> Select this option to enable driver support for IBM Coherent
> diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c
> index 6d228cc..23f98f4 100644
> --- a/drivers/misc/cxl/api.c
> +++ b/drivers/misc/cxl/api.c
> @@ -323,6 +323,33 @@ struct cxl_context *cxl_fops_get_context(struct file *file)
> }
> EXPORT_SYMBOL_GPL(cxl_fops_get_context);
>
> +void cxl_set_driver_ops(struct cxl_context *ctx,
> + struct cxl_afu_driver_ops *ops)
> +{
> + WARN_ON(!ops->fetch_event || !ops->event_delivered);
> + atomic_set(&ctx->afu_driver_events, 0);
> + ctx->afu_driver_ops = ops;
> +}
> +EXPORT_SYMBOL_GPL(cxl_set_driver_ops);
> +
> +int cxl_unset_driver_ops(struct cxl_context *ctx)
> +{
> + if (atomic_read(&ctx->afu_driver_events))
> + return -EBUSY;
> +
> + ctx->afu_driver_ops = NULL;
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(cxl_unset_driver_ops);
> +
> +void cxl_context_events_pending(struct cxl_context *ctx,
> + unsigned int new_events)
> +{
> + atomic_add(new_events, &ctx->afu_driver_events);
> + wake_up_all(&ctx->wq);
> +}
> +EXPORT_SYMBOL_GPL(cxl_context_events_pending);
> +
> int cxl_start_work(struct cxl_context *ctx,
> struct cxl_ioctl_start_work *work)
> {
> diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h
> index 4fe5078..b0027e6 100644
> --- a/drivers/misc/cxl/cxl.h
> +++ b/drivers/misc/cxl/cxl.h
> @@ -24,6 +24,7 @@
> #include <asm/reg.h>
> #include <misc/cxl-base.h>
>
> +#include <misc/cxl.h>
> #include <uapi/misc/cxl.h>
>
> extern uint cxl_verbose;
> @@ -34,7 +35,7 @@ extern uint cxl_verbose;
> * Bump version each time a user API change is made, whether it is
> * backwards compatible ot not.
> */
> -#define CXL_API_VERSION 2
> +#define CXL_API_VERSION 3
> #define CXL_API_VERSION_COMPATIBLE 1
>
> /*
> @@ -522,6 +523,10 @@ struct cxl_context {
> bool pending_fault;
> bool pending_afu_err;
>
> + /* Used by AFU drivers for driver specific event delivery */
> + struct cxl_afu_driver_ops *afu_driver_ops;
> + atomic_t afu_driver_events;
> +
> struct rcu_head rcu;
> };
>
> diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c
> index eec468f..cce2622 100644
> --- a/drivers/misc/cxl/file.c
> +++ b/drivers/misc/cxl/file.c
> @@ -293,6 +293,17 @@ int afu_mmap(struct file *file, struct vm_area_struct *vm)
> return cxl_context_iomap(ctx, vm);
> }
>
> +static inline bool ctx_event_pending(struct cxl_context *ctx)
> +{
> + if (ctx->pending_irq || ctx->pending_fault || ctx->pending_afu_err)
> + return true;
> +
> + if (ctx->afu_driver_ops && atomic_read(&ctx->afu_driver_events))
> + return true;
> +
> + return false;
> +}
> +
> unsigned int afu_poll(struct file *file, struct poll_table_struct *poll)
> {
> struct cxl_context *ctx = file->private_data;
> @@ -305,8 +316,7 @@ unsigned int afu_poll(struct file *file, struct poll_table_struct *poll)
> pr_devel("afu_poll wait done pe: %i\n", ctx->pe);
>
> spin_lock_irqsave(&ctx->lock, flags);
> - if (ctx->pending_irq || ctx->pending_fault ||
> - ctx->pending_afu_err)
> + if (ctx_event_pending(ctx))
> mask |= POLLIN | POLLRDNORM;
> else if (ctx->status == CLOSED)
> /* Only error on closed when there are no futher events pending
> @@ -319,16 +329,40 @@ unsigned int afu_poll(struct file *file, struct poll_table_struct *poll)
> return mask;
> }
>
> -static inline int ctx_event_pending(struct cxl_context *ctx)
> +static ssize_t afu_driver_event_copy(struct cxl_context *ctx,
> + char __user *buf,
> + struct cxl_event *event,
> + struct cxl_event_afu_driver_reserved *pl)
> {
> - return (ctx->pending_irq || ctx->pending_fault ||
> - ctx->pending_afu_err || (ctx->status == CLOSED));
> + /* Check data size */
> + if (!pl || (pl->data_size > CXL_MAX_EVENT_DATA_SIZE)) {
> + ctx->afu_driver_ops->event_delivered(ctx, pl, -EINVAL);
> + return -EFAULT;
> + }
> +
> + /* Copy event header */
> + event->header.size += pl->data_size;
> + if (copy_to_user(buf, event, sizeof(struct cxl_event_header))) {
> + ctx->afu_driver_ops->event_delivered(ctx, pl, -EFAULT);
> + return -EFAULT;
> + }
> +
> + /* Copy event data */
> + buf += sizeof(struct cxl_event_header);
> + if (copy_to_user(buf, &pl->data, pl->data_size)) {
> + ctx->afu_driver_ops->event_delivered(ctx, pl, -EFAULT);
> + return -EFAULT;
> + }
> +
> + ctx->afu_driver_ops->event_delivered(ctx, pl, 0); /* Success */
> + return event->header.size;
> }
>
> ssize_t afu_read(struct file *file, char __user *buf, size_t count,
> loff_t *off)
> {
> struct cxl_context *ctx = file->private_data;
> + struct cxl_event_afu_driver_reserved *pl = NULL;
> struct cxl_event event;
> unsigned long flags;
> int rc;
> @@ -344,7 +378,7 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count,
>
> for (;;) {
> prepare_to_wait(&ctx->wq, &wait, TASK_INTERRUPTIBLE);
> - if (ctx_event_pending(ctx))
> + if (ctx_event_pending(ctx) || (ctx->status == CLOSED))
> break;
>
> if (!cxl_ops->link_ok(ctx->afu->adapter, ctx->afu)) {
> @@ -374,7 +408,12 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count,
> memset(&event, 0, sizeof(event));
> event.header.process_element = ctx->pe;
> event.header.size = sizeof(struct cxl_event_header);
> - if (ctx->pending_irq) {
> + if (ctx->afu_driver_ops && atomic_read(&ctx->afu_driver_events)) {
> + pr_devel("afu_read delivering AFU driver specific event\n");
> + pl = ctx->afu_driver_ops->fetch_event(ctx);
> + atomic_dec(&ctx->afu_driver_events);
> + event.header.type = CXL_EVENT_AFU_DRIVER;
> + } else if (ctx->pending_irq) {
> pr_devel("afu_read delivering AFU interrupt\n");
> event.header.size += sizeof(struct cxl_event_afu_interrupt);
> event.header.type = CXL_EVENT_AFU_INTERRUPT;
> @@ -404,6 +443,9 @@ ssize_t afu_read(struct file *file, char __user *buf, size_t count,
>
> spin_unlock_irqrestore(&ctx->lock, flags);
>
> + if (event.header.type == CXL_EVENT_AFU_DRIVER)
> + return afu_driver_event_copy(ctx, buf, &event, pl);
> +
> if (copy_to_user(buf, &event, event.header.size))
> return -EFAULT;
> return event.header.size;
> @@ -558,7 +600,7 @@ int __init cxl_file_init(void)
> * If these change we really need to update API. Either change some
> * flags or update API version number CXL_API_VERSION.
> */
> - BUILD_BUG_ON(CXL_API_VERSION != 2);
> + BUILD_BUG_ON(CXL_API_VERSION != 3);
> BUILD_BUG_ON(sizeof(struct cxl_ioctl_start_work) != 64);
> BUILD_BUG_ON(sizeof(struct cxl_event_header) != 8);
> BUILD_BUG_ON(sizeof(struct cxl_event_afu_interrupt) != 8);
> diff --git a/include/misc/cxl.h b/include/misc/cxl.h
> index 56560c5..1d8dde8 100644
> --- a/include/misc/cxl.h
> +++ b/include/misc/cxl.h
> @@ -220,4 +220,57 @@ void cxl_perst_reloads_same_image(struct cxl_afu *afu,
> */
> ssize_t cxl_read_adapter_vpd(struct pci_dev *dev, void *buf, size_t count);
>
> +/*
> + * AFU driver ops allow an AFU driver to create their own events to pass to
> + * userspace through the file descriptor as a simpler alternative to overriding
> + * the read() and poll() calls that works with the generic cxl events. These
> + * events are given priority over the generic cxl events, so they will be
> + * delivered first if multiple types of events are pending.
> + *
> + * The AFU driver must call cxl_context_events_pending() to notify the cxl
> + * driver that new events are ready to be delivered for a specific context.
> + * cxl_context_events_pending() will adjust the current count of AFU driver
> + * events for this context, and wake up anyone waiting on the context wait
> + * queue.
> + *
> + * The cxl driver will then call fetch_event() to get a structure defining
> + * the size and address of the driver specific event data. Data size cannot
> + * be greater than CXL_MAX_EVENT_DATA_SIZE. The cxl driver will build a cxl
> + * header with type and process_element fields filled in, and header.size
> + * set to sizeof(struct cxl_event_header) + data_size.
> + *
> + * fetch_event() is called with a spin lock held, so it must not sleep.
> + *
> + * The cxl driver will then deliver the event to userspace, and finally
> + * call event_delivered() to return the status of the operation, identified
> + * by its context and AFU driver event data pointers:
> + * 0 Success
> + * -EFAULT copy_to_user() has failed
> + * -EINVAL Event data pointer is NULL, or
> + * event data size is greater than CXL_MAX_EVENT_DATA_SIZE
> + */
> +struct cxl_afu_driver_ops {
> + struct cxl_event_afu_driver_reserved *(*fetch_event) (
> + struct cxl_context *ctx);
> + void (*event_delivered) (struct cxl_context *ctx,
> + struct cxl_event_afu_driver_reserved *event,
> + int rc);
> +};
> +
> +/*
> + * Associate the above driver ops with a specific context.
> + * Reset the current count of AFU driver events.
> + */
> +void cxl_set_driver_ops(struct cxl_context *ctx,
> + struct cxl_afu_driver_ops *ops);
> +/*
> + * Remove the driver ops from a specific context.
> + * The current count of AFU driver events must be 0.
> + */
> +int cxl_unset_driver_ops(struct cxl_context *ctx);
> +
> +/* Notify cxl driver that new events are ready to be delivered for context */
> +void cxl_context_events_pending(struct cxl_context *ctx,
> + unsigned int new_events);
> +
> #endif /* _MISC_CXL_H */
> diff --git a/include/uapi/misc/cxl.h b/include/uapi/misc/cxl.h
> index 8cd334f..4fa36e4 100644
> --- a/include/uapi/misc/cxl.h
> +++ b/include/uapi/misc/cxl.h
> @@ -93,6 +93,7 @@ enum cxl_event_type {
> CXL_EVENT_AFU_INTERRUPT = 1,
> CXL_EVENT_DATA_STORAGE = 2,
> CXL_EVENT_AFU_ERROR = 3,
> + CXL_EVENT_AFU_DRIVER = 4,
> };
>
> struct cxl_event_header {
> @@ -124,12 +125,32 @@ struct cxl_event_afu_error {
> __u64 error;
> };
>
> +#define CXL_MAX_EVENT_DATA_SIZE 128
Any reason this can't be larger if the user buffer is 4K?
Having a cap is good, and our current plans for exploiting this will
not use anywhere near this limit, however I feel that since we've
moved to a dynamic model, the size limit should be a bit more
accommodating - especially since changing this later would require
a version bump.
How about 2K?
> +
> +struct cxl_event_afu_driver_reserved {
> + /*
> + * Defines the buffer passed to the cxl driver by the AFU driver.
> + *
> + * This is not ABI since the event header.size passed to the user for
> + * existing events is set in the read call to sizeof(cxl_event_header)
> + * + sizeof(whatever event is being dispatched) and the user is already
> + * required to use a 4K buffer on the read call.
> + *
> + * Of course the contents will be ABI, but that's up the AFU driver.
> + *
> + * data_size is limited to MAX_EVENT_DATA_SIZE to prevent abuse.
> + */
> + size_t data_size;
> + u8 data[];
> +};
> +
> struct cxl_event {
> struct cxl_event_header header;
> union {
> struct cxl_event_afu_interrupt irq;
> struct cxl_event_data_storage fault;
> struct cxl_event_afu_error afu_error;
> + struct cxl_event_afu_driver_reserved afu_driver_event;
> };
> };
>
> --
> 2.1.0
>
More information about the Linuxppc-dev
mailing list