From 4c898c7f2f286b204fefc5dddb568f755d195d0c Mon Sep 17 00:00:00 2001 From: Daniel Ritz Date: Thu, 22 Sep 2005 00:47:11 -0700 Subject: [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 ;cardctl eject; rmmod . 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 Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/base/dd.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index d5bbce38282..3565e979530 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -40,6 +40,9 @@ */ void device_bind_driver(struct device * dev) { + if (klist_node_attached(&dev->knode_driver)) + return; + pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id, dev->driver->name); klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices); -- cgit v1.2.3-70-g09d2 From 3e51377dc412df9d4933c4fd1a147b5b560abe10 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Thu, 22 Sep 2005 00:47:36 -0700 Subject: [PATCH] fix class symlinks in sysfs The class symlinks in sysfs don't properly handle changing device names. To demonstrate, rename your network device from eth0 to eth1. Your pci (or usb, or whatever) device will still have a 'net:eth0' link, except now it points to /sys/class/net/eth1. The attached patch makes sure the class symlink name changes when the class device name changes. It isn't 100% correct, it should be using sysfs_rename_link. Unfortunately, sysfs_rename_link doesn't exist. Signed-off-by: Bill Nottingham Signed-off-by: Greg Kroah-Hartman Signed-off-by: Linus Torvalds --- drivers/base/class.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/class.c b/drivers/base/class.c index 3b112e3542f..ce23dc8c18c 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -669,6 +669,7 @@ void class_device_destroy(struct class *cls, dev_t devt) int class_device_rename(struct class_device *class_dev, char *new_name) { int error = 0; + char *old_class_name = NULL, *new_class_name = NULL; class_dev = class_device_get(class_dev); if (!class_dev) @@ -677,12 +678,24 @@ int class_device_rename(struct class_device *class_dev, char *new_name) pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id, new_name); + if (class_dev->dev) + old_class_name = make_class_name(class_dev); + strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN); error = kobject_rename(&class_dev->kobj, new_name); + if (class_dev->dev) { + new_class_name = make_class_name(class_dev); + sysfs_create_link(&class_dev->dev->kobj, &class_dev->kobj, + new_class_name); + sysfs_remove_link(&class_dev->dev->kobj, old_class_name); + } class_device_put(class_dev); + kfree(old_class_name); + kfree(new_class_name); + return error; } -- cgit v1.2.3-70-g09d2