diff options
| -rw-r--r-- | Documentation/kobject.txt | 144 | ||||
| -rw-r--r-- | include/linux/kobject.h | 26 | ||||
| -rw-r--r-- | lib/kobject.c | 74 |
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); |
