aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2005-11-17 16:54:12 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-01-04 19:18:08 -0500
commitbf74ad5bc41727d5f2f1c6bedb2c1fac394de731 (patch)
tree1e46f41550a9fe6df40fedeace23f5aff656b478 /drivers/base
parent6d20b035dee4300e9786c6e1cb77a765c7f9460a (diff)
[PATCH] Hold the device's parent's lock during probe and remove
This patch (as604) makes the driver core hold a device's parent's lock as well as the device's lock during calls to the probe and remove methods in a driver. This facility is needed by USB device drivers, owing to the peculiar way USB devices work: A device provides multiple interfaces, and drivers are bound to interfaces rather than to devices; Nevertheless a reset, reset-configuration, suspend, or resume affects the entire device and requires the caller to hold the lock for the device, not just a lock for one of the interfaces. Since a USB driver's probe method is always called with the interface lock held, the locking order rules (always lock parent before child) prevent these methods from acquiring the device lock. The solution provided here is to call all probe and remove methods, for all devices (not just USB), with the parent lock already acquired. Although currently only the USB subsystem requires these changes, people have mentioned in prior discussion that the overhead of acquiring an extra semaphore in all the prove/remove sequences is not overly large. Up to now, the USB core has been using its own set of private semaphores. A followup patch will remove them, relying entirely on the device semaphores provided by the driver core. The code paths affected by this patch are: device_add and device_del: The USB core already holds the parent lock, so no actual change is needed. driver_register and driver_unregister: The driver core will now lock both the parent and the device before probing or removing. driver_bind and driver_unbind (in sysfs): These routines will now lock both the parent and the device before binding or unbinding. bus_rescan_devices: The helper routine will lock the parent before probing a device. I have not tested this patch for conflicts with other subsystems. As far as I can see, the only possibility of conflict would lie in the bus_rescan_devices pathway, and it seems pretty remote. Nevertheless, it would be good for this to get a lot of testing in -mm. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/bus.c15
-rw-r--r--drivers/base/dd.c15
2 files changed, 28 insertions, 2 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index fa601b085eba..e3f915a24891 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -152,7 +152,11 @@ static ssize_t driver_unbind(struct device_driver *drv,
152 152
153 dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); 153 dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
154 if (dev && dev->driver == drv) { 154 if (dev && dev->driver == drv) {
155 if (dev->parent) /* Needed for USB */
156 down(&dev->parent->sem);
155 device_release_driver(dev); 157 device_release_driver(dev);
158 if (dev->parent)
159 up(&dev->parent->sem);
156 err = count; 160 err = count;
157 } 161 }
158 put_device(dev); 162 put_device(dev);
@@ -175,9 +179,13 @@ static ssize_t driver_bind(struct device_driver *drv,
175 179
176 dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); 180 dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
177 if (dev && dev->driver == NULL) { 181 if (dev && dev->driver == NULL) {
182 if (dev->parent) /* Needed for USB */
183 down(&dev->parent->sem);
178 down(&dev->sem); 184 down(&dev->sem);
179 err = driver_probe_device(drv, dev); 185 err = driver_probe_device(drv, dev);
180 up(&dev->sem); 186 up(&dev->sem);
187 if (dev->parent)
188 up(&dev->parent->sem);
181 } 189 }
182 put_device(dev); 190 put_device(dev);
183 put_bus(bus); 191 put_bus(bus);
@@ -484,8 +492,13 @@ void bus_remove_driver(struct device_driver * drv)
484/* Helper for bus_rescan_devices's iter */ 492/* Helper for bus_rescan_devices's iter */
485static int bus_rescan_devices_helper(struct device *dev, void *data) 493static int bus_rescan_devices_helper(struct device *dev, void *data)
486{ 494{
487 if (!dev->driver) 495 if (!dev->driver) {
496 if (dev->parent) /* Needed for USB */
497 down(&dev->parent->sem);
488 device_attach(dev); 498 device_attach(dev);
499 if (dev->parent)
500 up(&dev->parent->sem);
501 }
489 return 0; 502 return 0;
490} 503}
491 504
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 3b419c9a1e7e..2b905016664d 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -65,7 +65,8 @@ void device_bind_driver(struct device * dev)
65 * This function returns 1 if a match is found, an error if one 65 * This function returns 1 if a match is found, an error if one
66 * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise. 66 * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise.
67 * 67 *
68 * This function must be called with @dev->sem held. 68 * This function must be called with @dev->sem held. When called
69 * for a USB interface, @dev->parent->sem must be held as well.
69 */ 70 */
70int driver_probe_device(struct device_driver * drv, struct device * dev) 71int driver_probe_device(struct device_driver * drv, struct device * dev)
71{ 72{
@@ -123,6 +124,8 @@ static int __device_attach(struct device_driver * drv, void * data)
123 * 124 *
124 * Returns 1 if the device was bound to a driver; 125 * Returns 1 if the device was bound to a driver;
125 * 0 if no matching device was found; error code otherwise. 126 * 0 if no matching device was found; error code otherwise.
127 *
128 * When called for a USB interface, @dev->parent->sem must be held.
126 */ 129 */
127int device_attach(struct device * dev) 130int device_attach(struct device * dev)
128{ 131{
@@ -152,10 +155,14 @@ static int __driver_attach(struct device * dev, void * data)
152 * is an error. 155 * is an error.
153 */ 156 */
154 157
158 if (dev->parent) /* Needed for USB */
159 down(&dev->parent->sem);
155 down(&dev->sem); 160 down(&dev->sem);
156 if (!dev->driver) 161 if (!dev->driver)
157 driver_probe_device(drv, dev); 162 driver_probe_device(drv, dev);
158 up(&dev->sem); 163 up(&dev->sem);
164 if (dev->parent)
165 up(&dev->parent->sem);
159 166
160 return 0; 167 return 0;
161} 168}
@@ -181,6 +188,8 @@ void driver_attach(struct device_driver * drv)
181 * Manually detach device from driver. 188 * Manually detach device from driver.
182 * 189 *
183 * __device_release_driver() must be called with @dev->sem held. 190 * __device_release_driver() must be called with @dev->sem held.
191 * When called for a USB interface, @dev->parent->sem must be held
192 * as well.
184 */ 193 */
185 194
186static void __device_release_driver(struct device * dev) 195static void __device_release_driver(struct device * dev)
@@ -233,10 +242,14 @@ void driver_detach(struct device_driver * drv)
233 get_device(dev); 242 get_device(dev);
234 spin_unlock(&drv->klist_devices.k_lock); 243 spin_unlock(&drv->klist_devices.k_lock);
235 244
245 if (dev->parent) /* Needed for USB */
246 down(&dev->parent->sem);
236 down(&dev->sem); 247 down(&dev->sem);
237 if (dev->driver == drv) 248 if (dev->driver == drv)
238 __device_release_driver(dev); 249 __device_release_driver(dev);
239 up(&dev->sem); 250 up(&dev->sem);
251 if (dev->parent)
252 up(&dev->parent->sem);
240 put_device(dev); 253 put_device(dev);
241 } 254 }
242} 255}