Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
[firefly-linux-kernel-4.4.55.git] / drivers / pci / remove.c
index ef8b18c48f2641993f56aa643ed8a35579856971..fd77e2bde2e8feadc7c5b1cceab94374f00100fe 100644 (file)
@@ -79,7 +79,7 @@ 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
@@ -90,7 +90,7 @@ static void __pci_remove_behind_bridge(struct pci_dev *dev);
  * 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;
@@ -102,7 +102,9 @@ static void __pci_remove_bus_device(struct pci_dev *dev)
 
        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);
@@ -127,14 +129,15 @@ static void pci_stop_behind_bridge(struct pci_dev *dev)
 }
 
 /**
- * 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)
 {
        pci_stop_behind_bridge(dev);
        __pci_remove_behind_bridge(dev);
@@ -144,7 +147,15 @@ 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);
        }
@@ -166,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);