Merge branch 'pci/vpd'

- Ensure device is accessible before VPD access via sysfs (Alex Williamson)

- Ensure device doesn't go to a low-power state while we're polling for PME
  (Alex Williamson)

* pci/vpd:
  PCI: Fix runtime PM race with PME polling
  PCI/VPD: Add runtime power management to sysfs interface
This commit is contained in:
Bjorn Helgaas
2023-08-29 11:03:50 -05:00
2 changed files with 48 additions and 9 deletions

View File

@@ -2420,10 +2420,13 @@ static void pci_pme_list_scan(struct work_struct *work)
mutex_lock(&pci_pme_list_mutex);
list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
if (pme_dev->dev->pme_poll) {
struct pci_dev *bridge;
struct pci_dev *pdev = pme_dev->dev;
if (pdev->pme_poll) {
struct pci_dev *bridge = pdev->bus->self;
struct device *dev = &pdev->dev;
int pm_status;
bridge = pme_dev->dev->bus->self;
/*
* If bridge is in low power state, the
* configuration space of subordinate devices
@@ -2431,14 +2434,20 @@ static void pci_pme_list_scan(struct work_struct *work)
*/
if (bridge && bridge->current_state != PCI_D0)
continue;
/*
* If the device is in D3cold it should not be
* polled either.
* If the device is in a low power state it
* should not be polled either.
*/
if (pme_dev->dev->current_state == PCI_D3cold)
pm_status = pm_runtime_get_if_active(dev, true);
if (!pm_status)
continue;
pci_pme_wakeup(pme_dev->dev, NULL);
if (pdev->current_state != PCI_D3cold)
pci_pme_wakeup(pdev, NULL);
if (pm_status > 0)
pm_runtime_put(dev);
} else {
list_del(&pme_dev->list);
kfree(pme_dev);

View File

@@ -275,8 +275,23 @@ static ssize_t vpd_read(struct file *filp, struct kobject *kobj,
size_t count)
{
struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
struct pci_dev *vpd_dev = dev;
ssize_t ret;
return pci_read_vpd(dev, off, count, buf);
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
vpd_dev = pci_get_func0_dev(dev);
if (!vpd_dev)
return -ENODEV;
}
pci_config_pm_runtime_get(vpd_dev);
ret = pci_read_vpd(vpd_dev, off, count, buf);
pci_config_pm_runtime_put(vpd_dev);
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
pci_dev_put(vpd_dev);
return ret;
}
static ssize_t vpd_write(struct file *filp, struct kobject *kobj,
@@ -284,8 +299,23 @@ static ssize_t vpd_write(struct file *filp, struct kobject *kobj,
size_t count)
{
struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
struct pci_dev *vpd_dev = dev;
ssize_t ret;
return pci_write_vpd(dev, off, count, buf);
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
vpd_dev = pci_get_func0_dev(dev);
if (!vpd_dev)
return -ENODEV;
}
pci_config_pm_runtime_get(vpd_dev);
ret = pci_write_vpd(vpd_dev, off, count, buf);
pci_config_pm_runtime_put(vpd_dev);
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
pci_dev_put(vpd_dev);
return ret;
}
static BIN_ATTR(vpd, 0600, vpd_read, vpd_write, 0);