提交 bf74ad5b 编写于 作者: A Alan Stern 提交者: Greg Kroah-Hartman

[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: NAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 6d20b035
...@@ -152,7 +152,11 @@ static ssize_t driver_unbind(struct device_driver *drv, ...@@ -152,7 +152,11 @@ static ssize_t driver_unbind(struct device_driver *drv,
dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
if (dev && dev->driver == drv) { if (dev && dev->driver == drv) {
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
device_release_driver(dev); device_release_driver(dev);
if (dev->parent)
up(&dev->parent->sem);
err = count; err = count;
} }
put_device(dev); put_device(dev);
...@@ -175,9 +179,13 @@ static ssize_t driver_bind(struct device_driver *drv, ...@@ -175,9 +179,13 @@ static ssize_t driver_bind(struct device_driver *drv,
dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); dev = bus_find_device(bus, NULL, (void *)buf, driver_helper);
if (dev && dev->driver == NULL) { if (dev && dev->driver == NULL) {
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem); down(&dev->sem);
err = driver_probe_device(drv, dev); err = driver_probe_device(drv, dev);
up(&dev->sem); up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
} }
put_device(dev); put_device(dev);
put_bus(bus); put_bus(bus);
...@@ -484,8 +492,13 @@ void bus_remove_driver(struct device_driver * drv) ...@@ -484,8 +492,13 @@ void bus_remove_driver(struct device_driver * drv)
/* Helper for bus_rescan_devices's iter */ /* Helper for bus_rescan_devices's iter */
static int bus_rescan_devices_helper(struct device *dev, void *data) static int bus_rescan_devices_helper(struct device *dev, void *data)
{ {
if (!dev->driver) if (!dev->driver) {
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
device_attach(dev); device_attach(dev);
if (dev->parent)
up(&dev->parent->sem);
}
return 0; return 0;
} }
......
...@@ -65,7 +65,8 @@ void device_bind_driver(struct device * dev) ...@@ -65,7 +65,8 @@ void device_bind_driver(struct device * dev)
* This function returns 1 if a match is found, an error if one * This function returns 1 if a match is found, an error if one
* occurs (that is not -ENODEV or -ENXIO), and 0 otherwise. * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise.
* *
* This function must be called with @dev->sem held. * This function must be called with @dev->sem held. When called
* for a USB interface, @dev->parent->sem must be held as well.
*/ */
int driver_probe_device(struct device_driver * drv, struct device * dev) int driver_probe_device(struct device_driver * drv, struct device * dev)
{ {
...@@ -123,6 +124,8 @@ static int __device_attach(struct device_driver * drv, void * data) ...@@ -123,6 +124,8 @@ static int __device_attach(struct device_driver * drv, void * data)
* *
* Returns 1 if the device was bound to a driver; * Returns 1 if the device was bound to a driver;
* 0 if no matching device was found; error code otherwise. * 0 if no matching device was found; error code otherwise.
*
* When called for a USB interface, @dev->parent->sem must be held.
*/ */
int device_attach(struct device * dev) int device_attach(struct device * dev)
{ {
...@@ -152,10 +155,14 @@ static int __driver_attach(struct device * dev, void * data) ...@@ -152,10 +155,14 @@ static int __driver_attach(struct device * dev, void * data)
* is an error. * is an error.
*/ */
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem); down(&dev->sem);
if (!dev->driver) if (!dev->driver)
driver_probe_device(drv, dev); driver_probe_device(drv, dev);
up(&dev->sem); up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
return 0; return 0;
} }
...@@ -181,6 +188,8 @@ void driver_attach(struct device_driver * drv) ...@@ -181,6 +188,8 @@ void driver_attach(struct device_driver * drv)
* Manually detach device from driver. * Manually detach device from driver.
* *
* __device_release_driver() must be called with @dev->sem held. * __device_release_driver() must be called with @dev->sem held.
* When called for a USB interface, @dev->parent->sem must be held
* as well.
*/ */
static void __device_release_driver(struct device * dev) static void __device_release_driver(struct device * dev)
...@@ -233,10 +242,14 @@ void driver_detach(struct device_driver * drv) ...@@ -233,10 +242,14 @@ void driver_detach(struct device_driver * drv)
get_device(dev); get_device(dev);
spin_unlock(&drv->klist_devices.k_lock); spin_unlock(&drv->klist_devices.k_lock);
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
down(&dev->sem); down(&dev->sem);
if (dev->driver == drv) if (dev->driver == drv)
__device_release_driver(dev); __device_release_driver(dev);
up(&dev->sem); up(&dev->sem);
if (dev->parent)
up(&dev->parent->sem);
put_device(dev); put_device(dev);
} }
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册