diff options
author | Daniel Ritz <daniel.ritz@gmx.ch> | 2005-09-22 03:47:11 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-22 10:58:24 -0400 |
commit | 4c898c7f2f286b204fefc5dddb568f755d195d0c (patch) | |
tree | 026fead9aef220a5fd994e2eaa269ed598651d39 /drivers | |
parent | 0b50f81d5a63428f131ff20596f4e3d473e5b94f (diff) |
[PATCH] Driver Core: fis bus rescan devices race
bus_rescan_devices_helper() does not hold the dev->sem when it checks for
!dev->driver(). device_attach() holds the sem, but calls again
device_bind_driver() even when dev->driver is set.
What happens is that a first device_attach() call (module insertion time)
is on the way binding the device to a driver. Another thread calls
bus_rescan_devices(). Now when bus_rescan_devices_helper() checks for
dev->driver it is still NULL 'cos the the prior device_attach() is not yet
finished. But as soon as the first one releases the dev->sem the second
device_attach() tries to rebind the already bound device again.
device_bind_driver() does this blindly which leads to a corrupt
driver->klist_devices list (the device links itself, the head points to the
device). Later a call to device_release_driver() sets dev->driver to NULL
and breaks the link it has to itself on knode_driver. Rmmoding the driver
later calls driver_detach() which leads to an endless loop 'cos the list
head in klist_devices still points to the device. And since dev->driver is
NULL it's stuck with the same device forever. Boom. And rmmod hangs.
Very easy to reproduce with new-style pcmcia and a 16bit card. Just loop
modprobe <pcmcia-modules> ;cardctl eject; rmmod <card driver, pcmcia
modules>.
Easiest fix is to check if the device is already bound to a driver in
device_bind_driver(). This avoids the double binding.
Signed-off-by: Daniel Ritz <daniel.ritz@gmx.ch>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/dd.c | 3 |
1 files changed, 3 insertions, 0 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c index d5bbce38282f..3565e9795301 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c | |||
@@ -40,6 +40,9 @@ | |||
40 | */ | 40 | */ |
41 | void device_bind_driver(struct device * dev) | 41 | void device_bind_driver(struct device * dev) |
42 | { | 42 | { |
43 | if (klist_node_attached(&dev->knode_driver)) | ||
44 | return; | ||
45 | |||
43 | pr_debug("bound device '%s' to driver '%s'\n", | 46 | pr_debug("bound device '%s' to driver '%s'\n", |
44 | dev->bus_id, dev->driver->name); | 47 | dev->bus_id, dev->driver->name); |
45 | klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); | 48 | klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); |