summaryrefslogtreecommitdiffstats
path: root/drivers/pci/remove.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/remove.c')
-rw-r--r--drivers/pci/remove.c55
1 files changed, 41 insertions, 14 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 6def3624c68..fd77e2bde2e 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -77,8 +77,9 @@ void pci_remove_bus(struct pci_bus *pci_bus)
}
EXPORT_SYMBOL(pci_remove_bus);
+static void __pci_remove_behind_bridge(struct pci_dev *dev);
/**
- * pci_remove_bus_device - remove a PCI device and any children
+ * pci_stop_and_remove_bus_device - remove a PCI device and any children
* @dev: the device to remove
*
* Remove a PCI device from the device lists, informing the drivers
@@ -89,46 +90,72 @@ EXPORT_SYMBOL(pci_remove_bus);
* device lists, remove the /proc entry, and notify userspace
* (/sbin/hotplug).
*/
-static void __pci_remove_bus_device(struct pci_dev *dev)
+void __pci_remove_bus_device(struct pci_dev *dev)
{
if (dev->subordinate) {
struct pci_bus *b = dev->subordinate;
- pci_remove_behind_bridge(dev);
+ __pci_remove_behind_bridge(dev);
pci_remove_bus(b);
dev->subordinate = NULL;
}
pci_destroy_dev(dev);
}
-void pci_remove_bus_device(struct pci_dev *dev)
+EXPORT_SYMBOL(__pci_remove_bus_device);
+
+void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
pci_stop_bus_device(dev);
__pci_remove_bus_device(dev);
}
+static void __pci_remove_behind_bridge(struct pci_dev *dev)
+{
+ struct list_head *l, *n;
+
+ if (dev->subordinate)
+ list_for_each_safe(l, n, &dev->subordinate->devices)
+ __pci_remove_bus_device(pci_dev_b(l));
+}
+
+static void pci_stop_behind_bridge(struct pci_dev *dev)
+{
+ struct list_head *l, *n;
+
+ if (dev->subordinate)
+ list_for_each_safe(l, n, &dev->subordinate->devices)
+ pci_stop_bus_device(pci_dev_b(l));
+}
+
/**
- * pci_remove_behind_bridge - remove all devices behind a PCI bridge
+ * pci_stop_and_remove_behind_bridge - stop and remove all devices behind
+ * a PCI bridge
* @dev: PCI bridge device
*
* Remove all devices on the bus, except for the parent bridge.
* This also removes any child buses, and any devices they may
* contain in a depth-first manner.
*/
-void pci_remove_behind_bridge(struct pci_dev *dev)
+void pci_stop_and_remove_behind_bridge(struct pci_dev *dev)
{
- struct list_head *l, *n;
-
- if (dev->subordinate)
- list_for_each_safe(l, n, &dev->subordinate->devices)
- __pci_remove_bus_device(pci_dev_b(l));
+ pci_stop_behind_bridge(dev);
+ __pci_remove_behind_bridge(dev);
}
static void pci_stop_bus_devices(struct pci_bus *bus)
{
struct list_head *l, *n;
- list_for_each_safe(l, n, &bus->devices) {
+ /*
+ * VFs could be removed by pci_stop_and_remove_bus_device() in the
+ * pci_stop_bus_devices() code path for PF.
+ * aka, bus->devices get updated in the process.
+ * but VFs are inserted after PFs when SRIOV is enabled for PF,
+ * We can iterate the list backwards to get prev valid PF instead
+ * of removed VF.
+ */
+ list_for_each_prev_safe(l, n, &bus->devices) {
struct pci_dev *dev = pci_dev_b(l);
pci_stop_bus_device(dev);
}
@@ -150,6 +177,6 @@ void pci_stop_bus_device(struct pci_dev *dev)
pci_stop_dev(dev);
}
-EXPORT_SYMBOL(pci_remove_bus_device);
-EXPORT_SYMBOL(pci_remove_behind_bridge);
+EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
+EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge);
EXPORT_SYMBOL_GPL(pci_stop_bus_device);