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.c156
1 files changed, 43 insertions, 113 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 04a4861b4749..513972f3ed13 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -32,152 +32,82 @@ static void pci_stop_dev(struct pci_dev *dev)
static void pci_destroy_dev(struct pci_dev *dev)
{
- /* Remove the device from the device lists, and prevent any further
- * list accesses from this device */
down_write(&pci_bus_sem);
list_del(&dev->bus_list);
- dev->bus_list.next = dev->bus_list.prev = NULL;
up_write(&pci_bus_sem);
pci_free_resources(dev);
pci_dev_put(dev);
}
-/**
- * pci_remove_device_safe - remove an unused hotplug device
- * @dev: the device to remove
- *
- * Delete the device structure from the device lists and
- * notify userspace (/sbin/hotplug), but only if the device
- * in question is not being used by a driver.
- * Returns 0 on success.
- */
-#if 0
-int pci_remove_device_safe(struct pci_dev *dev)
-{
- if (pci_dev_driver(dev))
- return -EBUSY;
- pci_destroy_dev(dev);
- return 0;
-}
-#endif /* 0 */
-
-void pci_remove_bus(struct pci_bus *pci_bus)
+void pci_remove_bus(struct pci_bus *bus)
{
- pci_proc_detach_bus(pci_bus);
+ pci_proc_detach_bus(bus);
down_write(&pci_bus_sem);
- list_del(&pci_bus->node);
- pci_bus_release_busn_res(pci_bus);
+ list_del(&bus->node);
+ pci_bus_release_busn_res(bus);
up_write(&pci_bus_sem);
- if (!pci_bus->is_added)
+ if (!bus->is_added)
return;
- pci_remove_legacy_files(pci_bus);
- device_unregister(&pci_bus->dev);
+ pci_remove_legacy_files(bus);
+ device_unregister(&bus->dev);
}
EXPORT_SYMBOL(pci_remove_bus);
-static void __pci_remove_behind_bridge(struct pci_dev *dev);
-/**
- * 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
- * that the device has been removed. We also remove any subordinate
- * buses and children in a depth-first manner.
- *
- * For each device we remove, delete the device structure from the
- * device lists, remove the /proc entry, and notify userspace
- * (/sbin/hotplug).
- */
-void __pci_remove_bus_device(struct pci_dev *dev)
+static void pci_stop_bus_device(struct pci_dev *dev)
{
- if (dev->subordinate) {
- struct pci_bus *b = dev->subordinate;
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
- __pci_remove_behind_bridge(dev);
- pci_remove_bus(b);
- dev->subordinate = NULL;
+ /*
+ * Stopping an SR-IOV PF device removes all the associated VFs,
+ * which will update the bus->devices list and confuse the
+ * iterator. Therefore, iterate in reverse so we remove the VFs
+ * first, then the PF.
+ */
+ if (bus) {
+ list_for_each_entry_safe_reverse(child, tmp,
+ &bus->devices, bus_list)
+ pci_stop_bus_device(child);
}
- pci_destroy_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);
+ pci_stop_dev(dev);
}
-static void __pci_remove_behind_bridge(struct pci_dev *dev)
+static void pci_remove_bus_device(struct pci_dev *dev)
{
- struct list_head *l, *n;
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
- if (dev->subordinate)
- list_for_each_safe(l, n, &dev->subordinate->devices)
- __pci_remove_bus_device(pci_dev_b(l));
-}
+ if (bus) {
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
-static void pci_stop_behind_bridge(struct pci_dev *dev)
-{
- struct list_head *l, *n;
+ pci_remove_bus(bus);
+ dev->subordinate = NULL;
+ }
- if (dev->subordinate)
- list_for_each_safe(l, n, &dev->subordinate->devices)
- pci_stop_bus_device(pci_dev_b(l));
+ pci_destroy_dev(dev);
}
/**
- * pci_stop_and_remove_behind_bridge - stop and remove all devices behind
- * a PCI bridge
- * @dev: PCI bridge device
+ * pci_stop_and_remove_bus_device - remove a PCI device and any children
+ * @dev: the device to remove
*
- * 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_stop_and_remove_behind_bridge(struct pci_dev *dev)
-{
- 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;
-
- /*
- * 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);
- }
-}
-
-/**
- * pci_stop_bus_device - stop a PCI device and any children
- * @dev: the device to stop
+ * Remove a PCI device from the device lists, informing the drivers
+ * that the device has been removed. We also remove any subordinate
+ * buses and children in a depth-first manner.
*
- * Stop a PCI device (detach the driver, remove from the global list
- * and so on). This also stop any subordinate buses and children in a
- * depth-first manner.
+ * For each device we remove, delete the device structure from the
+ * device lists, remove the /proc entry, and notify userspace
+ * (/sbin/hotplug).
*/
-void pci_stop_bus_device(struct pci_dev *dev)
+void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
- if (dev->subordinate)
- pci_stop_bus_devices(dev->subordinate);
-
- pci_stop_dev(dev);
+ pci_stop_bus_device(dev);
+ pci_remove_bus_device(dev);
}
-
EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
-EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge);
-EXPORT_SYMBOL_GPL(pci_stop_bus_device);