Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI: Add device disconnected state

Add a new state to pci_dev to be set when it is unexpectedly disconnected.
The PCI driver tear down functions can observe this new device state so
they may skip operations that will fail.

The pciehp and pcie-dpc drivers are aware when the link is down, so these
set the flag when their handlers detect the device is disconnected.

Tested-by: Krishna Dhulipala <krishnad@fb.com>
Signed-off-by: Keith Busch <keith.busch@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Wei Zhang <wzhang@fb.com>

authored by

Keith Busch and committed by
Bjorn Helgaas
89ee9f76 d3881e50

+27
+6
drivers/pci/hotplug/pciehp_pci.c
··· 109 109 break; 110 110 } 111 111 } 112 + if (!presence) { 113 + pci_dev_set_disconnected(dev, NULL); 114 + if (pci_has_subordinate(dev)) 115 + pci_walk_bus(dev->subordinate, 116 + pci_dev_set_disconnected, NULL); 117 + } 112 118 pci_stop_and_remove_bus_device(dev); 113 119 /* 114 120 * Ensure that no new Requests will be generated from
+14
drivers/pci/pci.h
··· 274 274 resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ 275 275 }; 276 276 277 + /* pci_dev priv_flags */ 278 + #define PCI_DEV_DISCONNECTED 0 279 + 280 + static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused) 281 + { 282 + set_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags); 283 + return 0; 284 + } 285 + 286 + static inline bool pci_dev_is_disconnected(const struct pci_dev *dev) 287 + { 288 + return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags); 289 + } 290 + 277 291 #ifdef CONFIG_PCI_ATS 278 292 void pci_restore_ats_state(struct pci_dev *dev); 279 293 #else
+5
drivers/pci/pcie/pcie-dpc.c
··· 14 14 #include <linux/init.h> 15 15 #include <linux/pci.h> 16 16 #include <linux/pcieport_if.h> 17 + #include "../pci.h" 17 18 18 19 struct dpc_dev { 19 20 struct pcie_device *dev; ··· 67 66 list_for_each_entry_safe_reverse(dev, temp, &parent->devices, 68 67 bus_list) { 69 68 pci_dev_get(dev); 69 + pci_dev_set_disconnected(dev, NULL); 70 + if (pci_has_subordinate(dev)) 71 + pci_walk_bus(dev->subordinate, 72 + pci_dev_set_disconnected, NULL); 70 73 pci_stop_and_remove_bus_device(dev); 71 74 pci_dev_put(dev); 72 75 }
+2
include/linux/pci.h
··· 396 396 phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */ 397 397 size_t romlen; /* Length of ROM if it's not from the BAR */ 398 398 char *driver_override; /* Driver name to force a match */ 399 + 400 + unsigned long priv_flags; /* Private flags for the pci driver */ 399 401 }; 400 402 401 403 static inline struct pci_dev *pci_physfn(struct pci_dev *dev)