aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2012-08-17 13:57:48 -0400
committerBjorn Helgaas <bhelgaas@google.com>2012-08-22 13:31:48 -0400
commit282e1d655fe7c7c2e6b0dd8166c4c6b7c2a1219b (patch)
tree9890411229a7f8b7314ddbf44e33554fa1b360b8
parent2ed168eeb3edec029aa0eca5cb981d6376f931f9 (diff)
PCI: Stop and remove devices in one pass
Previously, when we removed a PCI device, we made two passes over the hierarchy rooted at the device. In the first pass, we stopped all the devices, and in the second, we removed them. This patch combines the two passes into one so that we remove a device as soon as it and all its children have been stopped. Note that we previously stopped devices in reverse order and removed them in forward order. Now we stop and remove them in reverse order. Tested-by: Yijing Wang <wangyijing@huawei.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Yinghai Lu <yinghai@kernel.org>
-rw-r--r--drivers/pci/remove.c42
1 files changed, 7 insertions, 35 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 30d002e83e2d..38281042c31c 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -78,8 +78,6 @@ void pci_remove_bus(struct pci_bus *pci_bus)
78} 78}
79EXPORT_SYMBOL(pci_remove_bus); 79EXPORT_SYMBOL(pci_remove_bus);
80 80
81static void pci_stop_bus_device(struct pci_dev *dev);
82
83/** 81/**
84 * pci_stop_and_remove_bus_device - remove a PCI device and any children 82 * pci_stop_and_remove_bus_device - remove a PCI device and any children
85 * @dev: the device to remove 83 * @dev: the device to remove
@@ -92,38 +90,8 @@ static void pci_stop_bus_device(struct pci_dev *dev);
92 * device lists, remove the /proc entry, and notify userspace 90 * device lists, remove the /proc entry, and notify userspace
93 * (/sbin/hotplug). 91 * (/sbin/hotplug).
94 */ 92 */
95static void __pci_remove_bus_device(struct pci_dev *dev)
96{
97 struct pci_bus *bus = dev->subordinate;
98 struct pci_dev *child, *tmp;
99
100 if (bus) {
101 list_for_each_entry_safe(child, tmp, &bus->devices, bus_list)
102 __pci_remove_bus_device(child);
103
104 pci_remove_bus(bus);
105 dev->subordinate = NULL;
106 }
107
108 pci_destroy_dev(dev);
109}
110
111void pci_stop_and_remove_bus_device(struct pci_dev *dev) 93void pci_stop_and_remove_bus_device(struct pci_dev *dev)
112{ 94{
113 pci_stop_bus_device(dev);
114 __pci_remove_bus_device(dev);
115}
116
117/**
118 * pci_stop_bus_device - stop a PCI device and any children
119 * @dev: the device to stop
120 *
121 * Stop a PCI device (detach the driver, remove from the global list
122 * and so on). This also stop any subordinate buses and children in a
123 * depth-first manner.
124 */
125static void pci_stop_bus_device(struct pci_dev *dev)
126{
127 struct pci_bus *bus = dev->subordinate; 95 struct pci_bus *bus = dev->subordinate;
128 struct pci_dev *child, *tmp; 96 struct pci_dev *child, *tmp;
129 97
@@ -133,12 +101,16 @@ static void pci_stop_bus_device(struct pci_dev *dev)
133 * iterator. Therefore, iterate in reverse so we remove the VFs 101 * iterator. Therefore, iterate in reverse so we remove the VFs
134 * first, then the PF. 102 * first, then the PF.
135 */ 103 */
136 if (bus) 104 if (bus) {
137 list_for_each_entry_safe_reverse(child, tmp, 105 list_for_each_entry_safe_reverse(child, tmp,
138 &bus->devices, bus_list) 106 &bus->devices, bus_list)
139 pci_stop_bus_device(child); 107 pci_stop_and_remove_bus_device(child);
108
109 pci_remove_bus(bus);
110 dev->subordinate = NULL;
111 }
140 112
141 pci_stop_dev(dev); 113 pci_stop_dev(dev);
114 pci_destroy_dev(dev);
142} 115}
143
144EXPORT_SYMBOL(pci_stop_and_remove_bus_device); 116EXPORT_SYMBOL(pci_stop_and_remove_bus_device);