From a6c066de9d449b0bbe2efbf6431b19c270e02060 Mon Sep 17 00:00:00 2001 From: Patrick Mochel Date: Tue, 29 Oct 2002 19:47:41 -0800 Subject: Introduce struct subsystem. A struct subsystem is basically a collection of objects of a certain type, and some callbacks to operate on objects of that type. subsystems contain embedded kobjects themselves, and have a similar set of library routines that kobjects do, which are mostly just wrappers for the correlating kobject routines. kobjects are inserted in depth-first order into their subsystem's list of objects. Orphan kobjects are also given foster parents that point to their subsystem. This provides a bit more rigidity in the hierarchy, and disallows any orphan kobjects. When an object is unregistered, it is removed from its subsystem's list. When the objects' refcount hits 0, the subsystem's ->release() callback is called. Documentation describing the objects and the interfaces has also been added. --- Documentation/kobject.txt | 144 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/kobject.h | 26 +++++++++ lib/kobject.c | 74 ++++++++++++++++++++++-- 3 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 Documentation/kobject.txt 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 + +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 #include #include +#include #include 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); -- cgit v1.2.3