From 473c71058e2bfdbe091652b9a82572aa621bfffa Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 20:32:25 -0700 Subject: driver model: make driverfs an implicit initcall. --- include/linux/driverfs_fs.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/driverfs_fs.h b/include/linux/driverfs_fs.h index d859f8c2e041..b4270e947a1e 100644 --- a/include/linux/driverfs_fs.h +++ b/include/linux/driverfs_fs.h @@ -65,6 +65,4 @@ driverfs_create_symlink(struct driver_dir_entry * parent, extern void driverfs_remove_file(struct driver_dir_entry *, const char * name); -extern int init_driverfs_fs(void); - #endif /* _DDFS_H_ */ -- cgit v1.2.3 From 4278bccc2d9707c9c4ee1769b464bffe4dfd820a Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 20:54:20 -0700 Subject: driver model: change bus refcounting scheme to match devices'. Based on recent changes to device refcounting/unregistration. struct bus_type gets a ->present field, which is set when the bus is registered. bus_unregister() is added, which only clears ->present and calls put_bus(). get_bus() checks the ->present and returns NULL if clear. This allows all current references to buses to keep valid structures, while preventing any new references to the bus structure. --- drivers/base/bus.c | 36 +++++++++++++++++++++++++++++++----- include/linux/device.h | 10 +++------- 2 files changed, 34 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7daffbfd9913..1343c64c5a38 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -136,12 +136,35 @@ void bus_remove_device(struct device * dev) } } +struct bus_type * get_bus(struct bus_type * bus) +{ + struct bus_type * ret = bus; + spin_lock(&device_lock); + if (bus && bus->present && atomic_read(&bus->refcount)) + atomic_inc(&bus->refcount); + else + ret = NULL; + spin_unlock(&device_lock); + return ret; +} + +void put_bus(struct bus_type * bus) +{ + if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) + return; + list_del_init(&bus->node); + spin_unlock(&device_lock); + BUG_ON(bus->present); + bus_remove_dir(bus); +} + int bus_register(struct bus_type * bus) { rwlock_init(&bus->lock); INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->drivers); atomic_set(&bus->refcount,2); + bus->present = 1; spin_lock(&device_lock); list_add_tail(&bus->node,&bus_driver_list); @@ -156,13 +179,14 @@ int bus_register(struct bus_type * bus) return 0; } -void put_bus(struct bus_type * bus) +void bus_unregister(struct bus_type * bus) { - if (!atomic_dec_and_lock(&bus->refcount,&device_lock)) - return; - list_del_init(&bus->node); + spin_lock(&device_lock); + bus->present = 0; spin_unlock(&device_lock); - bus_remove_dir(bus); + + pr_debug("bus %s: unregistering\n",bus->name); + put_bus(bus); } EXPORT_SYMBOL(bus_for_each_dev); @@ -170,4 +194,6 @@ EXPORT_SYMBOL(bus_for_each_drv); EXPORT_SYMBOL(bus_add_device); EXPORT_SYMBOL(bus_remove_device); EXPORT_SYMBOL(bus_register); +EXPORT_SYMBOL(bus_unregister); +EXPORT_SYMBOL(get_bus); EXPORT_SYMBOL(put_bus); diff --git a/include/linux/device.h b/include/linux/device.h index 80a63939f924..03c7d995e96c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -56,6 +56,7 @@ struct bus_type { char * name; rwlock_t lock; atomic_t refcount; + u32 present; struct list_head node; struct list_head devices; @@ -73,14 +74,9 @@ struct bus_type { extern int bus_register(struct bus_type * bus); +extern void bus_unregister(struct bus_type * bus); -static inline struct bus_type * get_bus(struct bus_type * bus) -{ - BUG_ON(!atomic_read(&bus->refcount)); - atomic_inc(&bus->refcount); - return bus; -} - +extern struct bus_type * get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); extern int bus_for_each_dev(struct bus_type * bus, void * data, -- cgit v1.2.3 From 2884fae053fb6a58f4f7c5985d574532b7ddd3d2 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 21:11:26 -0700 Subject: driver model: make driver refcounting similar to devices'. In the spirit of devices and buses, change driver refcounting model to match the way that devices and buses are done. struct device_driver gets a ->present field, which is set on registration and cleared in driver_unregister(). get_device() checks the state of this flag and returns NULL if cleared. Note that the horribly wrong remove_driver() is deprecated and simply BUG()s when called. Please convert callers to use driver_unregister(). Updates to callers will be coming soon. Note also that this still doesn't fix the race in which a driver module can be removed while the refcount on a driver > 1. Near future work should help to remedy it, but no solutions are guaranteed.. --- drivers/base/driver.c | 74 ++++++++++++++++++++++++++++++-------------------- include/linux/device.h | 11 ++------ 2 files changed, 48 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 221e525736bd..b19b06201dd5 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -39,6 +39,43 @@ int driver_for_each_dev(struct device_driver * drv, void * data, return error; } +struct device_driver * get_driver(struct device_driver * drv) +{ + struct device_driver * ret = drv; + spin_lock(&device_lock); + if (drv && drv->present && atomic_read(&drv->refcount) > 0) + atomic_inc(&drv->refcount); + else + ret = NULL; + spin_unlock(&device_lock); + return ret; +} + + +void remove_driver(struct device_driver * drv) +{ + BUG(); +} + +/** + * put_driver - decrement driver's refcount and clean up if necessary + * @drv: driver in question + */ +void put_driver(struct device_driver * drv) +{ + struct bus_type * bus = drv->bus; + if (!atomic_dec_and_lock(&drv->refcount,&device_lock)) + return; + list_del_init(&drv->bus_list); + spin_unlock(&device_lock); + BUG_ON(drv->present); + driver_detach(drv); + driver_remove_dir(drv); + if (drv->release) + drv->release(drv); + put_bus(bus); +} + /** * driver_register - register driver with bus * @drv: driver to register @@ -50,12 +87,13 @@ int driver_register(struct device_driver * drv) if (!drv->bus) return -EINVAL; - pr_debug("Registering driver '%s' with bus '%s'\n",drv->name,drv->bus->name); + pr_debug("driver %s:%s: registering\n",drv->bus->name,drv->name); get_bus(drv->bus); atomic_set(&drv->refcount,2); rwlock_init(&drv->lock); INIT_LIST_HEAD(&drv->devices); + drv->present = 1; spin_lock(&device_lock); list_add(&drv->bus_list,&drv->bus->drivers); spin_unlock(&device_lock); @@ -65,39 +103,17 @@ int driver_register(struct device_driver * drv) return 0; } -static void __remove_driver(struct device_driver * drv) -{ - pr_debug("Unregistering driver '%s' from bus '%s'\n",drv->name,drv->bus->name); - driver_detach(drv); - driver_remove_dir(drv); - if (drv->release) - drv->release(drv); - put_bus(drv->bus); -} - -void remove_driver(struct device_driver * drv) +void driver_unregister(struct device_driver * drv) { spin_lock(&device_lock); - atomic_set(&drv->refcount,0); - list_del_init(&drv->bus_list); - spin_unlock(&device_lock); - __remove_driver(drv); -} - -/** - * put_driver - decrement driver's refcount and clean up if necessary - * @drv: driver in question - */ -void put_driver(struct device_driver * drv) -{ - if (!atomic_dec_and_lock(&drv->refcount,&device_lock)) - return; - list_del_init(&drv->bus_list); + drv->present = 0; spin_unlock(&device_lock); - __remove_driver(drv); + pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name); + put_driver(drv); } EXPORT_SYMBOL(driver_for_each_dev); EXPORT_SYMBOL(driver_register); +EXPORT_SYMBOL(driver_unregister); +EXPORT_SYMBOL(get_driver); EXPORT_SYMBOL(put_driver); -EXPORT_SYMBOL(remove_driver); diff --git a/include/linux/device.h b/include/linux/device.h index 03c7d995e96c..4b6b0cbe77ed 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -110,6 +110,7 @@ struct device_driver { rwlock_t lock; atomic_t refcount; + u32 present; struct list_head bus_list; struct list_head class_list; @@ -127,16 +128,10 @@ struct device_driver { }; - extern int driver_register(struct device_driver * drv); +extern void driver_unregister(struct device_driver * drv); -static inline struct device_driver * get_driver(struct device_driver * drv) -{ - BUG_ON(!atomic_read(&drv->refcount)); - atomic_inc(&drv->refcount); - return drv; -} - +extern struct device_driver * get_driver(struct device_driver * drv); extern void put_driver(struct device_driver * drv); extern void remove_driver(struct device_driver * drv); -- cgit v1.2.3 From 1067efac8c64c2608ececdca87e1329ba3ffa073 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 15 Oct 2002 21:26:48 -0700 Subject: driver model: change class reference counting to be like devices'. device classes join the club of devices, buses, and drivers. They get a ->present flag, which is set on registration and cleared on unregistration. They also get get_devclass() and put_devclass(), which, you guessed it, bump the reference count. get_...() of course checks the present flag and returns NULL if clear. --- drivers/base/class.c | 36 +++++++++++++++++++++++++++++++----- include/linux/device.h | 6 ++++++ 2 files changed, 37 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/base/class.c b/drivers/base/class.c index dfef9793871a..3c7024cc3efb 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -81,29 +81,55 @@ void devclass_remove_device(struct device * dev) } } +struct device_class * get_devclass(struct device_class * cls) +{ + struct device_class * ret = cls; + spin_lock(&device_lock); + if (cls && cls->present && atomic_read(&cls->refcount) > 0) + atomic_inc(&cls->refcount); + else + ret = NULL; + spin_unlock(&device_lock); + return ret; +} + +void put_devclass(struct device_class * cls) +{ + if (atomic_dec_and_lock(&cls->refcount,&device_lock)) { + list_del_init(&cls->node); + spin_unlock(&device_lock); + devclass_remove_dir(cls); + } +} + + int devclass_register(struct device_class * cls) { INIT_LIST_HEAD(&cls->drivers); INIT_LIST_HEAD(&cls->intf_list); - - pr_debug("registering device class '%s'\n",cls->name); + atomic_set(&cls->refcount,2); + cls->present = 1; + pr_debug("device class '%s': registering\n",cls->name); spin_lock(&device_lock); list_add_tail(&cls->node,&class_list); spin_unlock(&device_lock); devclass_make_dir(cls); + put_devclass(cls); return 0; } void devclass_unregister(struct device_class * cls) { - pr_debug("unregistering device class '%s'\n",cls->name); - devclass_remove_dir(cls); spin_lock(&device_lock); - list_del_init(&class_list); + cls->present = 0; spin_unlock(&device_lock); + pr_debug("device class '%s': unregistering\n",cls->name); + put_devclass(cls); } EXPORT_SYMBOL(devclass_register); EXPORT_SYMBOL(devclass_unregister); +EXPORT_SYMBOL(get_devclass); +EXPORT_SYMBOL(put_devclass); diff --git a/include/linux/device.h b/include/linux/device.h index 4b6b0cbe77ed..112fca2dd2d8 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -163,6 +163,9 @@ extern void driver_remove_file(struct device_driver *, struct driver_attribute * */ struct device_class { char * name; + atomic_t refcount; + u32 present; + u32 devnum; struct list_head node; @@ -180,6 +183,9 @@ struct device_class { extern int devclass_register(struct device_class *); extern void devclass_unregister(struct device_class *); +extern struct device_class * get_devclass(struct device_class *); +extern void put_devclass(struct device_class *); + struct devclass_attribute { struct attribute attr; -- cgit v1.2.3 From ba809e8ac59030f8b8c1822f0ce6b3161cabc60b Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Wed, 16 Oct 2002 01:28:28 -0700 Subject: driver model: replace rwlock in struct bus_type with a rwsem. Synchronize all walks of the device and driver lists of a bus with an rwsem wrapped around the entire iterator, instead of using device_lock and dropping it after we grabbed each node. Note this also prevents deadlock when walking the list of drivers and calling get_driver(), since get_driver() tries to take device_lock while we already have it held. --- drivers/base/bus.c | 24 +++++++++++------------- drivers/base/core.c | 1 - include/linux/device.h | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 1343c64c5a38..559956c29763 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -47,23 +47,21 @@ int bus_for_each_dev(struct bus_type * bus, void * data, int error = 0; get_bus(bus); - spin_lock(&device_lock); + down_write(&bus->rwsem); list_for_each(node,&bus->devices) { - struct device * dev = get_device_locked(to_dev(node)); + struct device * dev = get_device(to_dev(node)); if (dev) { - spin_unlock(&device_lock); error = callback(dev,data); if (prev) put_device(prev); prev = dev; - spin_lock(&device_lock); if (error) break; } } - spin_unlock(&device_lock); if (prev) put_device(prev); + up_write(&bus->rwsem); put_bus(bus); return error; } @@ -77,24 +75,21 @@ int bus_for_each_drv(struct bus_type * bus, void * data, /* pin bus in memory */ get_bus(bus); - - spin_lock(&device_lock); + down_write(&bus->rwsem); list_for_each(node,&bus->drivers) { struct device_driver * drv = get_driver(to_drv(node)); if (drv) { - spin_unlock(&device_lock); error = callback(drv,data); if (prev) put_driver(prev); prev = drv; - spin_lock(&device_lock); if (error) break; } } - spin_unlock(&device_lock); if (prev) put_driver(prev); + up_write(&bus->rwsem); put_bus(bus); return error; } @@ -111,11 +106,11 @@ int bus_for_each_drv(struct bus_type * bus, void * data, int bus_add_device(struct device * dev) { if (dev->bus) { + down_write(&dev->bus->rwsem); pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name); get_bus(dev->bus); - spin_lock(&device_lock); list_add_tail(&dev->bus_list,&dev->bus->devices); - spin_unlock(&device_lock); + up_write(&dev->bus->rwsem); device_bus_link(dev); } return 0; @@ -131,7 +126,10 @@ int bus_add_device(struct device * dev) void bus_remove_device(struct device * dev) { if (dev->bus) { + down_write(&dev->bus->rwsem); + list_del_init(&dev->bus_list); device_remove_symlink(&dev->bus->device_dir,dev->bus_id); + up_write(&dev->bus->rwsem); put_bus(dev->bus); } } @@ -160,7 +158,7 @@ void put_bus(struct bus_type * bus) int bus_register(struct bus_type * bus) { - rwlock_init(&bus->lock); + init_rwsem(&bus->rwsem); INIT_LIST_HEAD(&bus->devices); INIT_LIST_HEAD(&bus->drivers); atomic_set(&bus->refcount,2); diff --git a/drivers/base/core.c b/drivers/base/core.c index b6f2104ae038..a1a41f4b0a07 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -263,7 +263,6 @@ void put_device(struct device * dev) return; list_del_init(&dev->node); list_del_init(&dev->g_list); - list_del_init(&dev->bus_list); list_del_init(&dev->driver_list); spin_unlock(&device_lock); diff --git a/include/linux/device.h b/include/linux/device.h index 112fca2dd2d8..c0f8d6c5e4bf 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -54,7 +54,7 @@ struct device_class; struct bus_type { char * name; - rwlock_t lock; + struct rw_semaphore rwsem; atomic_t refcount; u32 present; -- cgit v1.2.3