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/pci.c | |
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/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 23 |
1 files changed, 16 insertions, 7 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); |