Skip to content

Commit 6613443

Browse files
ij-intelbjorn-helgaas
authored andcommitted
PCI: Do not wait for disconnected devices when resuming
On runtime resume, pci_dev_wait() is called: pci_pm_runtime_resume() pci_pm_bridge_power_up_actions() pci_bridge_wait_for_secondary_bus() pci_dev_wait() While a device is runtime suspended along with its PCI hierarchy, the device could get disconnected. In such case, the link will not come up no matter how long pci_dev_wait() waits for it. Besides the above mentioned case, there could be other ways to get the device disconnected while pci_dev_wait() is waiting for the link to come up. Make pci_dev_wait() exit if the device is already disconnected to avoid unnecessary delay. The use cases of pci_dev_wait() boil down to two: 1. Waiting for the device after reset 2. pci_bridge_wait_for_secondary_bus() The callers in both cases seem to benefit from propagating the disconnection as error even if device disconnection would be more analoguous to the case where there is no device in the first place which return 0 from pci_dev_wait(). In the case 2, it results in unnecessary marking of the devices disconnected again but that is just harmless extra work. Also make sure compiler does not become too clever with dev->error_state and use READ_ONCE() to force a fetch for the up-to-date value. Link: https://lore.kernel.org/r/[email protected] Reported-by: Mika Westerberg <[email protected]> Tested-by: Mika Westerberg <[email protected]> Signed-off-by: Ilpo Järvinen <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]>
1 parent 199f968 commit 6613443

File tree

2 files changed

+11
-1
lines changed

2 files changed

+11
-1
lines changed

drivers/pci/pci.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,6 +1277,11 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
12771277
for (;;) {
12781278
u32 id;
12791279

1280+
if (pci_dev_is_disconnected(dev)) {
1281+
pci_dbg(dev, "disconnected; not waiting\n");
1282+
return -ENOTTY;
1283+
}
1284+
12801285
pci_read_config_dword(dev, PCI_COMMAND, &id);
12811286
if (!PCI_POSSIBLE_ERROR(id))
12821287
break;

include/linux/pci.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2514,7 +2514,12 @@ static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
25142514

25152515
static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
25162516
{
2517-
return dev->error_state == pci_channel_io_perm_failure;
2517+
/*
2518+
* error_state is set in pci_dev_set_io_state() using xchg/cmpxchg()
2519+
* and read w/o common lock. READ_ONCE() ensures compiler cannot cache
2520+
* the value (e.g. inside the loop in pci_dev_wait()).
2521+
*/
2522+
return READ_ONCE(dev->error_state) == pci_channel_io_perm_failure;
25182523
}
25192524

25202525
void pci_request_acs(void);

0 commit comments

Comments
 (0)