cxl: Wrap iterations over afu slices inside 'afu_list_lock'
[linux-2.6-block.git] / drivers / misc / cxl / pci.c
index c79ba1c699ad1005973474b705065f4c5f8773c7..300531d6136f2efacb9964ebf2daea262a20289b 100644 (file)
@@ -1805,7 +1805,7 @@ static pci_ers_result_t cxl_vphb_error_detected(struct cxl_afu *afu,
        /* There should only be one entry, but go through the list
         * anyway
         */
-       if (afu->phb == NULL)
+       if (afu == NULL || afu->phb == NULL)
                return result;
 
        list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
@@ -1832,7 +1832,8 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
 {
        struct cxl *adapter = pci_get_drvdata(pdev);
        struct cxl_afu *afu;
-       pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET, afu_result;
+       pci_ers_result_t result = PCI_ERS_RESULT_NEED_RESET;
+       pci_ers_result_t afu_result = PCI_ERS_RESULT_NEED_RESET;
        int i;
 
        /* At this point, we could still have an interrupt pending.
@@ -1843,6 +1844,7 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
 
        /* If we're permanently dead, give up. */
        if (state == pci_channel_io_perm_failure) {
+               spin_lock(&adapter->afu_list_lock);
                for (i = 0; i < adapter->slices; i++) {
                        afu = adapter->afu[i];
                        /*
@@ -1851,6 +1853,7 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
                         */
                        cxl_vphb_error_detected(afu, state);
                }
+               spin_unlock(&adapter->afu_list_lock);
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
@@ -1932,11 +1935,17 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
         *     * In slot_reset, free the old resources and allocate new ones.
         *     * In resume, clear the flag to allow things to start.
         */
+
+       /* Make sure no one else changes the afu list */
+       spin_lock(&adapter->afu_list_lock);
+
        for (i = 0; i < adapter->slices; i++) {
                afu = adapter->afu[i];
 
-               afu_result = cxl_vphb_error_detected(afu, state);
+               if (afu == NULL)
+                       continue;
 
+               afu_result = cxl_vphb_error_detected(afu, state);
                cxl_context_detach_all(afu);
                cxl_ops->afu_deactivate_mode(afu, afu->current_mode);
                pci_deconfigure_afu(afu);
@@ -1948,6 +1957,7 @@ static pci_ers_result_t cxl_pci_error_detected(struct pci_dev *pdev,
                         (result == PCI_ERS_RESULT_NEED_RESET))
                        result = PCI_ERS_RESULT_NONE;
        }
+       spin_unlock(&adapter->afu_list_lock);
 
        /* should take the context lock here */
        if (cxl_adapter_context_lock(adapter) != 0)
@@ -1980,14 +1990,18 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev)
         */
        cxl_adapter_context_unlock(adapter);
 
+       spin_lock(&adapter->afu_list_lock);
        for (i = 0; i < adapter->slices; i++) {
                afu = adapter->afu[i];
 
+               if (afu == NULL)
+                       continue;
+
                if (pci_configure_afu(afu, adapter, pdev))
-                       goto err;
+                       goto err_unlock;
 
                if (cxl_afu_select_best_mode(afu))
-                       goto err;
+                       goto err_unlock;
 
                if (afu->phb == NULL)
                        continue;
@@ -1999,16 +2013,16 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev)
                        ctx = cxl_get_context(afu_dev);
 
                        if (ctx && cxl_release_context(ctx))
-                               goto err;
+                               goto err_unlock;
 
                        ctx = cxl_dev_context_init(afu_dev);
                        if (IS_ERR(ctx))
-                               goto err;
+                               goto err_unlock;
 
                        afu_dev->dev.archdata.cxl_ctx = ctx;
 
                        if (cxl_ops->afu_check_and_enable(afu))
-                               goto err;
+                               goto err_unlock;
 
                        afu_dev->error_state = pci_channel_io_normal;
 
@@ -2029,8 +2043,13 @@ static pci_ers_result_t cxl_pci_slot_reset(struct pci_dev *pdev)
                                result = PCI_ERS_RESULT_DISCONNECT;
                }
        }
+
+       spin_unlock(&adapter->afu_list_lock);
        return result;
 
+err_unlock:
+       spin_unlock(&adapter->afu_list_lock);
+
 err:
        /* All the bits that happen in both error_detected and cxl_remove
         * should be idempotent, so we don't need to worry about leaving a mix
@@ -2051,10 +2070,11 @@ static void cxl_pci_resume(struct pci_dev *pdev)
         * This is not the place to be checking if everything came back up
         * properly, because there's no return value: do that in slot_reset.
         */
+       spin_lock(&adapter->afu_list_lock);
        for (i = 0; i < adapter->slices; i++) {
                afu = adapter->afu[i];
 
-               if (afu->phb == NULL)
+               if (afu == NULL || afu->phb == NULL)
                        continue;
 
                list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
@@ -2063,6 +2083,7 @@ static void cxl_pci_resume(struct pci_dev *pdev)
                                afu_dev->driver->err_handler->resume(afu_dev);
                }
        }
+       spin_unlock(&adapter->afu_list_lock);
 }
 
 static const struct pci_error_handlers cxl_err_handler = {