summaryrefslogtreecommitdiff
path: root/drivers/pci/pci.c
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2023-08-29 11:03:50 -0500
committerBjorn Helgaas <bhelgaas@google.com>2023-08-29 11:03:50 -0500
commit3c298b840c196af2f738471ccf0ca40f7dae921a (patch)
tree3c412f6837398f112b279dd5ea94d6357cce4c21 /drivers/pci/pci.c
parent93a3241d615eae69b09be1fb6b309ad116ca03ac (diff)
parentd3fcd7360338358aa0036bec6d2cf0e37a0ca624 (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.c23
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);