Commit b1d0e423 authored by Patrick Mochel's avatar Patrick Mochel

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
parent 4b0e0af3
......@@ -167,12 +167,13 @@ static int bus_make_dir(struct bus_type * bus)
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);
......
......@@ -98,9 +98,20 @@ static int device_attach(struct device * dev)
static void device_detach(struct device * 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 && dev->driver->remove)
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 @@ void driver_detach(struct device_driver * drv)
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 @@ void driver_detach(struct device_driver * drv)
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 @@ int device_register(struct device *dev)
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;
......
......@@ -81,26 +81,20 @@ int driver_register(struct device_driver * drv)
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);
}
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 remove_driver(struct device_driver * drv)
*/
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);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment