summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/kobject.txt144
-rw-r--r--include/linux/kobject.h26
-rw-r--r--lib/kobject.c74
3 files changed, 239 insertions, 5 deletions
diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt
new file mode 100644
index 000000000000..d8e2d36b9140
--- /dev/null
+++ b/Documentation/kobject.txt
@@ -0,0 +1,144 @@
+kobjects - Simple, Generic Kernel Objects
+
+Patrick Mochel <mochel@osdl.org>
+
+30 October 2002
+
+
+kobjects
+
+struct kobject introduces a simple, intregral datatype and a simple
+set of semantics for operating on the device. kobjects are intended to
+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:
+
+
+struct kobject {
+ char name[16];
+ atomic_t refcount;
+ struct list_head entry;
+ struct kobject * parent;
+ struct subsystem * subsys;
+ struct sysfs_dir dir;
+};
+
+void kobject_init(struct kobject *);
+int kobject_register(struct kobject *);
+void kobject_unregister(struct kobject *);
+struct kobject * kobject_get(struct kobject *);
+void kobject_put(struct kobject *);
+
+
+
+subsystems
+
+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:
+
+struct subsystem {
+ struct kobject kobj;
+ struct list_head list;
+ struct rw_semaphore rwsem;
+ struct subsystem * parent;
+ struct sysfs_ops * sysfs_ops;
+ void (*release)(struct kobject *);
+};
+
+void subsystem_init(struct subsystem *);
+int subsystem_register(struct subsystem *);
+void subsystem_unregister(struct subsystem *);
+
+struct subsystem * subsys_get(struct subsystem * s);
+void subsys_put(struct subsystem * s);
+
+
+
+Familial Relations
+
+kobjects and subsystems intersect and intertwine in several ways. Each
+is well-defined (though maybe they could be made simpler). Each kobject
+belongs to a subsystem. Since subsystems are kobjects themselves, they
+also belong to a controlling subsystem. This implies that subsystems
+are hierarchial.
+
+Many kobjects are hierarchial in nature, which is represented by
+including a pointer to its parent kobject in struct kobject. Many
+different types of kobject-embedding objects may all point to the same
+parent.
+
+The ancestral hierarchy of kobjects should not be confused with
+membership in a subsystem, or the ancestral relationship of
+subsystems. A set of kobjects may all belong to a subsystem, but all
+have different parents.
+
+kobjects may be orphans and have no explicit parent. In that case, the
+subsystem to which the object belongs becomes its parent.
+
+
+Sysfs
+
+These rules force a complete kobject hierarchy, which Suprise! maps
+very well onto a filesystem.
+
+driverfs was recently cloned, and there now exists sysfs. All driverfs
+operations operate on a separate data type: struct driver_dir_entry,
+which all objects that are represented in driverfs must have. driverfs
+also allowed rogue directory creation that had no explicit objects
+associated with them.
+
+struct kobject is intended to be the common data type which sysfs
+operates on. This gives the filesystem the ability to directly access
+more fields of the object, including the reference count. This also
+forces each directory in the filesystem to be tied directly to a
+kobject.
+
+
+Directory Placement
+
+Parental relationships are determined in the kobject/subsystem layer,
+and the kobject is then passed off to the sysfs layer. kobjects with
+no parent have directories created for them in the sysfs root
+directory. Per the rules above, the only kobjects that remain orphans
+are subsystems without parent subsystems (since leaf objects either
+have an explicit parent, or are assigned their controlling subsystem
+as their foster parent).
+
+
+File Callbacks
+
+Previously, each driverfs directory contained a pointer to a list of file
+operations for reading and writing driverfs files. These callbacks
+received a struct driver_dir_entry, when they performed a
+container_of() transform on to receive the specific object type for
+which the call was meant.
+
+These callbacks have been converted to accept a struct kobject instead
+of struct driver_dir_entry. Since all kobjects belong to a subsystem
+that contains kobjects all of the same type, the sysfs operations
+have been moved to reside in the subsystem, since they are common for
+all kobjects.
+
+
+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/include/linux/kobject.h b/include/linux/kobject.h
index 12431a980712..32dfaaf52d88 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -9,6 +9,7 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/sysfs.h>
+#include <linux/rwsem.h>
#include <asm/atomic.h>
struct kobject {
@@ -16,6 +17,7 @@ struct kobject {
atomic_t refcount;
struct list_head entry;
struct kobject * parent;
+ struct subsystem * subsys;
struct sysfs_dir dir;
};
@@ -27,4 +29,28 @@ extern void kobject_unregister(struct kobject *);
extern struct kobject * kobject_get(struct kobject *);
extern void kobject_put(struct kobject *);
+
+struct subsystem {
+ struct kobject kobj;
+ struct list_head list;
+ struct rw_semaphore rwsem;
+ struct subsystem * parent;
+ void (*release)(struct kobject *);
+ struct sysfs_ops * sysfs_ops;
+};
+
+extern void subsystem_init(struct subsystem *);
+extern int subsystem_register(struct subsystem *);
+extern void subsystem_unregister(struct subsystem *);
+
+static inline struct subsystem * subsys_get(struct subsystem * s)
+{
+ return container_of(kobject_get(&s->kobj),struct subsystem,kobj);
+}
+
+static inline void subsys_put(struct subsystem * s)
+{
+ kobject_put(&s->kobj);
+}
+
#endif /* _KOBJECT_H_ */
diff --git a/lib/kobject.c b/lib/kobject.c
index 48e950c9e4ce..89fb79c873fb 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -31,10 +31,21 @@ void kobject_init(struct kobject * kobj)
int kobject_register(struct kobject * kobj)
{
+ struct subsystem * s = subsys_get(kobj->subsys);
+ struct kobject * parent = kobject_get(kobj->parent);
+
pr_debug("kobject %s: registering\n",kobj->name);
- if (kobj->parent)
- kobject_get(kobj->parent);
- return 0;
+ 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;
+ }
+ up_write(&s->rwsem);
+ }
+ return sysfs_create_dir(kobj);
}
/**
@@ -53,6 +64,12 @@ int kobject_register(struct kobject * kobj)
void kobject_unregister(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);
+ }
kobject_put(kobj);
}
@@ -64,7 +81,7 @@ void kobject_unregister(struct kobject * kobj)
struct kobject * kobject_get(struct kobject * kobj)
{
struct kobject * ret = kobj;
- if (atomic_read(&kobj->refcount) > 0)
+ if (kobj && atomic_read(&kobj->refcount) > 0)
atomic_inc(&kobj->refcount);
else
ret = NULL;
@@ -78,21 +95,68 @@ struct kobject * kobject_get(struct kobject * kobj)
* 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)
{
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 (parent)
+ if (s) {
+ if (s->release)
+ s->release(kobj);
+ if (&s->kobj != parent)
+ subsys_put(s);
+ }
+
+ if (parent)
kobject_put(parent);
}
+
+void subsystem_init(struct subsystem * s)
+{
+ kobject_init(&s->kobj);
+ init_rwsem(&s->rwsem);
+ INIT_LIST_HEAD(&s->list);
+}
+
+/**
+ * subsystem_register - register a subsystem.
+ * @s: the subsystem we're registering.
+ */
+
+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);
+ return kobject_register(&s->kobj);
+}
+
+void subsystem_unregister(struct subsystem * s)
+{
+ pr_debug("subsystem %s: unregistering\n",s->kobj.name);
+ kobject_unregister(&s->kobj);
+}
+
+
EXPORT_SYMBOL(kobject_init);
EXPORT_SYMBOL(kobject_register);
EXPORT_SYMBOL(kobject_unregister);
EXPORT_SYMBOL(kobject_get);
EXPORT_SYMBOL(kobject_put);
+
+EXPORT_SYMBOL(subsystem_init);
+EXPORT_SYMBOL(subsystem_register);
+EXPORT_SYMBOL(subsystem_unregister);