cxl: Add alternate MMIO error handling
authorIan Munsie <imunsie@au1.ibm.com>
Thu, 23 Jul 2015 06:43:56 +0000 (16:43 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 18 Aug 2015 09:34:43 +0000 (19:34 +1000)
userspace programs using cxl currently have to use two strategies for
dealing with MMIO errors simultaneously. They have to check every read
for a return of all Fs in case the adapter has gone away and the kernel
has not yet noticed, and they have to deal with SIGBUS in case the
kernel has already noticed, invalidated the mapping and marked the
context as failed.

In order to simplify things, this patch adds an alternative approach
where the kernel will return a page filled with Fs instead of delivering
a SIGBUS. This allows userspace to only need to deal with one of these
two error paths, and is intended for use in libraries that use cxl
transparently and may not be able to safely install a signal handler.

This approach will only work if certain constraints are met. Namely, if
the application is both reading and writing to an address in the problem
state area it cannot assume that a non-FF read is OK, as it may just be
reading out a value it has previously written. Further - since only one
page is used per context a write to a given offset would be visible when
reading the same offset from a different page in the mapping (this only
applies within a single context, not between contexts).

An application could deal with this by e.g. making sure it also reads
from a read-only offset after any reads to a read/write offset.

Due to these constraints, this functionality must be explicitly
requested by userspace when starting the context by passing in the
CXL_START_WORK_ERR_FF flag.

Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
Acked-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
drivers/misc/cxl/context.c
drivers/misc/cxl/cxl.h
drivers/misc/cxl/file.c
include/uapi/misc/cxl.h

index 615842115848fe7bddfe1c9a5320d8d3b879cbb2..941fda04aa9a3092d1ddea19164348aeddbf7cb0 100644 (file)
@@ -126,6 +126,18 @@ static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        if (ctx->status != STARTED) {
                mutex_unlock(&ctx->status_mutex);
                pr_devel("%s: Context not started, failing problem state access\n", __func__);
+               if (ctx->mmio_err_ff) {
+                       if (!ctx->ff_page) {
+                               ctx->ff_page = alloc_page(GFP_USER);
+                               if (!ctx->ff_page)
+                                       return VM_FAULT_OOM;
+                               memset(page_address(ctx->ff_page), 0xff, PAGE_SIZE);
+                       }
+                       get_page(ctx->ff_page);
+                       vmf->page = ctx->ff_page;
+                       vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
+                       return 0;
+               }
                return VM_FAULT_SIGBUS;
        }
 
@@ -257,6 +269,8 @@ static void reclaim_ctx(struct rcu_head *rcu)
        struct cxl_context *ctx = container_of(rcu, struct cxl_context, rcu);
 
        free_page((u64)ctx->sstp);
+       if (ctx->ff_page)
+               __free_page(ctx->ff_page);
        ctx->sstp = NULL;
 
        kfree(ctx);
index 6f5386653dae55019577d06627d8f2f43c15f780..e7af256f60c54a97736ec7907a60171cab2eb90d 100644 (file)
@@ -34,7 +34,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 1
+#define CXL_API_VERSION 2
 #define CXL_API_VERSION_COMPATIBLE 1
 
 /*
@@ -418,6 +418,8 @@ struct cxl_context {
        /* Used to unmap any mmaps when force detaching */
        struct address_space *mapping;
        struct mutex mapping_lock;
+       struct page *ff_page;
+       bool mmio_err_ff;
 
        spinlock_t sste_lock; /* Protects segment table entries */
        struct cxl_sste *sstp;
index 57bdb473749f7373a190aff1c0c6919ca7e68dd8..a30bf285b5bdd75c3f2b357d89dbab251bb98c7c 100644 (file)
@@ -184,6 +184,8 @@ static long afu_ioctl_start_work(struct cxl_context *ctx,
        if (work.flags & CXL_START_WORK_AMR)
                amr = work.amr & mfspr(SPRN_UAMOR);
 
+       ctx->mmio_err_ff = !!(work.flags & CXL_START_WORK_ERR_FF);
+
        /*
         * We grab the PID here and not in the file open to allow for the case
         * where a process (master, some daemon, etc) has opened the chardev on
@@ -538,7 +540,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 != 1);
+       BUILD_BUG_ON(CXL_API_VERSION != 2);
        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);
index 99a8ca15fe648c9d176325eb2ed9c952a5ac7088..1e889aa8a36e8cd8b7b70cf0da4b06d5848449f9 100644 (file)
@@ -29,8 +29,10 @@ struct cxl_ioctl_start_work {
 
 #define CXL_START_WORK_AMR             0x0000000000000001ULL
 #define CXL_START_WORK_NUM_IRQS                0x0000000000000002ULL
+#define CXL_START_WORK_ERR_FF          0x0000000000000004ULL
 #define CXL_START_WORK_ALL             (CXL_START_WORK_AMR |\
-                                        CXL_START_WORK_NUM_IRQS)
+                                        CXL_START_WORK_NUM_IRQS |\
+                                        CXL_START_WORK_ERR_FF)
 
 
 /* Possible modes that an afu can be in */