diff options
Diffstat (limited to 'drivers/pci/remove.c')
-rw-r--r-- | drivers/pci/remove.c | 156 |
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) | |||
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 | { | ||
58 | if (pci_dev_driver(dev)) | ||
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 | { | 44 | { |
67 | pci_proc_detach_bus(pci_bus); | 45 | pci_proc_detach_bus(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); | 59 | static void pci_stop_bus_device(struct pci_dev *dev) |
82 | /** | ||
83 | * pci_stop_and_remove_bus_device - remove a PCI device and any children | ||
84 | * @dev: the device to remove | ||
85 | * | ||
86 | * Remove a PCI device from the device lists, informing the drivers | ||
87 | * that the device has been removed. We also remove any subordinate | ||
88 | * buses and children in a depth-first manner. | ||
89 | * | ||
90 | * For each device we remove, delete the device structure from the | ||
91 | * device lists, remove the /proc entry, and notify userspace | ||
92 | * (/sbin/hotplug). | ||
93 | */ | ||
94 | void __pci_remove_bus_device(struct pci_dev *dev) | ||
95 | { | 60 | { |
96 | if (dev->subordinate) { | 61 | struct pci_bus *bus = dev->subordinate; |
97 | struct pci_bus *b = dev->subordinate; | 62 | struct pci_dev *child, *tmp; |
98 | 63 | ||
99 | __pci_remove_behind_bridge(dev); | 64 | /* |
100 | pci_remove_bus(b); | 65 | * Stopping an SR-IOV PF device removes all the associated VFs, |
101 | dev->subordinate = NULL; | 66 | * which will update the bus->devices list and confuse the |
67 | * iterator. Therefore, iterate in reverse so we remove the VFs | ||
68 | * first, then the PF. | ||
69 | */ | ||
70 | if (bus) { | ||
71 | list_for_each_entry_safe_reverse(child, tmp, | ||
72 | &bus->devices, bus_list) | ||
73 | pci_stop_bus_device(child); | ||
102 | } | 74 | } |
103 | 75 | ||
104 | pci_destroy_dev(dev); | 76 | pci_stop_dev(dev); |
105 | } | ||
106 | EXPORT_SYMBOL(__pci_remove_bus_device); | ||
107 | |||
108 | void pci_stop_and_remove_bus_device(struct pci_dev *dev) | ||
109 | { | ||
110 | pci_stop_bus_device(dev); | ||
111 | __pci_remove_bus_device(dev); | ||
112 | } | 77 | } |
113 | 78 | ||
114 | static void __pci_remove_behind_bridge(struct pci_dev *dev) | 79 | static void pci_remove_bus_device(struct pci_dev *dev) |
115 | { | 80 | { |
116 | struct list_head *l, *n; | 81 | struct pci_bus *bus = dev->subordinate; |
82 | struct pci_dev *child, *tmp; | ||
117 | 83 | ||
118 | if (dev->subordinate) | 84 | if (bus) { |
119 | list_for_each_safe(l, n, &dev->subordinate->devices) | 85 | list_for_each_entry_safe(child, tmp, |
120 | __pci_remove_bus_device(pci_dev_b(l)); | 86 | &bus->devices, bus_list) |
121 | } | 87 | pci_remove_bus_device(child); |
122 | 88 | ||
123 | static void pci_stop_behind_bridge(struct pci_dev *dev) | 89 | pci_remove_bus(bus); |
124 | { | 90 | dev->subordinate = NULL; |
125 | struct list_head *l, *n; | 91 | } |
126 | 92 | ||
127 | if (dev->subordinate) | 93 | pci_destroy_dev(dev); |
128 | list_for_each_safe(l, n, &dev->subordinate->devices) | ||
129 | pci_stop_bus_device(pci_dev_b(l)); | ||
130 | } | 94 | } |
131 | 95 | ||
132 | /** | 96 | /** |
133 | * pci_stop_and_remove_behind_bridge - stop and remove all devices behind | 97 | * pci_stop_and_remove_bus_device - remove a PCI device and any children |
134 | * a PCI bridge | 98 | * @dev: the device to remove |
135 | * @dev: PCI bridge device | ||
136 | * | 99 | * |
137 | * Remove all devices on the bus, except for the parent bridge. | 100 | * Remove a PCI device from the device lists, informing the drivers |
138 | * This also removes any child buses, and any devices they may | 101 | * that the device has been removed. We also remove any subordinate |
139 | * contain in a depth-first manner. | 102 | * buses and children 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 | |||
151 | /* | ||
152 | * VFs could be removed by pci_stop_and_remove_bus_device() in the | ||
153 | * pci_stop_bus_devices() code path for PF. | ||
154 | * aka, bus->devices get updated in the process. | ||
155 | * but VFs are inserted after PFs when SRIOV is enabled for PF, | ||
156 | * We can iterate the list backwards to get prev valid PF instead | ||
157 | * of removed VF. | ||
158 | */ | ||
159 | list_for_each_prev_safe(l, n, &bus->devices) { | ||
160 | struct pci_dev *dev = pci_dev_b(l); | ||
161 | pci_stop_bus_device(dev); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * pci_stop_bus_device - stop a PCI device and any children | ||
167 | * @dev: the device to stop | ||
168 | * | 103 | * |
169 | * Stop a PCI device (detach the driver, remove from the global list | 104 | * For each device we remove, delete the device structure from the |
170 | * and so on). This also stop any subordinate buses and children in a | 105 | * device lists, remove the /proc entry, and notify userspace |
171 | * depth-first manner. | 106 | * (/sbin/hotplug). |
172 | */ | 107 | */ |
173 | void pci_stop_bus_device(struct pci_dev *dev) | 108 | void pci_stop_and_remove_bus_device(struct pci_dev *dev) |
174 | { | 109 | { |
175 | if (dev->subordinate) | 110 | pci_stop_bus_device(dev); |
176 | pci_stop_bus_devices(dev->subordinate); | 111 | pci_remove_bus_device(dev); |
177 | |||
178 | pci_stop_dev(dev); | ||
179 | } | 112 | } |
180 | |||
181 | EXPORT_SYMBOL(pci_stop_and_remove_bus_device); | 113 | 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); | ||