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
Suggested-by: Frederic Barrat
Cc: sta...@vger.kernel.org # v4.9+
Signed-off-by: Andrew Donnellan
Signed-off-by: Vaibhav Jain
---
v1 -> v2:
* Refactored to avoid locking over function boundaries - we now both lock
and unlock in cxl_pcie_{read,write}_config(), rather than locking in
cxl_pcie_config_info() and unlocking from the caller. Thanks Vaibhav.
* Changed the stable tag to 4.9 rather than 4.4 - by the time this is
merged, 4.9 will have landed, and I'll need to manually backport this for
4.4.
---
drivers/misc/cxl/cxl.h | 2 ++
drivers/misc/cxl/main.c | 3 ++-
drivers/misc/cxl/pci.c | 2 ++
drivers/misc/cxl/vphb.c | 51 -
4 files changed, 35 insertions(+), 23 deletions(-)
diff --git a/drivers/misc/cxl/cxl.h b/drivers/misc/cxl/cxl.h
index a144073..379c463 100644
--- a/drivers/misc/cxl/cxl.h
+++ b/drivers/misc/cxl/cxl.h
@@ -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;
diff --git a/drivers/misc/cxl/main.c b/drivers/misc/cxl/main.c
index 62e0dfb..2a6bf1d 100644
--- a/drivers/misc/cxl/main.c
+++ b/drivers/misc/cxl/main.c
@@ -268,7 +268,8 @@ struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int
slice)
idr_init(>contexts_idr);
mutex_init(>contexts_lock);
spin_lock_init(>afu_cntl_lock);
-
+ init_rwsem(>configured_rwsem);
+ down_write(>configured_rwsem);
afu->prefault_mode = CXL_PREFAULT_NONE;
afu->irqs_max = afu->adapter->user_irqs;
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index c4d79b5d..c7b2121 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
@@ -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(>configured_rwsem);
return 0;
err2:
@@ -1141,6 +1142,7 @@ static int pci_configure_afu(struct cxl_afu *afu, struct
cxl *adapter, struct pc
static void pci_deconfigure_afu(struct cxl_afu *afu)
{
+ down_write(>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);
diff --git a/drivers/misc/cxl/vphb.c b/drivers/misc/cxl/vphb.c
index 3519ace..639a343 100644
--- a/drivers/misc/cxl/vphb.c
+++ b/drivers/misc/cxl/vphb.c
@@ -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 = pci_bus_to_afu(bus);
+ /* Grab a reader lock on afu. */
+ if (afu == NULL || !down_read_trylock(>configured_rwsem))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ rc = cxl_pcie_config_info(bus, devfn, afu, );
if (rc)
- return rc;
+