PCI: Add device disconnected state
authorKeith Busch <keith.busch@intel.com>
Thu, 30 Mar 2017 03:48:59 +0000 (22:48 -0500)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 30 Mar 2017 03:54:46 +0000 (22:54 -0500)
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>
drivers/pci/hotplug/pciehp_pci.c
drivers/pci/pci.h
drivers/pci/pcie/pcie-dpc.c
include/linux/pci.h

index 9e69403be6327156c0a7a95fa8ce5b422aa60cb9..19f30a9f461d50b3ec12847b67ed2eea251f16f5 100644 (file)
@@ -109,6 +109,12 @@ int pciehp_unconfigure_device(struct slot *p_slot)
                                break;
                        }
                }
+               if (!presence) {
+                       pci_dev_set_disconnected(dev, NULL);
+                       if (pci_has_subordinate(dev))
+                               pci_walk_bus(dev->subordinate,
+                                            pci_dev_set_disconnected, NULL);
+               }
                pci_stop_and_remove_bus_device(dev);
                /*
                 * Ensure that no new Requests will be generated from
index 8dd38e69d6f2a8ae4f4b7d6801f3f321abb5ac1a..245719c3e409de0c298ad18f2f7349962d897646 100644 (file)
@@ -274,6 +274,20 @@ struct pci_sriov {
        resource_size_t barsz[PCI_SRIOV_NUM_BARS];      /* VF BAR size */
 };
 
+/* pci_dev priv_flags */
+#define PCI_DEV_DISCONNECTED 0
+
+static inline int pci_dev_set_disconnected(struct pci_dev *dev, void *unused)
+{
+       set_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
+       return 0;
+}
+
+static inline bool pci_dev_is_disconnected(const struct pci_dev *dev)
+{
+       return test_bit(PCI_DEV_DISCONNECTED, &dev->priv_flags);
+}
+
 #ifdef CONFIG_PCI_ATS
 void pci_restore_ats_state(struct pci_dev *dev);
 #else
index d4d70ef4a2d7a9f844a0e7e72e5a532c116d5137..77d2ca99d2ec20bb0cf2b6b07d9add8cca845e1f 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/pcieport_if.h>
+#include "../pci.h"
 
 struct dpc_dev {
        struct pcie_device      *dev;
@@ -66,6 +67,10 @@ static void interrupt_event_handler(struct work_struct *work)
        list_for_each_entry_safe_reverse(dev, temp, &parent->devices,
                                         bus_list) {
                pci_dev_get(dev);
+               pci_dev_set_disconnected(dev, NULL);
+               if (pci_has_subordinate(dev))
+                       pci_walk_bus(dev->subordinate,
+                                    pci_dev_set_disconnected, NULL);
                pci_stop_and_remove_bus_device(dev);
                pci_dev_put(dev);
        }
index d705f3088ff9492fa11e75174e3e7f1137f6c55f..2887933329a2cc71b81f86369212acfd77d6474f 100644 (file)
@@ -396,6 +396,8 @@ struct pci_dev {
        phys_addr_t rom; /* Physical address of ROM if it's not from the BAR */
        size_t romlen; /* Length of ROM if it's not from the BAR */
        char *driver_override; /* Driver name to force a match */
+
+       unsigned long priv_flags; /* Private flags for the pci driver */
 };
 
 static inline struct pci_dev *pci_physfn(struct pci_dev *dev)