cxl: Allocate and release the SPA with the AFU
authorDaniel Axtens <dja@axtens.net>
Fri, 14 Aug 2015 07:41:19 +0000 (17:41 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 14 Aug 2015 11:32:04 +0000 (21:32 +1000)
Previously the SPA was allocated and freed upon entering and leaving
AFU-directed mode. This causes some issues for error recovery - contexts
hold a pointer inside the SPA, and they may persist after the AFU has
been detached.

We would ideally like to allocate the SPA when the AFU is allocated, and
release it until the AFU is released. However, we don't know how big the
SPA needs to be until we read the AFU descriptor.

Therefore, restructure the code:

 - Allocate the SPA only once, on the first attach.

 - Release the SPA only when the entire AFU is being released (not
   detached). Guard the release with a NULL check, so we don't free
   if it was never allocated (e.g. dedicated mode)

Acked-by: Cyril Bur <cyrilbur@gmail.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
drivers/misc/cxl/cxl.h
drivers/misc/cxl/native.c
drivers/misc/cxl/pci.c

index 9b9e89fd02cc8120ebc4073c6bad0c355d235197..d540542f99316cc17ef454bb9d5a942ede1b298d 100644 (file)
@@ -632,6 +632,9 @@ void unregister_cxl_calls(struct cxl_calls *calls);
 int cxl_alloc_adapter_nr(struct cxl *adapter);
 void cxl_remove_adapter_nr(struct cxl *adapter);
 
+int cxl_alloc_spa(struct cxl_afu *afu);
+void cxl_release_spa(struct cxl_afu *afu);
+
 int cxl_file_init(void);
 void cxl_file_exit(void);
 int cxl_register_adapter(struct cxl *adapter);
index 44568dd68bb9c1e7e009f12990a2b1ae86910940..b37f2e8004f5bcd58f970ea274ebd29ef4b1eae3 100644 (file)
@@ -183,10 +183,8 @@ static int spa_max_procs(int spa_size)
        return ((spa_size / 8) - 96) / 17;
 }
 
-static int alloc_spa(struct cxl_afu *afu)
+int cxl_alloc_spa(struct cxl_afu *afu)
 {
-       u64 spap;
-
        /* Work out how many pages to allocate */
        afu->spa_order = 0;
        do {
@@ -205,6 +203,13 @@ static int alloc_spa(struct cxl_afu *afu)
        pr_devel("spa pages: %i afu->spa_max_procs: %i   afu->num_procs: %i\n",
                 1<<afu->spa_order, afu->spa_max_procs, afu->num_procs);
 
+       return 0;
+}
+
+static void attach_spa(struct cxl_afu *afu)
+{
+       u64 spap;
+
        afu->sw_command_status = (__be64 *)((char *)afu->spa +
                                            ((afu->spa_max_procs + 3) * 128));
 
@@ -213,14 +218,19 @@ static int alloc_spa(struct cxl_afu *afu)
        spap |= CXL_PSL_SPAP_V;
        pr_devel("cxl: SPA allocated at 0x%p. Max processes: %i, sw_command_status: 0x%p CXL_PSL_SPAP_An=0x%016llx\n", afu->spa, afu->spa_max_procs, afu->sw_command_status, spap);
        cxl_p1n_write(afu, CXL_PSL_SPAP_An, spap);
-
-       return 0;
 }
 
-static void release_spa(struct cxl_afu *afu)
+static inline void detach_spa(struct cxl_afu *afu)
 {
        cxl_p1n_write(afu, CXL_PSL_SPAP_An, 0);
-       free_pages((unsigned long) afu->spa, afu->spa_order);
+}
+
+void cxl_release_spa(struct cxl_afu *afu)
+{
+       if (afu->spa) {
+               free_pages((unsigned long) afu->spa, afu->spa_order);
+               afu->spa = NULL;
+       }
 }
 
 int cxl_tlb_slb_invalidate(struct cxl *adapter)
@@ -447,8 +457,11 @@ static int activate_afu_directed(struct cxl_afu *afu)
 
        dev_info(&afu->dev, "Activating AFU directed mode\n");
 
-       if (alloc_spa(afu))
-               return -ENOMEM;
+       if (afu->spa == NULL) {
+               if (cxl_alloc_spa(afu))
+                       return -ENOMEM;
+       }
+       attach_spa(afu);
 
        cxl_p1n_write(afu, CXL_PSL_SCNTL_An, CXL_PSL_SCNTL_An_PM_AFU);
        cxl_p1n_write(afu, CXL_PSL_AMOR_An, 0xFFFFFFFFFFFFFFFFULL);
@@ -559,8 +572,6 @@ static int deactivate_afu_directed(struct cxl_afu *afu)
        cxl_afu_disable(afu);
        cxl_psl_purge(afu);
 
-       release_spa(afu);
-
        return 0;
 }
 
index 1d314f1f95fed4c4702419d6cfb40f50781a027c..dc26cdd653ad4a585bee9ebb23c3202103d14d8f 100644 (file)
@@ -552,6 +552,8 @@ static void cxl_release_afu(struct device *dev)
        pr_devel("cxl_release_afu\n");
 
        idr_destroy(&afu->contexts_idr);
+       cxl_release_spa(afu);
+
        kfree(afu);
 }