diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2023-08-29 11:03:50 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2023-08-29 11:03:50 -0500 |
commit | 3c298b840c196af2f738471ccf0ca40f7dae921a (patch) | |
tree | 3c412f6837398f112b279dd5ea94d6357cce4c21 /drivers/pci | |
parent | 93a3241d615eae69b09be1fb6b309ad116ca03ac (diff) | |
parent | d3fcd7360338358aa0036bec6d2cf0e37a0ca624 (diff) |
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
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci.c | 23 | ||||
-rw-r--r-- | drivers/pci/vpd.c | 34 |
2 files changed, 48 insertions, 9 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 702fe577089b..5d45e518a266 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -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); diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c index a4fc4d0690fe..485a642b9304 100644 --- a/drivers/pci/vpd.c +++ b/drivers/pci/vpd.c @@ -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; + + 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 pci_read_vpd(dev, off, count, buf); + 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; + + 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 pci_write_vpd(dev, off, count, buf); + return ret; } static BIN_ATTR(vpd, 0600, vpd_read, vpd_write, 0); |