summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorKeith Busch <keith.busch@intel.com>2017-03-29 22:48:59 -0500
committerBjorn Helgaas <bhelgaas@google.com>2017-03-29 22:54:46 -0500
commit89ee9f7680031d7df91a1a27abac69e034c2e892 (patch)
tree4e874d870f8fd310aa954153c590f6ae93517868 /drivers/pci
parentd3881e5015421a578bc328136471fcf1d02ac389 (diff)
downloadlinux-89ee9f7680031d7df91a1a27abac69e034c2e892.tar.gz
linux-89ee9f7680031d7df91a1a27abac69e034c2e892.tar.bz2
linux-89ee9f7680031d7df91a1a27abac69e034c2e892.zip
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>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c6
-rw-r--r--drivers/pci/pci.h14
-rw-r--r--drivers/pci/pcie/pcie-dpc.c5
3 files changed, 25 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
index 9e69403be632..19f30a9f461d 100644
--- a/drivers/pci/hotplug/pciehp_pci.c
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -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
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 8dd38e69d6f2..245719c3e409 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -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
diff --git a/drivers/pci/pcie/pcie-dpc.c b/drivers/pci/pcie/pcie-dpc.c
index d4d70ef4a2d7..77d2ca99d2ec 100644
--- a/drivers/pci/pcie/pcie-dpc.c
+++ b/drivers/pci/pcie/pcie-dpc.c
@@ -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);
}