]> git.neil.brown.name Git - history.git/commitdiff
Attempt to better locking in device model core:
authorPatrick Mochel <mochel@osdl.org>
Wed, 5 Jun 2002 08:59:32 +0000 (01:59 -0700)
committerPatrick Mochel <mochel@osdl.org>
Wed, 5 Jun 2002 08:59:32 +0000 (01:59 -0700)
- 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
drivers/base/core.c
drivers/base/driver.c

index 6b240c111a84bec1f1c8d7184672c0cfbc1f3079..95d41d8969112984de41fdef01b1040e4142a673 100644 (file)
@@ -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);
index 01887cd6942e190f0cf86dab71f05eaa143c6b4b..4b751c78228cbb1520d60441648071c442431bda 100644 (file)
@@ -98,9 +98,20 @@ static int device_attach(struct device * dev)
 
 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 @@ 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;
index d3a4a55962996b2ff3b098f2aa1a92997a738cbd..2c9d43c05bc0ed7a73e3e5843ce5ab71ab895c6b 100644 (file)
@@ -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);
-       }
+       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 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);
 }