Skip to content

Commit 3c298b8

Browse files
committed
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
2 parents 93a3241 + d3fcd73 commit 3c298b8

File tree

2 files changed

+48
-9
lines changed

2 files changed

+48
-9
lines changed

drivers/pci/pci.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2420,25 +2420,34 @@ static void pci_pme_list_scan(struct work_struct *work)
24202420

24212421
mutex_lock(&pci_pme_list_mutex);
24222422
list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
2423-
if (pme_dev->dev->pme_poll) {
2424-
struct pci_dev *bridge;
2423+
struct pci_dev *pdev = pme_dev->dev;
2424+
2425+
if (pdev->pme_poll) {
2426+
struct pci_dev *bridge = pdev->bus->self;
2427+
struct device *dev = &pdev->dev;
2428+
int pm_status;
24252429

2426-
bridge = pme_dev->dev->bus->self;
24272430
/*
24282431
* If bridge is in low power state, the
24292432
* configuration space of subordinate devices
24302433
* may be not accessible
24312434
*/
24322435
if (bridge && bridge->current_state != PCI_D0)
24332436
continue;
2437+
24342438
/*
2435-
* If the device is in D3cold it should not be
2436-
* polled either.
2439+
* If the device is in a low power state it
2440+
* should not be polled either.
24372441
*/
2438-
if (pme_dev->dev->current_state == PCI_D3cold)
2442+
pm_status = pm_runtime_get_if_active(dev, true);
2443+
if (!pm_status)
24392444
continue;
24402445

2441-
pci_pme_wakeup(pme_dev->dev, NULL);
2446+
if (pdev->current_state != PCI_D3cold)
2447+
pci_pme_wakeup(pdev, NULL);
2448+
2449+
if (pm_status > 0)
2450+
pm_runtime_put(dev);
24422451
} else {
24432452
list_del(&pme_dev->list);
24442453
kfree(pme_dev);

drivers/pci/vpd.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,47 @@ static ssize_t vpd_read(struct file *filp, struct kobject *kobj,
275275
size_t count)
276276
{
277277
struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
278+
struct pci_dev *vpd_dev = dev;
279+
ssize_t ret;
280+
281+
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
282+
vpd_dev = pci_get_func0_dev(dev);
283+
if (!vpd_dev)
284+
return -ENODEV;
285+
}
286+
287+
pci_config_pm_runtime_get(vpd_dev);
288+
ret = pci_read_vpd(vpd_dev, off, count, buf);
289+
pci_config_pm_runtime_put(vpd_dev);
290+
291+
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
292+
pci_dev_put(vpd_dev);
278293

279-
return pci_read_vpd(dev, off, count, buf);
294+
return ret;
280295
}
281296

282297
static ssize_t vpd_write(struct file *filp, struct kobject *kobj,
283298
struct bin_attribute *bin_attr, char *buf, loff_t off,
284299
size_t count)
285300
{
286301
struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
302+
struct pci_dev *vpd_dev = dev;
303+
ssize_t ret;
304+
305+
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
306+
vpd_dev = pci_get_func0_dev(dev);
307+
if (!vpd_dev)
308+
return -ENODEV;
309+
}
310+
311+
pci_config_pm_runtime_get(vpd_dev);
312+
ret = pci_write_vpd(vpd_dev, off, count, buf);
313+
pci_config_pm_runtime_put(vpd_dev);
314+
315+
if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
316+
pci_dev_put(vpd_dev);
287317

288-
return pci_write_vpd(dev, off, count, buf);
318+
return ret;
289319
}
290320
static BIN_ATTR(vpd, 0600, vpd_read, vpd_write, 0);
291321

0 commit comments

Comments
 (0)