Messages in this thread Patch in this message | | | Date | Wed, 5 Jun 2002 16:20:39 -0700 (PDT) | From | Patrick Mochel <> | Subject | device model update 2/2 |
| |
This is an attempt to better locking for both device and driver removal. Comments gladly welcomed.
Pull from bk://ldm.bkbits.net/linux-2.5
-pat
ChangeSet@1.456, 2002-06-05 15:59:32-07:00, mochel@osdl.org Attempt to better locking in device model core:
- remove device from driver's list on device_detach - set device's driver to NULL - decrement reference count on driver on device_detach - remove devices from driver's list in driver_detach - use a write_lock instead of read_lock - don't lock around initialization of device fields
- assume we have a bus in __remove_driver (we enforce this in driver_register) - do put_bus last in __remove_driver - lock bus around atomic_set in remove_driver and atomic_dec_and_test in put_driver - remove from bus's list while we have it locked
drivers/base/bus.c | 5 +++-- drivers/base/core.c | 30 +++++++++++++++++++++--------- drivers/base/driver.c | 29 +++++++++++++---------------- 3 files changed, 37 insertions, 27 deletions
diff -Nru a/drivers/base/bus.c b/drivers/base/bus.c --- a/drivers/base/bus.c Wed Jun 5 16:02:11 2002 +++ b/drivers/base/bus.c Wed Jun 5 16:02:11 2002 @@ -167,12 +167,13 @@ int bus_register(struct bus_type * bus) { - spin_lock(&device_lock); rwlock_init(&bus->lock); INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->drivers); - list_add_tail(&bus->node,&bus_driver_list); atomic_set(&bus->refcount,2); + + spin_lock(&device_lock); + list_add_tail(&bus->node,&bus_driver_list); spin_unlock(&device_lock); pr_debug("bus type '%s' registered\n",bus->name); diff -Nru a/drivers/base/core.c b/drivers/base/core.c --- a/drivers/base/core.c Wed Jun 5 16:02:11 2002 +++ b/drivers/base/core.c Wed Jun 5 16:02:11 2002 @@ -98,9 +98,20 @@ static void device_detach(struct device * dev) { - /* detach from driver */ - if (dev->driver && dev->driver->remove) - dev->driver->remove(dev); + if (dev->driver) { + write_lock(&dev->driver->lock); + list_del_init(&dev->driver_list); + write_unlock(&dev->driver->lock); + + lock_device(dev); + dev->driver = NULL; + unlock_device(dev); + + /* detach from driver */ + if (dev->driver->remove) + dev->driver->remove(dev); + put_driver(dev->driver); + } } static int do_driver_attach(struct device * dev, void * data) @@ -140,12 +151,13 @@ struct list_head * node; int error = 0; - read_lock(&drv->lock); + write_lock(&drv->lock); node = drv->devices.next; while (node != &drv->devices) { next = list_entry(node,struct device,driver_list); get_device(next); - read_unlock(&drv->lock); + list_del_init(&next->driver_list); + write_unlock(&drv->lock); if (dev) put_device(dev); @@ -154,10 +166,10 @@ put_device(dev); break; } - read_lock(&drv->lock); - node = dev->driver_list.next; + write_lock(&drv->lock); + node = drv->devices.next; } - read_unlock(&drv->lock); + write_unlock(&drv->lock); if (dev) put_device(dev); } @@ -181,13 +193,13 @@ if (!dev || !strlen(dev->bus_id)) return -EINVAL; - spin_lock(&device_lock); INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->g_list); spin_lock_init(&dev->lock); atomic_set(&dev->refcount,2); + spin_lock(&device_lock); if (dev != &device_root) { if (!dev->parent) dev->parent = &device_root; diff -Nru a/drivers/base/driver.c b/drivers/base/driver.c --- a/drivers/base/driver.c Wed Jun 5 16:02:11 2002 +++ b/drivers/base/driver.c Wed Jun 5 16:02:11 2002 @@ -81,26 +81,20 @@ static void __remove_driver(struct device_driver * drv) { - if (drv->bus) { - pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name); - - driver_detach(drv); - write_lock(&drv->bus->lock); - list_del_init(&drv->bus_list); - write_unlock(&drv->bus->lock); - - driverfs_remove_dir(&drv->dir); - put_bus(drv->bus); - } + pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name); + driver_detach(drv); + driverfs_remove_dir(&drv->dir); if (drv->release) drv->release(drv); + put_bus(drv->bus); } void remove_driver(struct device_driver * drv) { - spin_lock(&device_lock); + write_lock(&drv->bus->lock); atomic_set(&drv->refcount,0); - spin_unlock(&device_lock); + list_del_init(&drv->bus_list); + write_unlock(&drv->bus->lock); __remove_driver(drv); } @@ -110,10 +104,13 @@ */ void put_driver(struct device_driver * drv) { - if (!atomic_dec_and_lock(&drv->refcount,&device_lock)) + write_lock(&drv->bus->lock); + if (!atomic_dec_and_test(&drv->refcount)) { + write_unlock(&drv->bus->lock); return; - spin_unlock(&device_lock); - + } + list_del_init(&drv->bus_list); + write_unlock(&drv->bus->lock); __remove_driver(drv); }
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |