]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
cxl: Prevent read/write to AFU config space while AFU not configured
authorAndrew Donnellan <andrew.donnellan@au1.ibm.com>
Fri, 9 Dec 2016 06:18:50 +0000 (17:18 +1100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 15 Mar 2017 02:02:42 +0000 (10:02 +0800)
commit 14a3ae34bfd0bcb1cc12d55b06a8584c11fac6fc upstream.

During EEH recovery, we deconfigure all AFUs whilst leaving the
corresponding vPHB and virtual PCI device in place.

If something attempts to interact with the AFU's PCI config space (e.g.
running lspci) after the AFU has been deconfigured and before it's
reconfigured, cxl_pcie_{read,write}_config() will read invalid values from
the deconfigured struct cxl_afu and proceed to Oops when they try to
dereference pointers that have been set to NULL during deconfiguration.

Add a rwsem to struct cxl_afu so we can prevent interaction with config
space while the AFU is deconfigured.

Reported-by: Pradipta Ghosh <pradghos@in.ibm.com>
Suggested-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Signed-off-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: Vaibhav Jain <vaibhav@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/cxl/cxl.h
drivers/misc/cxl/main.c
drivers/misc/cxl/pci.c
drivers/misc/cxl/vphb.c

index a144073593fa1e5170bba669d7ba467eb06ada5b..379c463e0c4f77a961f33f06182f965911a33fe7 100644 (file)
@@ -418,6 +418,8 @@ struct cxl_afu {
        struct dentry *debugfs;
        struct mutex contexts_lock;
        spinlock_t afu_cntl_lock;
+       /* Used to block access to AFU config space while deconfigured */
+       struct rw_semaphore configured_rwsem;
 
        /* AFU error buffer fields and bin attribute for sysfs */
        u64 eb_len, eb_offset;
index 62e0dfb5f15b62d64980bb008d448038f8fd3c36..2a6bf1d0a3a4f9ae2ffb2f083915df18fd4372dc 100644 (file)
@@ -268,7 +268,8 @@ struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int slice)
        idr_init(&afu->contexts_idr);
        mutex_init(&afu->contexts_lock);
        spin_lock_init(&afu->afu_cntl_lock);
-
+       init_rwsem(&afu->configured_rwsem);
+       down_write(&afu->configured_rwsem);
        afu->prefault_mode = CXL_PREFAULT_NONE;
        afu->irqs_max = afu->adapter->user_irqs;
 
index e96be9ca4e60437db6bfba9b098fad852790f722..b2ff10891775fc82ef1b6d83d25a84c0610de2f1 100644 (file)
@@ -1129,6 +1129,7 @@ static int pci_configure_afu(struct cxl_afu *afu, struct cxl *adapter, struct pc
        if ((rc = cxl_native_register_psl_irq(afu)))
                goto err2;
 
+       up_write(&afu->configured_rwsem);
        return 0;
 
 err2:
@@ -1141,6 +1142,7 @@ err1:
 
 static void pci_deconfigure_afu(struct cxl_afu *afu)
 {
+       down_write(&afu->configured_rwsem);
        cxl_native_release_psl_irq(afu);
        if (afu->adapter->native->sl_ops->release_serr_irq)
                afu->adapter->native->sl_ops->release_serr_irq(afu);
index 3519acebfdab6be8387151d969f55859952c2f09..639a343b7836a49f4da124fcdae68bf3dad96c6c 100644 (file)
@@ -76,23 +76,22 @@ static int cxl_pcie_cfg_record(u8 bus, u8 devfn)
        return (bus << 8) + devfn;
 }
 
-static int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn,
-                               struct cxl_afu **_afu, int *_record)
+static inline struct cxl_afu *pci_bus_to_afu(struct pci_bus *bus)
 {
-       struct pci_controller *phb;
-       struct cxl_afu *afu;
-       int record;
+       struct pci_controller *phb = bus ? pci_bus_to_host(bus) : NULL;
 
-       phb = pci_bus_to_host(bus);
-       if (phb == NULL)
-               return PCIBIOS_DEVICE_NOT_FOUND;
+       return phb ? phb->private_data : NULL;
+}
+
+static inline int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn,
+                                      struct cxl_afu *afu, int *_record)
+{
+       int record;
 
-       afu = (struct cxl_afu *)phb->private_data;
        record = cxl_pcie_cfg_record(bus->number, devfn);
        if (record > afu->crs_num)
                return PCIBIOS_DEVICE_NOT_FOUND;
 
-       *_afu = afu;
        *_record = record;
        return 0;
 }
@@ -106,9 +105,14 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
        u16 val16;
        u32 val32;
 
-       rc = cxl_pcie_config_info(bus, devfn, &afu, &record);
+       afu = pci_bus_to_afu(bus);
+       /* Grab a reader lock on afu. */
+       if (afu == NULL || !down_read_trylock(&afu->configured_rwsem))
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       rc = cxl_pcie_config_info(bus, devfn, afu, &record);
        if (rc)
-               return rc;
+               goto out;
 
        switch (len) {
        case 1:
@@ -127,10 +131,9 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
                WARN_ON(1);
        }
 
-       if (rc)
-               return PCIBIOS_DEVICE_NOT_FOUND;
-
-       return PCIBIOS_SUCCESSFUL;
+out:
+       up_read(&afu->configured_rwsem);
+       return rc ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
 }
 
 static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
@@ -139,9 +142,14 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
        int rc, record;
        struct cxl_afu *afu;
 
-       rc = cxl_pcie_config_info(bus, devfn, &afu, &record);
+       afu = pci_bus_to_afu(bus);
+       /* Grab a reader lock on afu. */
+       if (afu == NULL || !down_read_trylock(&afu->configured_rwsem))
+               return PCIBIOS_DEVICE_NOT_FOUND;
+
+       rc = cxl_pcie_config_info(bus, devfn, afu, &record);
        if (rc)
-               return rc;
+               goto out;
 
        switch (len) {
        case 1:
@@ -157,10 +165,9 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
                WARN_ON(1);
        }
 
-       if (rc)
-               return PCIBIOS_SET_FAILED;
-
-       return PCIBIOS_SUCCESSFUL;
+out:
+       up_read(&afu->configured_rwsem);
+       return rc ? PCIBIOS_SET_FAILED : PCIBIOS_SUCCESSFUL;
 }
 
 static struct pci_ops cxl_pcie_pci_ops =