aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/dd.c51
1 files changed, 38 insertions, 13 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 8510918109e0..eab2030c506d 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -177,41 +177,66 @@ void driver_attach(struct device_driver * drv)
177 * @dev: device. 177 * @dev: device.
178 * 178 *
179 * Manually detach device from driver. 179 * Manually detach device from driver.
180 * Note that this is called without incrementing the bus 180 *
181 * reference count nor taking the bus's rwsem. Be sure that 181 * __device_release_driver() must be called with @dev->sem held.
182 * those are accounted for before calling this function.
183 */ 182 */
184void device_release_driver(struct device * dev) 183
184static void __device_release_driver(struct device * dev)
185{ 185{
186 struct device_driver * drv; 186 struct device_driver * drv;
187 187
188 down(&dev->sem); 188 drv = dev->driver;
189 if (dev->driver) { 189 if (drv) {
190 drv = dev->driver; 190 get_driver(drv);
191 sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); 191 sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj));
192 sysfs_remove_link(&dev->kobj, "driver"); 192 sysfs_remove_link(&dev->kobj, "driver");
193 klist_del(&dev->knode_driver); 193 klist_remove(&dev->knode_driver);
194 194
195 if (drv->remove) 195 if (drv->remove)
196 drv->remove(dev); 196 drv->remove(dev);
197 dev->driver = NULL; 197 dev->driver = NULL;
198 put_driver(drv);
198 } 199 }
199 up(&dev->sem);
200} 200}
201 201
202static int __remove_driver(struct device * dev, void * unused) 202void device_release_driver(struct device * dev)
203{ 203{
204 device_release_driver(dev); 204 /*
205 return 0; 205 * If anyone calls device_release_driver() recursively from
206 * within their ->remove callback for the same device, they
207 * will deadlock right here.
208 */
209 down(&dev->sem);
210 __device_release_driver(dev);
211 up(&dev->sem);
206} 212}
207 213
214
208/** 215/**
209 * driver_detach - detach driver from all devices it controls. 216 * driver_detach - detach driver from all devices it controls.
210 * @drv: driver. 217 * @drv: driver.
211 */ 218 */
212void driver_detach(struct device_driver * drv) 219void driver_detach(struct device_driver * drv)
213{ 220{
214 driver_for_each_device(drv, NULL, NULL, __remove_driver); 221 struct device * dev;
222
223 for (;;) {
224 spin_lock_irq(&drv->klist_devices.k_lock);
225 if (list_empty(&drv->klist_devices.k_list)) {
226 spin_unlock_irq(&drv->klist_devices.k_lock);
227 break;
228 }
229 dev = list_entry(drv->klist_devices.k_list.prev,
230 struct device, knode_driver.n_node);
231 get_device(dev);
232 spin_unlock_irq(&drv->klist_devices.k_lock);
233
234 down(&dev->sem);
235 if (dev->driver == drv)
236 __device_release_driver(dev);
237 up(&dev->sem);
238 put_device(dev);
239 }
215} 240}
216 241
217 242