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) ...@@ -167,12 +167,13 @@ static int bus_make_dir(struct bus_type * bus)
int bus_register(struct bus_type * bus) int bus_register(struct bus_type * bus)
{ {
spin_lock(&device_lock);
rwlock_init(&bus->lock); rwlock_init(&bus->lock);
INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->devices);
INIT_LIST_HEAD(&bus->drivers); INIT_LIST_HEAD(&bus->drivers);
list_add_tail(&bus->node,&bus_driver_list);
atomic_set(&bus->refcount,2); atomic_set(&bus->refcount,2);
spin_lock(&device_lock);
list_add_tail(&bus->node,&bus_driver_list);
spin_unlock(&device_lock); spin_unlock(&device_lock);
pr_debug("bus type '%s' registered\n",bus->name); pr_debug("bus type '%s' registered\n",bus->name);
......
...@@ -98,9 +98,20 @@ static int device_attach(struct device * dev) ...@@ -98,9 +98,20 @@ static int device_attach(struct device * dev)
static void device_detach(struct device * dev) static void device_detach(struct device * dev)
{ {
/* detach from driver */ if (dev->driver) {
if (dev->driver && dev->driver->remove) write_lock(&dev->driver->lock);
dev->driver->remove(dev); 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) static int do_driver_attach(struct device * dev, void * data)
...@@ -140,12 +151,13 @@ void driver_detach(struct device_driver * drv) ...@@ -140,12 +151,13 @@ void driver_detach(struct device_driver * drv)
struct list_head * node; struct list_head * node;
int error = 0; int error = 0;
read_lock(&drv->lock); write_lock(&drv->lock);
node = drv->devices.next; node = drv->devices.next;
while (node != &drv->devices) { while (node != &drv->devices) {
next = list_entry(node,struct device,driver_list); next = list_entry(node,struct device,driver_list);
get_device(next); get_device(next);
read_unlock(&drv->lock); list_del_init(&next->driver_list);
write_unlock(&drv->lock);
if (dev) if (dev)
put_device(dev); put_device(dev);
...@@ -154,10 +166,10 @@ void driver_detach(struct device_driver * drv) ...@@ -154,10 +166,10 @@ void driver_detach(struct device_driver * drv)
put_device(dev); put_device(dev);
break; break;
} }
read_lock(&drv->lock); write_lock(&drv->lock);
node = dev->driver_list.next; node = drv->devices.next;
} }
read_unlock(&drv->lock); write_unlock(&drv->lock);
if (dev) if (dev)
put_device(dev); put_device(dev);
} }
...@@ -181,13 +193,13 @@ int device_register(struct device *dev) ...@@ -181,13 +193,13 @@ int device_register(struct device *dev)
if (!dev || !strlen(dev->bus_id)) if (!dev || !strlen(dev->bus_id))
return -EINVAL; return -EINVAL;
spin_lock(&device_lock);
INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->node);
INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->children);
INIT_LIST_HEAD(&dev->g_list); INIT_LIST_HEAD(&dev->g_list);
spin_lock_init(&dev->lock); spin_lock_init(&dev->lock);
atomic_set(&dev->refcount,2); atomic_set(&dev->refcount,2);
spin_lock(&device_lock);
if (dev != &device_root) { if (dev != &device_root) {
if (!dev->parent) if (!dev->parent)
dev->parent = &device_root; dev->parent = &device_root;
......
...@@ -81,26 +81,20 @@ int driver_register(struct device_driver * drv) ...@@ -81,26 +81,20 @@ int driver_register(struct device_driver * drv)
static void __remove_driver(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);
pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name); driver_detach(drv);
driverfs_remove_dir(&drv->dir);
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) if (drv->release)
drv->release(drv); drv->release(drv);
put_bus(drv->bus);
} }
void remove_driver(struct device_driver * drv) void remove_driver(struct device_driver * drv)
{ {
spin_lock(&device_lock); write_lock(&drv->bus->lock);
atomic_set(&drv->refcount,0); atomic_set(&drv->refcount,0);
spin_unlock(&device_lock); list_del_init(&drv->bus_list);
write_unlock(&drv->bus->lock);
__remove_driver(drv); __remove_driver(drv);
} }
...@@ -110,10 +104,13 @@ void remove_driver(struct device_driver * drv) ...@@ -110,10 +104,13 @@ void remove_driver(struct device_driver * drv)
*/ */
void put_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; return;
spin_unlock(&device_lock); }
list_del_init(&drv->bus_list);
write_unlock(&drv->bus->lock);
__remove_driver(drv); __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