diff options
| author | Patrick Mochel <mochel@osdl.org> | 2002-11-18 22:56:23 -0600 |
|---|---|---|
| committer | Patrick Mochel <mochel@osdl.org> | 2002-11-18 22:56:23 -0600 |
| commit | bb4be78ad606eb5d9dce29ab089b4dc0253886be (patch) | |
| tree | 8fc0974f694d09e561532405c2be41ae84a5abc4 | |
| parent | 65b5db509e62f3d11e15cbf916323ee7f0849888 (diff) | |
| parent | 0d3b7ccf5759e4a8a6dec610bbff7952405b5e7e (diff) | |
Merge osdl.org:/home/mochel/src/kernel/devel/linux-2.5-virgin
into osdl.org:/home/mochel/src/kernel/devel/linux-2.5-kobject
| -rw-r--r-- | Documentation/kobject.txt | 125 | ||||
| -rw-r--r-- | drivers/base/base.h | 3 | ||||
| -rw-r--r-- | drivers/base/bus.c | 2 | ||||
| -rw-r--r-- | drivers/base/core.c | 4 | ||||
| -rw-r--r-- | drivers/base/cpu.c | 4 | ||||
| -rw-r--r-- | drivers/base/driver.c | 6 | ||||
| -rw-r--r-- | drivers/base/fs/device.c | 2 | ||||
| -rw-r--r-- | drivers/base/hotplug.c | 2 | ||||
| -rw-r--r-- | drivers/base/intf.c | 4 | ||||
| -rw-r--r-- | drivers/base/power.c | 2 | ||||
| -rw-r--r-- | drivers/base/sys.c | 2 | ||||
| -rw-r--r-- | fs/sysfs/inode.c | 2 | ||||
| -rw-r--r-- | include/linux/kobject.h | 4 | ||||
| -rw-r--r-- | lib/kobject.c | 154 |
14 files changed, 223 insertions, 93 deletions
diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt index bc4ddfde8158..692467d0255c 100644 --- a/Documentation/kobject.txt +++ b/Documentation/kobject.txt @@ -5,7 +5,9 @@ Patrick Mochel <mochel@osdl.org> 30 October 2002 -kobjects +1. kobjects + +1.1 Description struct kobject introduces a simple, intregral datatype and a simple set of semantics for operating on the device. kobjects are intended to @@ -13,8 +15,8 @@ be embedded in larger data structures and replace fields it duplicates. A set of library functions has been developed to assist in the manipulation of kobjects. -struct kobject looks like this: +1.2 Defintion struct kobject { char name[16]; @@ -26,21 +28,29 @@ struct kobject { }; void kobject_init(struct kobject *); +int kobject_add(struct kobject *); int kobject_register(struct kobject *); + +void kobject_del(struct kobject *); +void kobject_cleanup(struct kobject *); void kobject_unregister(struct kobject *); + struct kobject * kobject_get(struct kobject *); void kobject_put(struct kobject *); -subsystems +2. subsystems + +2.1 Description struct subsystem is introduced to describe a collection of objects of a certain type. subsystems are kobjects themselves, though they contain lists of kobjects that belong to that subsystem. Objects of a subsystem (the embedder objects in which kobjects live) are all of the -same type. The interface looks like: +same type. +2.2 Definition struct subsystem { struct kobject kobj; @@ -60,6 +70,95 @@ struct subsystem * subsys_get(struct subsystem * s); void subsys_put(struct subsystem * s); +3. The Interface + +The kobject API provides a symmeticral interface that may be used in +one of two ways: by using the default front-end registration +interface, or by directly using the backend helpers the registration +interface uses. + +3.1 Default Usage + +The default usage is to use kobjet_register() to add a device to the +object hierarchy, and kobject_unregister() to remove it. + +kobject_register() will call kobject_init() and kobject_add() +consecutively. kobject_init() will initialize the object and increment +the reference count of the subsystem the object belongs to. It will +leave the reference count of the object at 1. + +kobject_add() will insert it into the object hierarchy and create +a sysfs directory for the object. This will increment the reference +count of the object, leaving it at 2. + +kobject_unregister() will call kobject_del() and kobject_put() +consecutively. kobject_del() will remove the object from the hierarchy +and the sysfs directory for the object. It will decrement the +reference count for the object. Assuming there are no other users of +the object, it will be left at 1. + +kobject_put() will decrement the reference count of the object, and +when it reaches 0, call kobject_cleanup(). This will happen +immediately if there are no other users of the object. +kobject_cleanup() will call the subsystem's release() method +for the object, and decrement the subsystem's reference count. + +Because kobject_unregister() calls kobject_put(), instead of +kobject_cleanup() directly, when an object is unregistered, the +pointer to the object is guaranteed to remain valid until the last +reference to the object has gone away. + +Users of objects should call kobject_get() to obtain a reference to +the object that they are using. If the object passed to it is a valid +object (i.e. still present in the system), it will return a pointer to +the object. Otherwise, it will return NULL. + +When users are done using an object, they should call kobject_put() to +decrement the reference count on the object. As explained above, when +the reference count for the object reaches 0, kobject_cleanup() will +be called for the object. + + +3.2 Backend Usage + +Users of the kobject infrastructure may use the backend functions +directly. In order to maintain consistency and reduce confusion, users +of the interface should use only the front end registration-oriented +interface, or the backend helpers. + +Using the backend helpers allows code to use the kobjects solely for +the reference counting and garbage collection mechanisms, and +optionally adding them to the object hierarchy or exporting them via +sysfs. + +To take advantage of this side of the interface, users should call +kobject_init() to initialize the object. As stated above, this will +leave the reference count of the object at 1, and will enable the +subsystem to use the reference count of the object. + +When the life of the object is ending, the kobject_put() should be +called to decrement the reference count of the object. Just like +above, this will call kobject_cleanup() when the reference count +reaches 0, and release() method of the object's subsystem will be +called. + +During the lifetime of the object, kobject_add() and kobject_del() may +be called to add the object to the hierarchy and export it via +sysfs. kobject_del() must always be called if kobject_add() has +previously been called. Care should be taken to ensure kobject_del() +is called before the final kobject_put() is called, though not doing +so will not cause catastrophe, only confusion when reading the source +code. Fatal results are avoided by having kobject_add() increment the +reference count of the object, for kobject_del() to decrement. + + +3.3 Summary + +Using either interface, users should obtain the same results. The +registration interface does the same actions as the backend interface, +though it guarantees that initialization and addition, and deletion +and cleanup, happen consecutively. + Familial Relations @@ -134,21 +233,3 @@ object that registers with them. A subsystem definition may contain a NULL-terminated array of attributes that will be exported when an object is registered with the subsystem. - -Reference Counting - -All objects contain reference counts. All functions accessing objects -should increment the reference count until they are finished, and -decrement the reference count. When an object is initialized, it -receives a reference count of 1. When a device is unregistered, the -reference is decremented. When the reference counts reaches 0, the -subsystem's ->release() callback for that object type (remember -subsystems control only one type of device each) is called; and the -reference counts of the kobject's subsystem and parent are -decremented. - -The ->release() callback is the opportunity for the subsystem to free -memory allocated for the object. It is the notification that -absolutely no one is using the structure any more (and can't acquire a -reference to it), so it is safe to free it. - diff --git a/drivers/base/base.h b/drivers/base/base.h index c6f4b2f47bf6..c9eeed6b07e9 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -19,6 +19,9 @@ extern void bus_remove_driver(struct device_driver *); extern int devclass_add_device(struct device *); extern void devclass_remove_device(struct device *); +extern int devclass_add_driver(struct device_driver *); +extern void devclass_remove_driver(struct device_driver *); + extern int interface_add(struct device_class *, struct device *); extern void interface_remove(struct device_class *, struct device *); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 845c0a669f32..17145af25b1b 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -7,7 +7,7 @@ * */ -#define DEBUG 0 +#undef DEBUG #include <linux/device.h> #include <linux/module.h> diff --git a/drivers/base/core.c b/drivers/base/core.c index dd04ea679f02..b6eb63fd9fdc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -5,7 +5,7 @@ * 2002 Open Source Development Lab */ -#define DEBUG 0 +#undef DEBUG #include <linux/device.h> #include <linux/err.h> @@ -134,7 +134,7 @@ int device_add(struct device *dev) devclass_add_device(dev); register_done: if (error) { - up(&device_sem); + down(&device_sem); list_del_init(&dev->g_list); list_del_init(&dev->node); up(&device_sem); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 29f25fca2255..84a85241ce8a 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -48,7 +48,7 @@ int __init register_cpu(struct cpu *cpu, int num, struct node *root) static int __init register_cpu_type(void) { - driver_register(&cpu_driver); - return devclass_register(&cpu_devclass); + devclass_register(&cpu_devclass); + return driver_register(&cpu_driver); } postcore_initcall(register_cpu_type); diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 824ac9ee2e99..0887022e13cd 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -3,7 +3,7 @@ * */ -#define DEBUG 0 +#undef DEBUG #include <linux/device.h> #include <linux/module.h> @@ -118,6 +118,7 @@ int driver_register(struct device_driver * drv) INIT_LIST_HEAD(&drv->devices); drv->present = 1; bus_add_driver(drv); + devclass_add_driver(drv); put_driver(drv); return 0; } @@ -128,6 +129,9 @@ void driver_unregister(struct device_driver * drv) drv->present = 0; spin_unlock(&device_lock); pr_debug("driver %s:%s: unregistering\n",drv->bus->name,drv->name); + bus_remove_driver(drv); + devclass_remove_driver(drv); + kobject_unregister(&drv->kobj); put_driver(drv); } diff --git a/drivers/base/fs/device.c b/drivers/base/fs/device.c index faaf687507cb..1c0ef88ee1ac 100644 --- a/drivers/base/fs/device.c +++ b/drivers/base/fs/device.c @@ -5,7 +5,7 @@ * 2002 Open Source Development Lab */ -#define DEBUG 0 +#undef DEBUG #include <linux/device.h> #include <linux/module.h> diff --git a/drivers/base/hotplug.c b/drivers/base/hotplug.c index 9a8a1a9b28f3..3499840e34b5 100644 --- a/drivers/base/hotplug.c +++ b/drivers/base/hotplug.c @@ -10,7 +10,7 @@ * */ -#define DEBUG 0 +#undef DEBUG #include <linux/device.h> #include <linux/slab.h> diff --git a/drivers/base/intf.c b/drivers/base/intf.c index 1876c8f0a651..e9eb94a2bed5 100644 --- a/drivers/base/intf.c +++ b/drivers/base/intf.c @@ -2,7 +2,7 @@ * intf.c - class-specific interface management */ -#define DEBUG 1 +#undef DEBUG #include <linux/device.h> #include <linux/module.h> @@ -37,7 +37,7 @@ int interface_register(struct device_interface * intf) struct device_class * cls = intf->devclass; if (cls) { - pr_debug("register interface '%s' with class '%s\n", + pr_debug("register interface '%s' with class '%s'\n", intf->name,cls->name); kobject_init(&intf->kobj); strncpy(intf->kobj.name,intf->name,KOBJ_NAME_LEN); diff --git a/drivers/base/power.c b/drivers/base/power.c index 280ca6d777cf..1cd4027feffa 100644 --- a/drivers/base/power.c +++ b/drivers/base/power.c @@ -8,7 +8,7 @@ * */ -#define DEBUG 0 +#undef DEBUG #include <linux/device.h> #include <linux/module.h> diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 3fcc2a6912bd..3745757f5b89 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -10,7 +10,7 @@ * add themselves as children of the system bus. */ -#define DEBUG 1 +#undef DEBUG #include <linux/device.h> #include <linux/err.h> diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index ad364d6aacb8..8f35216643fe 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -23,6 +23,8 @@ * Please see Documentation/filesystems/sysfs.txt for more information. */ +#undef DEBUG + #include <linux/list.h> #include <linux/init.h> #include <linux/pagemap.h> diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 40de16d0a227..7b3157a78f7e 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -24,6 +24,10 @@ struct kobject { }; extern void kobject_init(struct kobject *); +extern void kobject_cleanup(struct kobject *); + +extern int kobject_add(struct kobject *); +extern void kobject_del(struct kobject *); extern int kobject_register(struct kobject *); extern void kobject_unregister(struct kobject *); diff --git a/lib/kobject.c b/lib/kobject.c index f0312a268192..33e4f8c9ce58 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -2,15 +2,17 @@ * kobject.c - library routines for handling generic kernel objects */ -#define DEBUG 0 +#undef DEBUG #include <linux/kobject.h> #include <linux/string.h> #include <linux/module.h> #include <linux/stat.h> +static spinlock_t kobj_lock = SPIN_LOCK_UNLOCKED; + /** - * kobject_populate_dir - populate directory with attributes. + * populate_dir - populate directory with attributes. * @kobj: object we're working on. * * Most subsystems have a set of default attributes that @@ -21,7 +23,7 @@ * */ -static int kobject_populate_dir(struct kobject * kobj) +static int populate_dir(struct kobject * kobj) { struct subsystem * s = kobj->subsys; struct attribute * attr; @@ -37,6 +39,20 @@ static int kobject_populate_dir(struct kobject * kobj) return error; } +static int create_dir(struct kobject * kobj) +{ + int error = 0; + if (strlen(kobj->name)) { + error = sysfs_create_dir(kobj); + if (!error) { + if ((error = populate_dir(kobj))) + sysfs_remove_dir(kobj); + } + } + return error; +} + + /** * kobject_init - initialize object. * @kobj: object in question. @@ -46,70 +62,88 @@ void kobject_init(struct kobject * kobj) { atomic_set(&kobj->refcount,1); INIT_LIST_HEAD(&kobj->entry); + kobj->subsys = subsys_get(kobj->subsys); } /** - * kobject_register - register an object. - * @kobj: object in question. - * - * For now, fill in the replicated fields in the object's - * directory entry, and create a dir in sysfs. - * This stuff should go away in the future, as we move - * more implicit things to sysfs. + * kobject_add - add an object to the hierarchy. + * @kobj: object. */ -int kobject_register(struct kobject * kobj) +int kobject_add(struct kobject * kobj) { int error = 0; - struct subsystem * s = subsys_get(kobj->subsys); + struct subsystem * s = kobj->subsys; struct kobject * parent = kobject_get(kobj->parent); - pr_debug("kobject %s: registering\n",kobj->name); - if (parent) - pr_debug(" parent is %s\n",parent->name); + if (!(kobj = kobject_get(kobj))) + return -ENOENT; + pr_debug("kobject %s: registering. parent: %s, subsys: %s\n", + kobj->name, parent ? parent->name : "<NULL>", + kobj->subsys ? kobj->subsys->kobj.name : "<NULL>" ); + if (s) { down_write(&s->rwsem); if (parent) list_add_tail(&kobj->entry,&parent->entry); else { list_add_tail(&kobj->entry,&s->list); - kobj->parent = &s->kobj; + kobj->parent = kobject_get(&s->kobj); } up_write(&s->rwsem); } - if (strlen(kobj->name)) { - error = sysfs_create_dir(kobj); - if (!error) { - error = kobject_populate_dir(kobj); - if (error) - sysfs_remove_dir(kobj); - } - } + error = create_dir(kobj); + if (error && kobj->parent) + kobject_put(kobj->parent); return error; } + /** - * kobject_unregister - unlink an object. - * @kobj: object going away. - * - * The device has been told to be removed, but may - * not necessarily be disappearing from the kernel. - * So, we remove the directory and decrement the refcount - * that we set with kobject_register(). - * - * Eventually (maybe now), the refcount will hit 0, and - * put_device() will clean the device up. + * kobject_register - initialize and add an object. + * @kobj: object in question. */ -void kobject_unregister(struct kobject * kobj) +int kobject_register(struct kobject * kobj) +{ + int error = 0; + if (kobj) { + kobject_init(kobj); + error = kobject_add(kobj); + if (error) + kobject_cleanup(kobj); + } else + error = -EINVAL; + return error; +} + +/** + * kobject_del - unlink kobject from hierarchy. + * @kobj: object. + */ + +void kobject_del(struct kobject * kobj) { - pr_debug("kobject %s: unregistering\n",kobj->name); sysfs_remove_dir(kobj); if (kobj->subsys) { down_write(&kobj->subsys->rwsem); list_del_init(&kobj->entry); up_write(&kobj->subsys->rwsem); } + if (kobj->parent) + kobject_put(kobj->parent); + kobject_put(kobj); +} + +/** + * kobject_unregister - remove object from hierarchy and decrement refcount. + * @kobj: object going away. + */ + +void kobject_unregister(struct kobject * kobj) +{ + pr_debug("kobject %s: unregistering\n",kobj->name); + kobject_del(kobj); kobject_put(kobj); } @@ -121,45 +155,48 @@ void kobject_unregister(struct kobject * kobj) struct kobject * kobject_get(struct kobject * kobj) { struct kobject * ret = kobj; + spin_lock(&kobj_lock); if (kobj && atomic_read(&kobj->refcount) > 0) atomic_inc(&kobj->refcount); else ret = NULL; + spin_unlock(&kobj_lock); return ret; } /** - * kobject_put - decrement refcount for object. + * kobject_cleanup - free kobject resources. * @kobj: object. - * - * Decrement the refcount, and check if 0. If it is, then - * we're gonna need to clean it up, and decrement the refcount - * of its parent. - * - * @kobj->parent could point to its subsystem, which we also - * want to decrement the reference count for. We always dec - * the refcount for the parent, but only do so for the subsystem - * if it points to a different place than the parent. */ -void kobject_put(struct kobject * kobj) +void kobject_cleanup(struct kobject * kobj) { - struct kobject * parent = kobj->parent; struct subsystem * s = kobj->subsys; - if (!atomic_dec_and_test(&kobj->refcount)) - return; - pr_debug("kobject %s: cleaning up\n",kobj->name); if (s) { + down_write(&s->rwsem); + list_del_init(&kobj->entry); if (s->release) s->release(kobj); - if (&s->kobj != parent) - subsys_put(s); - } + up_write(&s->rwsem); + subsys_put(s); + } +} + +/** + * kobject_put - decrement refcount for object. + * @kobj: object. + * + * Decrement the refcount, and if 0, call kobject_cleanup(). + */ - if (parent) - kobject_put(parent); +void kobject_put(struct kobject * kobj) +{ + if (!atomic_dec_and_lock(&kobj->refcount, &kobj_lock)) + return; + spin_unlock(&kobj_lock); + kobject_cleanup(kobj); } @@ -180,9 +217,8 @@ int subsystem_register(struct subsystem * s) subsystem_init(s); if (s->parent) s->kobj.parent = &s->parent->kobj; - pr_debug("subsystem %s: registering\n",s->kobj.name); - if (s->parent) - pr_debug(" parent is %s\n",s->parent->kobj.name); + pr_debug("subsystem %s: registering, parent: %s\n", + s->kobj.name,s->parent ? s->parent->kobj.name : "<none>"); return kobject_register(&s->kobj); } |
