summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci.c5
-rw-r--r--include/linux/pci.h7
2 files changed, 11 insertions, 1 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d3fabfca7e6f..a9848316c623 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1277,6 +1277,11 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
for (;;) {
u32 id;
+ if (pci_dev_is_disconnected(dev)) {
+ pci_dbg(dev, "disconnected; not waiting\n");
+ return -ENOTTY;
+ }
+
pci_read_config_dword(dev, PCI_COMMAND, &id);
if (!PCI_POSSIBLE_ERROR(id))
break;
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 69b10f2fb606..2151c7f9be86 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -2514,7 +2514,12 @@ static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
{
- return dev->error_state == pci_channel_io_perm_failure;
+ /*
+ * error_state is set in pci_dev_set_io_state() using xchg/cmpxchg()
+ * and read w/o common lock. READ_ONCE() ensures compiler cannot cache
+ * the value (e.g. inside the loop in pci_dev_wait()).
+ */
+ return READ_ONCE(dev->error_state) == pci_channel_io_perm_failure;
}
void pci_request_acs(void);