Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf
[cascardo/linux.git] / drivers / misc / cxl / file.c
index eec468f..5fb9894 100644 (file)
@@ -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,46 @@ 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 event */
+       if (!pl) {
+               ctx->afu_driver_ops->event_delivered(ctx, pl, -EINVAL);
+               return -EFAULT;
+       }
+
+       /* Check event size */
+       event->header.size += pl->data_size;
+       if (event->header.size > CXL_READ_MIN_SIZE) {
+               ctx->afu_driver_ops->event_delivered(ctx, pl, -EINVAL);
+               return -EFAULT;
+       }
+
+       /* Copy event header */
+       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 +384,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 +414,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 +449,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 +606,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);