summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Mochel <mochel@osdl.org>2002-11-18 22:56:23 -0600
committerPatrick Mochel <mochel@osdl.org>2002-11-18 22:56:23 -0600
commitbb4be78ad606eb5d9dce29ab089b4dc0253886be (patch)
tree8fc0974f694d09e561532405c2be41ae84a5abc4
parent65b5db509e62f3d11e15cbf916323ee7f0849888 (diff)
parent0d3b7ccf5759e4a8a6dec610bbff7952405b5e7e (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.txt125
-rw-r--r--drivers/base/base.h3
-rw-r--r--drivers/base/bus.c2
-rw-r--r--drivers/base/core.c4
-rw-r--r--drivers/base/cpu.c4
-rw-r--r--drivers/base/driver.c6
-rw-r--r--drivers/base/fs/device.c2
-rw-r--r--drivers/base/hotplug.c2
-rw-r--r--drivers/base/intf.c4
-rw-r--r--drivers/base/power.c2
-rw-r--r--drivers/base/sys.c2
-rw-r--r--fs/sysfs/inode.c2
-rw-r--r--include/linux/kobject.h4
-rw-r--r--lib/kobject.c154
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);
}