aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci/remove.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/remove.c')
-rw-r--r--drivers/pci/remove.c171
1 files changed, 89 insertions, 82 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 176615e7231..8bd76c9ba21 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -7,8 +7,6 @@ static void pci_free_resources(struct pci_dev *dev)
{
int i;
- msi_remove_pci_irq_vectors(dev);
-
pci_cleanup_rom(dev);
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *res = dev->resource + i;
@@ -19,10 +17,12 @@ static void pci_free_resources(struct pci_dev *dev)
static void pci_stop_dev(struct pci_dev *dev)
{
+ pci_pme_active(dev, false);
+
if (dev->is_added) {
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
- device_unregister(&dev->dev);
+ device_release_driver(&dev->dev);
dev->is_added = 0;
}
@@ -32,55 +32,72 @@ 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 */
+ if (!dev->dev.kobj.parent)
+ return;
+
+ device_del(&dev->dev);
+
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);
+ put_device(&dev->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)
+void pci_remove_bus(struct pci_bus *bus)
{
- if (pci_dev_driver(dev))
- return -EBUSY;
- pci_destroy_dev(dev);
- return 0;
+ pci_proc_detach_bus(bus);
+
+ down_write(&pci_bus_sem);
+ list_del(&bus->node);
+ pci_bus_release_busn_res(bus);
+ up_write(&pci_bus_sem);
+ pci_remove_legacy_files(bus);
+ pcibios_remove_bus(bus);
+ device_unregister(&bus->dev);
}
-#endif /* 0 */
+EXPORT_SYMBOL(pci_remove_bus);
-void pci_remove_bus(struct pci_bus *pci_bus)
+static void pci_stop_bus_device(struct pci_dev *dev)
{
- pci_proc_detach_bus(pci_bus);
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
+
+ /*
+ * 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);
+ }
- down_write(&pci_bus_sem);
- list_del(&pci_bus->node);
- up_write(&pci_bus_sem);
- if (!pci_bus->is_added)
- return;
+ pci_stop_dev(dev);
+}
+
+static void pci_remove_bus_device(struct pci_dev *dev)
+{
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
- pci_remove_legacy_files(pci_bus);
- device_remove_file(&pci_bus->dev, &dev_attr_cpuaffinity);
- device_remove_file(&pci_bus->dev, &dev_attr_cpulistaffinity);
- device_unregister(&pci_bus->dev);
+ if (bus) {
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
+
+ pci_remove_bus(bus);
+ dev->subordinate = NULL;
+ }
+
+ pci_destroy_dev(dev);
}
-EXPORT_SYMBOL(pci_remove_bus);
/**
- * 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
@@ -91,63 +108,53 @@ EXPORT_SYMBOL(pci_remove_bus);
* device lists, remove the /proc entry, and notify userspace
* (/sbin/hotplug).
*/
-void pci_remove_bus_device(struct pci_dev *dev)
+void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
pci_stop_bus_device(dev);
- if (dev->subordinate) {
- struct pci_bus *b = dev->subordinate;
-
- pci_remove_behind_bridge(dev);
- pci_remove_bus(b);
- dev->subordinate = NULL;
- }
-
- pci_destroy_dev(dev);
+ pci_remove_bus_device(dev);
}
+EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
-/**
- * pci_remove_behind_bridge - 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_bus_device_locked(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_lock_rescan_remove();
+ pci_stop_and_remove_bus_device(dev);
+ pci_unlock_rescan_remove();
}
+EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked);
-static void pci_stop_bus_devices(struct pci_bus *bus)
+void pci_stop_root_bus(struct pci_bus *bus)
{
- struct list_head *l, *n;
+ struct pci_dev *child, *tmp;
+ struct pci_host_bridge *host_bridge;
- list_for_each_safe(l, n, &bus->devices) {
- struct pci_dev *dev = pci_dev_b(l);
- pci_stop_bus_device(dev);
- }
+ if (!pci_is_root_bus(bus))
+ return;
+
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ list_for_each_entry_safe_reverse(child, tmp,
+ &bus->devices, bus_list)
+ pci_stop_bus_device(child);
+
+ /* stop the host bridge */
+ device_release_driver(&host_bridge->dev);
}
-/**
- * pci_stop_bus_device - stop a PCI device and any children
- * @dev: the device to stop
- *
- * 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.
- */
-void pci_stop_bus_device(struct pci_dev *dev)
+void pci_remove_root_bus(struct pci_bus *bus)
{
- if (dev->subordinate)
- pci_stop_bus_devices(dev->subordinate);
+ struct pci_dev *child, *tmp;
+ struct pci_host_bridge *host_bridge;
- pci_stop_dev(dev);
-}
+ if (!pci_is_root_bus(bus))
+ return;
+
+ host_bridge = to_pci_host_bridge(bus->bridge);
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
+ pci_remove_bus(bus);
+ host_bridge->bus = NULL;
-EXPORT_SYMBOL(pci_remove_bus_device);
-EXPORT_SYMBOL(pci_remove_behind_bridge);
-EXPORT_SYMBOL_GPL(pci_stop_bus_device);
+ /* remove the host bridge */
+ device_unregister(&host_bridge->dev);
+}