diff options
Diffstat (limited to 'drivers/pci/remove.c')
-rw-r--r-- | drivers/pci/remove.c | 131 |
1 files changed, 21 insertions, 110 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 04a4861b4749..4f9ca9162895 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c | |||
@@ -32,53 +32,30 @@ static void pci_stop_dev(struct pci_dev *dev) | |||
32 | 32 | ||
33 | static void pci_destroy_dev(struct pci_dev *dev) | 33 | static void pci_destroy_dev(struct pci_dev *dev) |
34 | { | 34 | { |
35 | /* Remove the device from the device lists, and prevent any further | ||
36 | * list accesses from this device */ | ||
37 | down_write(&pci_bus_sem); | 35 | down_write(&pci_bus_sem); |
38 | list_del(&dev->bus_list); | 36 | list_del(&dev->bus_list); |
39 | dev->bus_list.next = dev->bus_list.prev = NULL; | ||
40 | up_write(&pci_bus_sem); | 37 | up_write(&pci_bus_sem); |
41 | 38 | ||
42 | pci_free_resources(dev); | 39 | pci_free_resources(dev); |
43 | pci_dev_put(dev); | 40 | pci_dev_put(dev); |
44 | } | 41 | } |
45 | 42 | ||
46 | /** | 43 | void pci_remove_bus(struct pci_bus *bus) |
47 | * pci_remove_device_safe - remove an unused hotplug device | ||
48 | * @dev: the device to remove | ||
49 | * | ||
50 | * Delete the device structure from the device lists and | ||
51 | * notify userspace (/sbin/hotplug), but only if the device | ||
52 | * in question is not being used by a driver. | ||
53 | * Returns 0 on success. | ||
54 | */ | ||
55 | #if 0 | ||
56 | int pci_remove_device_safe(struct pci_dev *dev) | ||
57 | { | 44 | { |
58 | if (pci_dev_driver(dev)) | 45 | pci_proc_detach_bus(bus); |
59 | return -EBUSY; | ||
60 | pci_destroy_dev(dev); | ||
61 | return 0; | ||
62 | } | ||
63 | #endif /* 0 */ | ||
64 | |||
65 | void pci_remove_bus(struct pci_bus *pci_bus) | ||
66 | { | ||
67 | pci_proc_detach_bus(pci_bus); | ||
68 | 46 | ||
69 | down_write(&pci_bus_sem); | 47 | down_write(&pci_bus_sem); |
70 | list_del(&pci_bus->node); | 48 | list_del(&bus->node); |
71 | pci_bus_release_busn_res(pci_bus); | 49 | pci_bus_release_busn_res(bus); |
72 | up_write(&pci_bus_sem); | 50 | up_write(&pci_bus_sem); |
73 | if (!pci_bus->is_added) | 51 | if (!bus->is_added) |
74 | return; | 52 | return; |
75 | 53 | ||
76 | pci_remove_legacy_files(pci_bus); | 54 | pci_remove_legacy_files(bus); |
77 | device_unregister(&pci_bus->dev); | 55 | device_unregister(&bus->dev); |
78 | } | 56 | } |
79 | EXPORT_SYMBOL(pci_remove_bus); | 57 | EXPORT_SYMBOL(pci_remove_bus); |
80 | 58 | ||
81 | static void __pci_remove_behind_bridge(struct pci_dev *dev); | ||
82 | /** | 59 | /** |
83 | * pci_stop_and_remove_bus_device - remove a PCI device and any children | 60 | * pci_stop_and_remove_bus_device - remove a PCI device and any children |
84 | * @dev: the device to remove | 61 | * @dev: the device to remove |
@@ -91,93 +68,27 @@ static void __pci_remove_behind_bridge(struct pci_dev *dev); | |||
91 | * device lists, remove the /proc entry, and notify userspace | 68 | * device lists, remove the /proc entry, and notify userspace |
92 | * (/sbin/hotplug). | 69 | * (/sbin/hotplug). |
93 | */ | 70 | */ |
94 | void __pci_remove_bus_device(struct pci_dev *dev) | ||
95 | { | ||
96 | if (dev->subordinate) { | ||
97 | struct pci_bus *b = dev->subordinate; | ||
98 | |||
99 | __pci_remove_behind_bridge(dev); | ||
100 | pci_remove_bus(b); | ||
101 | dev->subordinate = NULL; | ||
102 | } | ||
103 | |||
104 | pci_destroy_dev(dev); | ||
105 | } | ||
106 | EXPORT_SYMBOL(__pci_remove_bus_device); | ||
107 | |||
108 | void pci_stop_and_remove_bus_device(struct pci_dev *dev) | 71 | void pci_stop_and_remove_bus_device(struct pci_dev *dev) |
109 | { | 72 | { |
110 | pci_stop_bus_device(dev); | 73 | struct pci_bus *bus = dev->subordinate; |
111 | __pci_remove_bus_device(dev); | 74 | struct pci_dev *child, *tmp; |
112 | } | ||
113 | |||
114 | static void __pci_remove_behind_bridge(struct pci_dev *dev) | ||
115 | { | ||
116 | struct list_head *l, *n; | ||
117 | |||
118 | if (dev->subordinate) | ||
119 | list_for_each_safe(l, n, &dev->subordinate->devices) | ||
120 | __pci_remove_bus_device(pci_dev_b(l)); | ||
121 | } | ||
122 | |||
123 | static void pci_stop_behind_bridge(struct pci_dev *dev) | ||
124 | { | ||
125 | struct list_head *l, *n; | ||
126 | |||
127 | if (dev->subordinate) | ||
128 | list_for_each_safe(l, n, &dev->subordinate->devices) | ||
129 | pci_stop_bus_device(pci_dev_b(l)); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * pci_stop_and_remove_behind_bridge - stop and remove all devices behind | ||
134 | * a PCI bridge | ||
135 | * @dev: PCI bridge device | ||
136 | * | ||
137 | * Remove all devices on the bus, except for the parent bridge. | ||
138 | * This also removes any child buses, and any devices they may | ||
139 | * contain in a depth-first manner. | ||
140 | */ | ||
141 | void pci_stop_and_remove_behind_bridge(struct pci_dev *dev) | ||
142 | { | ||
143 | pci_stop_behind_bridge(dev); | ||
144 | __pci_remove_behind_bridge(dev); | ||
145 | } | ||
146 | |||
147 | static void pci_stop_bus_devices(struct pci_bus *bus) | ||
148 | { | ||
149 | struct list_head *l, *n; | ||
150 | 75 | ||
151 | /* | 76 | /* |
152 | * VFs could be removed by pci_stop_and_remove_bus_device() in the | 77 | * Removing an SR-IOV PF device removes all the associated VFs, |
153 | * pci_stop_bus_devices() code path for PF. | 78 | * which will update the bus->devices list and confuse the |
154 | * aka, bus->devices get updated in the process. | 79 | * iterator. Therefore, iterate in reverse so we remove the VFs |
155 | * but VFs are inserted after PFs when SRIOV is enabled for PF, | 80 | * first, then the PF. |
156 | * We can iterate the list backwards to get prev valid PF instead | ||
157 | * of removed VF. | ||
158 | */ | 81 | */ |
159 | list_for_each_prev_safe(l, n, &bus->devices) { | 82 | if (bus) { |
160 | struct pci_dev *dev = pci_dev_b(l); | 83 | list_for_each_entry_safe_reverse(child, tmp, |
161 | pci_stop_bus_device(dev); | 84 | &bus->devices, bus_list) |
162 | } | 85 | pci_stop_and_remove_bus_device(child); |
163 | } | ||
164 | 86 | ||
165 | /** | 87 | pci_remove_bus(bus); |
166 | * pci_stop_bus_device - stop a PCI device and any children | 88 | dev->subordinate = NULL; |
167 | * @dev: the device to stop | 89 | } |
168 | * | ||
169 | * Stop a PCI device (detach the driver, remove from the global list | ||
170 | * and so on). This also stop any subordinate buses and children in a | ||
171 | * depth-first manner. | ||
172 | */ | ||
173 | void pci_stop_bus_device(struct pci_dev *dev) | ||
174 | { | ||
175 | if (dev->subordinate) | ||
176 | pci_stop_bus_devices(dev->subordinate); | ||
177 | 90 | ||
178 | pci_stop_dev(dev); | 91 | pci_stop_dev(dev); |
92 | pci_destroy_dev(dev); | ||
179 | } | 93 | } |
180 | |||
181 | EXPORT_SYMBOL(pci_stop_and_remove_bus_device); | 94 | EXPORT_SYMBOL(pci_stop_and_remove_bus_device); |
182 | EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge); | ||
183 | EXPORT_SYMBOL_GPL(pci_stop_bus_device); | ||