cxl: Prevent read/write to AFU config space while AFU not configured
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> Cc: stable@vger.kernel.org # v4.9+ 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>
This commit is contained in:
parent
d7b1946c79
commit
14a3ae34bf
|
@ -418,6 +418,8 @@ struct cxl_afu {
|
||||||
struct dentry *debugfs;
|
struct dentry *debugfs;
|
||||||
struct mutex contexts_lock;
|
struct mutex contexts_lock;
|
||||||
spinlock_t afu_cntl_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 */
|
/* AFU error buffer fields and bin attribute for sysfs */
|
||||||
u64 eb_len, eb_offset;
|
u64 eb_len, eb_offset;
|
||||||
|
|
|
@ -268,7 +268,8 @@ struct cxl_afu *cxl_alloc_afu(struct cxl *adapter, int slice)
|
||||||
idr_init(&afu->contexts_idr);
|
idr_init(&afu->contexts_idr);
|
||||||
mutex_init(&afu->contexts_lock);
|
mutex_init(&afu->contexts_lock);
|
||||||
spin_lock_init(&afu->afu_cntl_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->prefault_mode = CXL_PREFAULT_NONE;
|
||||||
afu->irqs_max = afu->adapter->user_irqs;
|
afu->irqs_max = afu->adapter->user_irqs;
|
||||||
|
|
||||||
|
|
|
@ -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)))
|
if ((rc = cxl_native_register_psl_irq(afu)))
|
||||||
goto err2;
|
goto err2;
|
||||||
|
|
||||||
|
up_write(&afu->configured_rwsem);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err2:
|
err2:
|
||||||
|
@ -1141,6 +1142,7 @@ err1:
|
||||||
|
|
||||||
static void pci_deconfigure_afu(struct cxl_afu *afu)
|
static void pci_deconfigure_afu(struct cxl_afu *afu)
|
||||||
{
|
{
|
||||||
|
down_write(&afu->configured_rwsem);
|
||||||
cxl_native_release_psl_irq(afu);
|
cxl_native_release_psl_irq(afu);
|
||||||
if (afu->adapter->native->sl_ops->release_serr_irq)
|
if (afu->adapter->native->sl_ops->release_serr_irq)
|
||||||
afu->adapter->native->sl_ops->release_serr_irq(afu);
|
afu->adapter->native->sl_ops->release_serr_irq(afu);
|
||||||
|
|
|
@ -76,23 +76,22 @@ static int cxl_pcie_cfg_record(u8 bus, u8 devfn)
|
||||||
return (bus << 8) + devfn;
|
return (bus << 8) + devfn;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cxl_pcie_config_info(struct pci_bus *bus, unsigned int devfn,
|
static inline struct cxl_afu *pci_bus_to_afu(struct pci_bus *bus)
|
||||||
struct cxl_afu **_afu, int *_record)
|
{
|
||||||
|
struct pci_controller *phb = bus ? pci_bus_to_host(bus) : NULL;
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
struct pci_controller *phb;
|
|
||||||
struct cxl_afu *afu;
|
|
||||||
int record;
|
int record;
|
||||||
|
|
||||||
phb = pci_bus_to_host(bus);
|
|
||||||
if (phb == NULL)
|
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
|
||||||
|
|
||||||
afu = (struct cxl_afu *)phb->private_data;
|
|
||||||
record = cxl_pcie_cfg_record(bus->number, devfn);
|
record = cxl_pcie_cfg_record(bus->number, devfn);
|
||||||
if (record > afu->crs_num)
|
if (record > afu->crs_num)
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||||
|
|
||||||
*_afu = afu;
|
|
||||||
*_record = record;
|
*_record = record;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -106,9 +105,14 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
|
||||||
u16 val16;
|
u16 val16;
|
||||||
u32 val32;
|
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)
|
if (rc)
|
||||||
return rc;
|
goto out;
|
||||||
|
|
||||||
switch (len) {
|
switch (len) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -127,10 +131,9 @@ static int cxl_pcie_read_config(struct pci_bus *bus, unsigned int devfn,
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc)
|
out:
|
||||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
up_read(&afu->configured_rwsem);
|
||||||
|
return rc ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
|
||||||
return PCIBIOS_SUCCESSFUL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
|
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;
|
int rc, record;
|
||||||
struct cxl_afu *afu;
|
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)
|
if (rc)
|
||||||
return rc;
|
goto out;
|
||||||
|
|
||||||
switch (len) {
|
switch (len) {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -157,10 +165,9 @@ static int cxl_pcie_write_config(struct pci_bus *bus, unsigned int devfn,
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rc)
|
out:
|
||||||
return PCIBIOS_SET_FAILED;
|
up_read(&afu->configured_rwsem);
|
||||||
|
return rc ? PCIBIOS_SET_FAILED : PCIBIOS_SUCCESSFUL;
|
||||||
return PCIBIOS_SUCCESSFUL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct pci_ops cxl_pcie_pci_ops =
|
static struct pci_ops cxl_pcie_pci_ops =
|
||||||
|
|
Loading…
Reference in a new issue