From aac136f255575f8bdfb97b63d27525bcc5620938 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 8 Mar 2005 17:32:38 -0800 Subject: [PATCH] class core: export MAJOR/MINOR to the hotplug env Move the creation of the sysfs "dev" file of a class device into the driver core. The struct class_device contains a dev_t value now. If set, the driver core will create the "dev" file containing the major/minor numbers automatically. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- include/linux/device.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/device.h b/include/linux/device.h index 786e1591102b..acec2a761ea0 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -184,6 +184,7 @@ struct class_device { struct kobject kobj; struct class * class; /* required */ + dev_t devt; /* dev_t, creates the sysfs "dev" */ struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ -- cgit v1.2.3 From db36587d7fea5d9fba0e85a10b82ef134db81aab Mon Sep 17 00:00:00 2001 From: Mike Waychison Date: Tue, 8 Mar 2005 17:52:29 -0800 Subject: [PATCH] driver core: clean driver unload Get rid of semaphore abuse by converting device_driver->unload_sem semaphore to device_driver->unloaded completion. This should get rid of any confusion as well as save a few bytes in the process. Signed-off-by: Mike Waychison Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 2 +- drivers/base/driver.c | 13 ++++++------- include/linux/device.h | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 3da24059bdf1..d04fbbdc6298 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -65,7 +65,7 @@ static struct sysfs_ops driver_sysfs_ops = { static void driver_release(struct kobject * kobj) { struct device_driver * drv = to_driver(kobj); - up(&drv->unload_sem); + complete(&drv->unloaded); } static struct kobj_type ktype_driver = { diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 9556f912f960..3b269f7e5213 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -79,14 +79,14 @@ void put_driver(struct device_driver * drv) * since most of the things we have to do deal with the bus * structures. * - * The one interesting aspect is that we initialize @drv->unload_sem - * to a locked state here. It will be unlocked when the driver - * reference count reaches 0. + * The one interesting aspect is that we setup @drv->unloaded + * as a completion that gets complete when the driver reference + * count reaches 0. */ int driver_register(struct device_driver * drv) { INIT_LIST_HEAD(&drv->devices); - init_MUTEX_LOCKED(&drv->unload_sem); + init_completion(&drv->unloaded); return bus_add_driver(drv); } @@ -97,7 +97,7 @@ int driver_register(struct device_driver * drv) * * Again, we pass off most of the work to the bus-level call. * - * Though, once that is done, we attempt to take @drv->unload_sem. + * Though, once that is done, we wait until @drv->unloaded is completed. * This will block until the driver refcount reaches 0, and it is * released. Only modular drivers will call this function, and we * have to guarantee that it won't complete, letting the driver @@ -107,8 +107,7 @@ int driver_register(struct device_driver * drv) void driver_unregister(struct device_driver * drv) { bus_remove_driver(drv); - down(&drv->unload_sem); - up(&drv->unload_sem); + wait_for_completion(&drv->unloaded); } /** diff --git a/include/linux/device.h b/include/linux/device.h index acec2a761ea0..e98232688899 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -102,7 +102,7 @@ struct device_driver { char * name; struct bus_type * bus; - struct semaphore unload_sem; + struct completion unloaded; struct kobject kobj; struct list_head devices; -- cgit v1.2.3 From 181dfb4cef2cf62ea71bdc17a52073f6f02d5bfe Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 8 Mar 2005 17:53:28 -0800 Subject: [PATCH] kset: make ksets have a spinlock, and use that to lock their lists Do this instead of using the rwsem of a subsys. Smaller, faster, and I'm trying to get rid of the rwsem in the subsys. Signed-off-by: Greg Kroah-Hartman --- include/linux/kobject.h | 2 ++ lib/kobject.c | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kobject.h b/include/linux/kobject.h index dc773d4043e8..765d660d3bea 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -102,6 +103,7 @@ struct kset { struct subsystem * subsys; struct kobj_type * ktype; struct list_head list; + spinlock_t list_lock; struct kobject kobj; struct kset_hotplug_ops * hotplug_ops; }; diff --git a/lib/kobject.c b/lib/kobject.c index e1282b3576b6..ff9491986b38 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -140,9 +140,9 @@ void kobject_init(struct kobject * kobj) static void unlink(struct kobject * kobj) { if (kobj->kset) { - down_write(&kobj->kset->subsys->rwsem); + spin_lock(&kobj->kset->list_lock); list_del_init(&kobj->entry); - up_write(&kobj->kset->subsys->rwsem); + spin_unlock(&kobj->kset->list_lock); } kobject_put(kobj); } @@ -168,13 +168,13 @@ int kobject_add(struct kobject * kobj) kobj->kset ? kobj->kset->kobj.name : "" ); if (kobj->kset) { - down_write(&kobj->kset->subsys->rwsem); + spin_lock(&kobj->kset->list_lock); if (!parent) parent = kobject_get(&kobj->kset->kobj); list_add_tail(&kobj->entry,&kobj->kset->list); - up_write(&kobj->kset->subsys->rwsem); + spin_unlock(&kobj->kset->list_lock); } kobj->parent = parent; @@ -380,6 +380,7 @@ void kset_init(struct kset * k) { kobject_init(&k->kobj); INIT_LIST_HEAD(&k->list); + spin_lock_init(&k->list_lock); } @@ -444,7 +445,7 @@ struct kobject * kset_find_obj(struct kset * kset, const char * name) struct list_head * entry; struct kobject * ret = NULL; - down_read(&kset->subsys->rwsem); + spin_lock(&kset->list_lock); list_for_each(entry,&kset->list) { struct kobject * k = to_kobj(entry); if (kobject_name(k) && !strcmp(kobject_name(k),name)) { @@ -452,7 +453,7 @@ struct kobject * kset_find_obj(struct kset * kset, const char * name) break; } } - up_read(&kset->subsys->rwsem); + spin_unlock(&kset->list_lock); return ret; } -- cgit v1.2.3 From dc01d0035a3efd4e12b4aabf7eaae4d69a263978 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 8 Mar 2005 18:00:20 -0800 Subject: [PATCH] kref: make kref_put return if this was the last put call. This is needed for the upcoming klist code from Pat. Signed-off-by: Greg Kroah-Hartman --- include/linux/kref.h | 2 +- lib/kref.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kref.h b/include/linux/kref.h index ea5948785ebf..6fee3539893f 100644 --- a/include/linux/kref.h +++ b/include/linux/kref.h @@ -26,7 +26,7 @@ struct kref { void kref_init(struct kref *kref); void kref_get(struct kref *kref); -void kref_put(struct kref *kref, void (*release) (struct kref *kref)); +int kref_put(struct kref *kref, void (*release) (struct kref *kref)); #endif /* __KERNEL__ */ #endif /* _KREF_H_ */ diff --git a/lib/kref.c b/lib/kref.c index 2218b7ae7db6..0d07cc31c818 100644 --- a/lib/kref.c +++ b/lib/kref.c @@ -42,14 +42,21 @@ void kref_get(struct kref *kref) * in as this function. * * Decrement the refcount, and if 0, call release(). + * Return 1 if the object was removed, otherwise return 0. Beware, if this + * function returns 0, you still can not count on the kref from remaining in + * memory. Only use the return value if you want to see if the kref is now + * gone, not present. */ -void kref_put(struct kref *kref, void (*release) (struct kref *kref)) +int kref_put(struct kref *kref, void (*release)(struct kref *kref)) { WARN_ON(release == NULL); WARN_ON(release == (void (*)(struct kref *))kfree); - if (atomic_dec_and_test(&kref->refcount)) + if (atomic_dec_and_test(&kref->refcount)) { release(kref); + return 1; + } + return 0; } EXPORT_SYMBOL(kref_init); -- cgit v1.2.3 From 66928d6cf6bc7ae31f2c465c570a23fe3b1a1a31 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 8 Mar 2005 23:06:02 -0800 Subject: [PATCH] kmap: remove usage of rwsem from kobj_map. This forces the caller to provide the lock, but as they all already had one, it's not a big change. It also removes the now-unneeded cdev_subsys. Thanks to Jon Corbet for reminding me about that. Signed-off-by: Greg Kroah-Hartman --- drivers/base/map.c | 21 ++++++++++----------- drivers/block/genhd.c | 2 +- fs/char_dev.c | 26 +++++++++----------------- include/linux/kobj_map.h | 2 +- 4 files changed, 21 insertions(+), 30 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/map.c b/drivers/base/map.c index aab93fc2a31b..2f455d86793c 100644 --- a/drivers/base/map.c +++ b/drivers/base/map.c @@ -25,7 +25,7 @@ struct kobj_map { int (*lock)(dev_t, void *); void *data; } *probes[255]; - struct rw_semaphore *sem; + struct semaphore *sem; }; int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, @@ -53,7 +53,7 @@ int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, p->range = range; p->data = data; } - down_write(domain->sem); + down(domain->sem); for (i = 0, p -= n; i < n; i++, p++, index++) { struct probe **s = &domain->probes[index % 255]; while (*s && (*s)->range < range) @@ -61,7 +61,7 @@ int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range, p->next = *s; *s = p; } - up_write(domain->sem); + up(domain->sem); return 0; } @@ -75,7 +75,7 @@ void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range) if (n > 255) n = 255; - down_write(domain->sem); + down(domain->sem); for (i = 0; i < n; i++, index++) { struct probe **s; for (s = &domain->probes[index % 255]; *s; s = &(*s)->next) { @@ -88,7 +88,7 @@ void kobj_unmap(struct kobj_map *domain, dev_t dev, unsigned long range) } } } - up_write(domain->sem); + up(domain->sem); kfree(found); } @@ -99,7 +99,7 @@ struct kobject *kobj_lookup(struct kobj_map *domain, dev_t dev, int *index) unsigned long best = ~0UL; retry: - down_read(domain->sem); + down(domain->sem); for (p = domain->probes[MAJOR(dev) % 255]; p; p = p->next) { struct kobject *(*probe)(dev_t, int *, void *); struct module *owner; @@ -120,7 +120,7 @@ retry: module_put(owner); continue; } - up_read(domain->sem); + up(domain->sem); kobj = probe(dev, index, data); /* Currently ->owner protects _only_ ->probe() itself. */ module_put(owner); @@ -128,12 +128,11 @@ retry: return kobj; goto retry; } - up_read(domain->sem); + up(domain->sem); return NULL; } -struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, - struct subsystem *s) +struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, struct semaphore *sem) { struct kobj_map *p = kmalloc(sizeof(struct kobj_map), GFP_KERNEL); struct probe *base = kmalloc(sizeof(struct probe), GFP_KERNEL); @@ -151,6 +150,6 @@ struct kobj_map *kobj_map_init(kobj_probe_t *base_probe, base->get = base_probe; for (i = 0; i < 255; i++) p->probes[i] = base; - p->sem = &s->rwsem; + p->sem = sem; return p; } diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 1b7217b37e76..ab4db71375e0 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -302,7 +302,7 @@ static struct kobject *base_probe(dev_t dev, int *part, void *data) static int __init genhd_device_init(void) { - bdev_map = kobj_map_init(base_probe, &block_subsys); + bdev_map = kobj_map_init(base_probe, &block_subsys_sem); blk_dev_init(); subsystem_register(&block_subsys); return 0; diff --git a/fs/char_dev.c b/fs/char_dev.c index bda65c2505a3..7357a9127df1 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -29,7 +29,7 @@ static struct kobj_map *cdev_map; /* degrade to linked list for small systems */ #define MAX_PROBE_HASH (CONFIG_BASE_SMALL ? 1 : 255) -static DEFINE_RWLOCK(chrdevs_lock); +static DECLARE_MUTEX(chrdevs_lock); static struct char_device_struct { struct char_device_struct *next; @@ -55,13 +55,13 @@ int get_chrdev_list(char *page) len = sprintf(page, "Character devices:\n"); - read_lock(&chrdevs_lock); + down(&chrdevs_lock); for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) { for (cd = chrdevs[i]; cd; cd = cd->next) len += sprintf(page+len, "%3d %s\n", cd->major, cd->name); } - read_unlock(&chrdevs_lock); + up(&chrdevs_lock); return len; } @@ -91,7 +91,7 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, memset(cd, 0, sizeof(struct char_device_struct)); - write_lock_irq(&chrdevs_lock); + down(&chrdevs_lock); /* temporary */ if (major == 0) { @@ -126,10 +126,10 @@ __register_chrdev_region(unsigned int major, unsigned int baseminor, } cd->next = *cp; *cp = cd; - write_unlock_irq(&chrdevs_lock); + up(&chrdevs_lock); return cd; out: - write_unlock_irq(&chrdevs_lock); + up(&chrdevs_lock); kfree(cd); return ERR_PTR(ret); } @@ -140,7 +140,7 @@ __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) struct char_device_struct *cd = NULL, **cp; int i = major_to_index(major); - write_lock_irq(&chrdevs_lock); + up(&chrdevs_lock); for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next) if ((*cp)->major == major && (*cp)->baseminor == baseminor && @@ -150,7 +150,7 @@ __unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct) cd = *cp; *cp = cd->next; } - write_unlock_irq(&chrdevs_lock); + up(&chrdevs_lock); return cd; } @@ -381,8 +381,6 @@ void cdev_del(struct cdev *p) } -static decl_subsys(cdev, NULL, NULL); - static void cdev_default_release(struct kobject *kobj) { struct cdev *p = container_of(kobj, struct cdev, kobj); @@ -435,13 +433,7 @@ static struct kobject *base_probe(dev_t dev, int *part, void *data) void __init chrdev_init(void) { -/* - * Keep cdev_subsys around because (and only because) the kobj_map code - * depends on the rwsem it contains. We don't make it public in sysfs, - * however. - */ - subsystem_init(&cdev_subsys); - cdev_map = kobj_map_init(base_probe, &cdev_subsys); + cdev_map = kobj_map_init(base_probe, &chrdevs_lock); } diff --git a/include/linux/kobj_map.h b/include/linux/kobj_map.h index 404a945c1533..b6cc10bf8dfc 100644 --- a/include/linux/kobj_map.h +++ b/include/linux/kobj_map.h @@ -7,6 +7,6 @@ int kobj_map(struct kobj_map *, dev_t, unsigned long, struct module *, kobj_probe_t *, int (*)(dev_t, void *), void *); void kobj_unmap(struct kobj_map *, dev_t, unsigned long); struct kobject *kobj_lookup(struct kobj_map *, dev_t, int *); -struct kobj_map *kobj_map_init(kobj_probe_t *, struct subsystem *); +struct kobj_map *kobj_map_init(kobj_probe_t *, struct semaphore *); #endif -- cgit v1.2.3 From 0c156ba8ede6d99bd1738d7d987d9d58f65d0aa9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 8 Mar 2005 23:41:29 -0800 Subject: [PATCH] class: add a semaphore to struct class, and use that instead of the subsystem rwsem. This moves us away from using the rwsem, although recursive adds and removes of class devices is not yet possible (nor is it really known if it even is needed.) So this simple change is done instead. Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 23 +++++++++++------------ include/linux/device.h | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/class.c b/drivers/base/class.c index b995a854adce..6bf650fce78c 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -140,6 +140,7 @@ int class_register(struct class * cls) INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->interfaces); + init_MUTEX(&cls->sem); error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); if (error) return error; @@ -413,12 +414,12 @@ int class_device_add(struct class_device *class_dev) /* now take care of our own registration */ if (parent) { - down_write(&parent->subsys.rwsem); + down(&parent->sem); list_add_tail(&class_dev->node, &parent->children); list_for_each_entry(class_intf, &parent->interfaces, node) if (class_intf->add) class_intf->add(class_dev); - up_write(&parent->subsys.rwsem); + up(&parent->sem); } if (MAJOR(class_dev->devt)) @@ -448,12 +449,12 @@ void class_device_del(struct class_device *class_dev) struct class_interface * class_intf; if (parent) { - down_write(&parent->subsys.rwsem); + down(&parent->sem); list_del_init(&class_dev->node); list_for_each_entry(class_intf, &parent->interfaces, node) if (class_intf->remove) class_intf->remove(class_dev); - up_write(&parent->subsys.rwsem); + up(&parent->sem); } if (class_dev->dev) @@ -509,8 +510,8 @@ void class_device_put(struct class_device *class_dev) int class_interface_register(struct class_interface *class_intf) { - struct class * parent; - struct class_device * class_dev; + struct class *parent; + struct class_device *class_dev; if (!class_intf || !class_intf->class) return -ENODEV; @@ -519,14 +520,13 @@ int class_interface_register(struct class_interface *class_intf) if (!parent) return -EINVAL; - down_write(&parent->subsys.rwsem); + down(&parent->sem); list_add_tail(&class_intf->node, &parent->interfaces); - if (class_intf->add) { list_for_each_entry(class_dev, &parent->children, node) class_intf->add(class_dev); } - up_write(&parent->subsys.rwsem); + up(&parent->sem); return 0; } @@ -539,14 +539,13 @@ void class_interface_unregister(struct class_interface *class_intf) if (!parent) return; - down_write(&parent->subsys.rwsem); + down(&parent->sem); list_del_init(&class_intf->node); - if (class_intf->remove) { list_for_each_entry(class_dev, &parent->children, node) class_intf->remove(class_dev); } - up_write(&parent->subsys.rwsem); + up(&parent->sem); class_put(parent); } diff --git a/include/linux/device.h b/include/linux/device.h index e98232688899..a33424d41038 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -148,6 +147,7 @@ struct class { struct subsystem subsys; struct list_head children; struct list_head interfaces; + struct semaphore sem; /* locks both the children and interfaces lists */ struct class_attribute * class_attrs; struct class_device_attribute * class_dev_attrs; -- cgit v1.2.3