diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/kobject.c | 734 | ||||
-rw-r--r-- | lib/kobject_uevent.c | 38 | ||||
-rw-r--r-- | lib/kref.c | 15 |
3 files changed, 496 insertions, 291 deletions
diff --git a/lib/kobject.c b/lib/kobject.c index 3590f022a609..1d63ead1815e 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -18,58 +18,57 @@ #include <linux/stat.h> #include <linux/slab.h> -/** - * populate_dir - populate directory with attributes. - * @kobj: object we're working on. - * - * Most subsystems have a set of default attributes that - * are associated with an object that registers with them. - * This is a helper called during object registration that - * loops through the default attributes of the subsystem - * and creates attributes files for them in sysfs. +/* + * populate_dir - populate directory with attributes. + * @kobj: object we're working on. * + * Most subsystems have a set of default attributes that are associated + * with an object that registers with them. This is a helper called during + * object registration that loops through the default attributes of the + * subsystem and creates attributes files for them in sysfs. */ - -static int populate_dir(struct kobject * kobj) +static int populate_dir(struct kobject *kobj) { - struct kobj_type * t = get_ktype(kobj); - struct attribute * attr; + struct kobj_type *t = get_ktype(kobj); + struct attribute *attr; int error = 0; int i; - + if (t && t->default_attrs) { for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) { - if ((error = sysfs_create_file(kobj,attr))) + error = sysfs_create_file(kobj, attr); + if (error) break; } } return error; } -static int create_dir(struct kobject * kobj) +static int create_dir(struct kobject *kobj) { int error = 0; if (kobject_name(kobj)) { error = sysfs_create_dir(kobj); if (!error) { - if ((error = populate_dir(kobj))) + error = populate_dir(kobj); + if (error) sysfs_remove_dir(kobj); } } return error; } -static inline struct kobject * to_kobj(struct list_head * entry) +static inline struct kobject *to_kobj(struct list_head *entry) { - return container_of(entry,struct kobject,entry); + return container_of(entry, struct kobject, entry); } static int get_kobj_path_length(struct kobject *kobj) { int length = 1; - struct kobject * parent = kobj; + struct kobject *parent = kobj; - /* walk up the ancestors until we hit the one pointing to the + /* walk up the ancestors until we hit the one pointing to the * root. * Add 1 to strlen for leading '/' of each level. */ @@ -84,18 +83,19 @@ static int get_kobj_path_length(struct kobject *kobj) static void fill_kobj_path(struct kobject *kobj, char *path, int length) { - struct kobject * parent; + struct kobject *parent; --length; for (parent = kobj; parent; parent = parent->parent) { int cur = strlen(kobject_name(parent)); /* back up enough to print this name with '/' */ length -= cur; - strncpy (path + length, kobject_name(parent), cur); + strncpy(path + length, kobject_name(parent), cur); *(path + --length) = '/'; } - pr_debug("%s: path = '%s'\n",__FUNCTION__,path); + pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobject_name(kobj), + kobj, __FUNCTION__, path); } /** @@ -123,179 +123,286 @@ char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask) } EXPORT_SYMBOL_GPL(kobject_get_path); -/** - * kobject_init - initialize object. - * @kobj: object in question. - */ -void kobject_init(struct kobject * kobj) +/* add the kobject to its kset's list */ +static void kobj_kset_join(struct kobject *kobj) { - if (!kobj) + if (!kobj->kset) return; - kref_init(&kobj->kref); - INIT_LIST_HEAD(&kobj->entry); - kobj->kset = kset_get(kobj->kset); + + kset_get(kobj->kset); + spin_lock(&kobj->kset->list_lock); + list_add_tail(&kobj->entry, &kobj->kset->list); + spin_unlock(&kobj->kset->list_lock); } +/* remove the kobject from its kset's list */ +static void kobj_kset_leave(struct kobject *kobj) +{ + if (!kobj->kset) + return; -/** - * unlink - remove kobject from kset list. - * @kobj: kobject. - * - * Remove the kobject from the kset list and decrement - * its parent's refcount. - * This is separated out, so we can use it in both - * kobject_del() and kobject_add() on error. - */ + spin_lock(&kobj->kset->list_lock); + list_del_init(&kobj->entry); + spin_unlock(&kobj->kset->list_lock); + kset_put(kobj->kset); +} -static void unlink(struct kobject * kobj) +static void kobject_init_internal(struct kobject *kobj) { - if (kobj->kset) { - spin_lock(&kobj->kset->list_lock); - list_del_init(&kobj->entry); - spin_unlock(&kobj->kset->list_lock); - } - kobject_put(kobj); + if (!kobj) + return; + kref_init(&kobj->kref); + INIT_LIST_HEAD(&kobj->entry); } -/** - * kobject_add - add an object to the hierarchy. - * @kobj: object. - */ -int kobject_add(struct kobject * kobj) +static int kobject_add_internal(struct kobject *kobj) { int error = 0; - struct kobject * parent; + struct kobject *parent; - if (!(kobj = kobject_get(kobj))) + if (!kobj) return -ENOENT; - if (!kobj->k_name) - kobject_set_name(kobj, "NO_NAME"); - if (!*kobj->k_name) { - pr_debug("kobject attempted to be registered with no name!\n"); + + if (!kobj->name || !kobj->name[0]) { + pr_debug("kobject: (%p): attempted to be registered with empty " + "name!\n", kobj); WARN_ON(1); - kobject_put(kobj); return -EINVAL; } - parent = kobject_get(kobj->parent); - pr_debug("kobject %s: registering. parent: %s, set: %s\n", - kobject_name(kobj), parent ? kobject_name(parent) : "<NULL>", - kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>" ); + parent = kobject_get(kobj->parent); + /* join kset if set, use it as parent if we do not already have one */ if (kobj->kset) { - spin_lock(&kobj->kset->list_lock); - if (!parent) parent = kobject_get(&kobj->kset->kobj); - - list_add_tail(&kobj->entry,&kobj->kset->list); - spin_unlock(&kobj->kset->list_lock); + kobj_kset_join(kobj); kobj->parent = parent; } + pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n", + kobject_name(kobj), kobj, __FUNCTION__, + parent ? kobject_name(parent) : "<NULL>", + kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>"); + error = create_dir(kobj); if (error) { - /* unlink does the kobject_put() for us */ - unlink(kobj); + kobj_kset_leave(kobj); kobject_put(parent); + kobj->parent = NULL; /* be noisy on error issues */ if (error == -EEXIST) - printk(KERN_ERR "kobject_add failed for %s with " + printk(KERN_ERR "%s failed for %s with " "-EEXIST, don't try to register things with " "the same name in the same directory.\n", - kobject_name(kobj)); + __FUNCTION__, kobject_name(kobj)); else - printk(KERN_ERR "kobject_add failed for %s (%d)\n", - kobject_name(kobj), error); + printk(KERN_ERR "%s failed for %s (%d)\n", + __FUNCTION__, kobject_name(kobj), error); dump_stack(); - } + } else + kobj->state_in_sysfs = 1; return error; } /** - * kobject_register - initialize and add an object. - * @kobj: object in question. + * kobject_set_name_vargs - Set the name of an kobject + * @kobj: struct kobject to set the name of + * @fmt: format string used to build the name + * @vargs: vargs to format the string. */ - -int kobject_register(struct kobject * kobj) +static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, + va_list vargs) { - int error = -EINVAL; - if (kobj) { - kobject_init(kobj); - error = kobject_add(kobj); - if (!error) - kobject_uevent(kobj, KOBJ_ADD); - } - return error; -} + va_list aq; + char *name; + + va_copy(aq, vargs); + name = kvasprintf(GFP_KERNEL, fmt, vargs); + va_end(aq); + if (!name) + return -ENOMEM; + + /* Free the old name, if necessary. */ + kfree(kobj->name); + + /* Now, set the new name */ + kobj->name = name; + + return 0; +} /** * kobject_set_name - Set the name of a kobject - * @kobj: kobject to name + * @kobj: struct kobject to set the name of * @fmt: format string used to build the name * * This sets the name of the kobject. If you have already added the * kobject to the system, you must call kobject_rename() in order to * change the name of the kobject. */ -int kobject_set_name(struct kobject * kobj, const char * fmt, ...) +int kobject_set_name(struct kobject *kobj, const char *fmt, ...) { - int error = 0; - int limit; - int need; va_list args; - char *name; + int retval; - /* find out how big a buffer we need */ - name = kmalloc(1024, GFP_KERNEL); - if (!name) { - error = -ENOMEM; - goto done; - } va_start(args, fmt); - need = vsnprintf(name, 1024, fmt, args); + retval = kobject_set_name_vargs(kobj, fmt, args); va_end(args); - kfree(name); - /* Allocate the new space and copy the string in */ - limit = need + 1; - name = kmalloc(limit, GFP_KERNEL); - if (!name) { - error = -ENOMEM; - goto done; + return retval; +} +EXPORT_SYMBOL(kobject_set_name); + +/** + * kobject_init - initialize a kobject structure + * @kobj: pointer to the kobject to initialize + * @ktype: pointer to the ktype for this kobject. + * + * This function will properly initialize a kobject such that it can then + * be passed to the kobject_add() call. + * + * After this function is called, the kobject MUST be cleaned up by a call + * to kobject_put(), not by a call to kfree directly to ensure that all of + * the memory is cleaned up properly. + */ +void kobject_init(struct kobject *kobj, struct kobj_type *ktype) +{ + char *err_str; + + if (!kobj) { + err_str = "invalid kobject pointer!"; + goto error; + } + if (!ktype) { + err_str = "must have a ktype to be initialized properly!\n"; + goto error; + } + if (kobj->state_initialized) { + /* do not error out as sometimes we can recover */ + printk(KERN_ERR "kobject (%p): tried to init an initialized " + "object, something is seriously wrong.\n", kobj); + dump_stack(); } - va_start(args, fmt); - need = vsnprintf(name, limit, fmt, args); - va_end(args); - /* something wrong with the string we copied? */ - if (need >= limit) { - kfree(name); - error = -EFAULT; - goto done; + kref_init(&kobj->kref); + INIT_LIST_HEAD(&kobj->entry); + kobj->ktype = ktype; + kobj->state_in_sysfs = 0; + kobj->state_add_uevent_sent = 0; + kobj->state_remove_uevent_sent = 0; + kobj->state_initialized = 1; + return; + +error: + printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str); + dump_stack(); +} +EXPORT_SYMBOL(kobject_init); + +static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, + const char *fmt, va_list vargs) +{ + va_list aq; + int retval; + + va_copy(aq, vargs); + retval = kobject_set_name_vargs(kobj, fmt, aq); + va_end(aq); + if (retval) { + printk(KERN_ERR "kobject: can not set name properly!\n"); + return retval; } + kobj->parent = parent; + return kobject_add_internal(kobj); +} - /* Free the old name, if necessary. */ - kfree(kobj->k_name); +/** + * kobject_add - the main kobject add function + * @kobj: the kobject to add + * @parent: pointer to the parent of the kobject. + * @fmt: format to name the kobject with. + * + * The kobject name is set and added to the kobject hierarchy in this + * function. + * + * If @parent is set, then the parent of the @kobj will be set to it. + * If @parent is NULL, then the parent of the @kobj will be set to the + * kobject associted with the kset assigned to this kobject. If no kset + * is assigned to the kobject, then the kobject will be located in the + * root of the sysfs tree. + * + * If this function returns an error, kobject_put() must be called to + * properly clean up the memory associated with the object. + * Under no instance should the kobject that is passed to this function + * be directly freed with a call to kfree(), that can leak memory. + * + * Note, no "add" uevent will be created with this call, the caller should set + * up all of the necessary sysfs files for the object and then call + * kobject_uevent() with the UEVENT_ADD parameter to ensure that + * userspace is properly notified of this kobject's creation. + */ +int kobject_add(struct kobject *kobj, struct kobject *parent, + const char *fmt, ...) +{ + va_list args; + int retval; - /* Now, set the new name */ - kobj->k_name = name; -done: - return error; + if (!kobj) + return -EINVAL; + + if (!kobj->state_initialized) { + printk(KERN_ERR "kobject '%s' (%p): tried to add an " + "uninitialized object, something is seriously wrong.\n", + kobject_name(kobj), kobj); + dump_stack(); + return -EINVAL; + } + va_start(args, fmt); + retval = kobject_add_varg(kobj, parent, fmt, args); + va_end(args); + + return retval; } -EXPORT_SYMBOL(kobject_set_name); +EXPORT_SYMBOL(kobject_add); /** - * kobject_rename - change the name of an object - * @kobj: object in question. - * @new_name: object's new name + * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy + * @kobj: pointer to the kobject to initialize + * @ktype: pointer to the ktype for this kobject. + * @parent: pointer to the parent of this kobject. + * @fmt: the name of the kobject. + * + * This function combines the call to kobject_init() and + * kobject_add(). The same type of error handling after a call to + * kobject_add() and kobject lifetime rules are the same here. */ +int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, + struct kobject *parent, const char *fmt, ...) +{ + va_list args; + int retval; + + kobject_init(kobj, ktype); + + va_start(args, fmt); + retval = kobject_add_varg(kobj, parent, fmt, args); + va_end(args); + + return retval; +} +EXPORT_SYMBOL_GPL(kobject_init_and_add); -int kobject_rename(struct kobject * kobj, const char *new_name) +/** + * kobject_rename - change the name of an object + * @kobj: object in question. + * @new_name: object's new name + */ +int kobject_rename(struct kobject *kobj, const char *new_name) { int error = 0; const char *devpath = NULL; @@ -334,8 +441,6 @@ int kobject_rename(struct kobject * kobj, const char *new_name) sprintf(devpath_string, "DEVPATH_OLD=%s", devpath); envp[0] = devpath_string; envp[1] = NULL; - /* Note : if we want to send the new name alone, not the full path, - * we could probably use kobject_name(kobj); */ error = sysfs_rename_dir(kobj, new_name); @@ -354,11 +459,10 @@ out: } /** - * kobject_move - move object to another parent - * @kobj: object in question. - * @new_parent: object's new parent (can be NULL) + * kobject_move - move object to another parent + * @kobj: object in question. + * @new_parent: object's new parent (can be NULL) */ - int kobject_move(struct kobject *kobj, struct kobject *new_parent) { int error; @@ -406,68 +510,74 @@ out: } /** - * kobject_del - unlink kobject from hierarchy. - * @kobj: object. + * kobject_del - unlink kobject from hierarchy. + * @kobj: object. */ - -void kobject_del(struct kobject * kobj) +void kobject_del(struct kobject *kobj) { if (!kobj) return; - sysfs_remove_dir(kobj); - unlink(kobj); -} -/** - * kobject_unregister - remove object from hierarchy and decrement refcount. - * @kobj: object going away. - */ - -void kobject_unregister(struct kobject * kobj) -{ - if (!kobj) - return; - pr_debug("kobject %s: unregistering\n",kobject_name(kobj)); - kobject_uevent(kobj, KOBJ_REMOVE); - kobject_del(kobj); - kobject_put(kobj); + sysfs_remove_dir(kobj); + kobj->state_in_sysfs = 0; + kobj_kset_leave(kobj); + kobject_put(kobj->parent); + kobj->parent = NULL; } /** - * kobject_get - increment refcount for object. - * @kobj: object. + * kobject_get - increment refcount for object. + * @kobj: object. */ - -struct kobject * kobject_get(struct kobject * kobj) +struct kobject *kobject_get(struct kobject *kobj) { if (kobj) kref_get(&kobj->kref); return kobj; } -/** - * kobject_cleanup - free kobject resources. - * @kobj: object. +/* + * kobject_cleanup - free kobject resources. + * @kobj: object to cleanup */ - -void kobject_cleanup(struct kobject * kobj) +static void kobject_cleanup(struct kobject *kobj) { - struct kobj_type * t = get_ktype(kobj); - struct kset * s = kobj->kset; - struct kobject * parent = kobj->parent; - const char *name = kobj->k_name; + struct kobj_type *t = get_ktype(kobj); + const char *name = kobj->name; + + pr_debug("kobject: '%s' (%p): %s\n", + kobject_name(kobj), kobj, __FUNCTION__); + + if (t && !t->release) + pr_debug("kobject: '%s' (%p): does not have a release() " + "function, it is broken and must be fixed.\n", + kobject_name(kobj), kobj); + + /* send "remove" if the caller did not do it but sent "add" */ + if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) { + pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n", + kobject_name(kobj), kobj); + kobject_uevent(kobj, KOBJ_REMOVE); + } + + /* remove from sysfs if the caller did not do it */ + if (kobj->state_in_sysfs) { + pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n", + kobject_name(kobj), kobj); + kobject_del(kobj); + } - pr_debug("kobject %s: cleaning up\n",kobject_name(kobj)); if (t && t->release) { + pr_debug("kobject: '%s' (%p): calling ktype release\n", + kobject_name(kobj), kobj); t->release(kobj); - /* If we have a release function, we can guess that this was - * not a statically allocated kobject, so we should be safe to - * free the name */ + } + + /* free name if we allocated it */ + if (name) { + pr_debug("kobject: '%s': free name\n", name); kfree(name); } - if (s) - kset_put(s); - kobject_put(parent); } static void kobject_release(struct kref *kref) @@ -476,107 +586,130 @@ static void kobject_release(struct kref *kref) } /** - * kobject_put - decrement refcount for object. - * @kobj: object. + * kobject_put - decrement refcount for object. + * @kobj: object. * - * Decrement the refcount, and if 0, call kobject_cleanup(). + * Decrement the refcount, and if 0, call kobject_cleanup(). */ -void kobject_put(struct kobject * kobj) +void kobject_put(struct kobject *kobj) { if (kobj) kref_put(&kobj->kref, kobject_release); } - -static void dir_release(struct kobject *kobj) +static void dynamic_kobj_release(struct kobject *kobj) { + pr_debug("kobject: (%p): %s\n", kobj, __FUNCTION__); kfree(kobj); } -static struct kobj_type dir_ktype = { - .release = dir_release, - .sysfs_ops = NULL, - .default_attrs = NULL, +static struct kobj_type dynamic_kobj_ktype = { + .release = dynamic_kobj_release, + .sysfs_ops = &kobj_sysfs_ops, }; /** - * kobject_kset_add_dir - add sub directory of object. - * @kset: kset the directory is belongs to. - * @parent: object in which a directory is created. - * @name: directory name. + * kobject_create - create a struct kobject dynamically * - * Add a plain directory object as child of given object. + * This function creates a kobject structure dynamically and sets it up + * to be a "dynamic" kobject with a default release function set up. + * + * If the kobject was not able to be created, NULL will be returned. + * The kobject structure returned from here must be cleaned up with a + * call to kobject_put() and not kfree(), as kobject_init() has + * already been called on this structure. */ -struct kobject *kobject_kset_add_dir(struct kset *kset, - struct kobject *parent, const char *name) +struct kobject *kobject_create(void) { - struct kobject *k; - int ret; - - if (!parent) - return NULL; - - k = kzalloc(sizeof(*k), GFP_KERNEL); - if (!k) - return NULL; + struct kobject *kobj; - k->kset = kset; - k->parent = parent; - k->ktype = &dir_ktype; - kobject_set_name(k, name); - ret = kobject_register(k); - if (ret < 0) { - printk(KERN_WARNING "%s: kobject_register error: %d\n", - __func__, ret); - kobject_del(k); + kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); + if (!kobj) return NULL; - } - return k; + kobject_init(kobj, &dynamic_kobj_ktype); + return kobj; } /** - * kobject_add_dir - add sub directory of object. - * @parent: object in which a directory is created. - * @name: directory name. + * kobject_create_and_add - create a struct kobject dynamically and register it with sysfs + * + * @name: the name for the kset + * @parent: the parent kobject of this kobject, if any. + * + * This function creates a kset structure dynamically and registers it + * with sysfs. When you are finished with this structure, call + * kobject_put() and the structure will be dynamically freed when + * it is no longer being used. * - * Add a plain directory object as child of given object. + * If the kobject was not able to be created, NULL will be returned. */ -struct kobject *kobject_add_dir(struct kobject *parent, const char *name) +struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) { - return kobject_kset_add_dir(NULL, parent, name); + struct kobject *kobj; + int retval; + + kobj = kobject_create(); + if (!kobj) + return NULL; + + retval = kobject_add(kobj, parent, "%s", name); + if (retval) { + printk(KERN_WARNING "%s: kobject_add error: %d\n", + __FUNCTION__, retval); + kobject_put(kobj); + kobj = NULL; + } + return kobj; } +EXPORT_SYMBOL_GPL(kobject_create_and_add); /** - * kset_init - initialize a kset for use - * @k: kset + * kset_init - initialize a kset for use + * @k: kset */ - -void kset_init(struct kset * k) +void kset_init(struct kset *k) { - kobject_init(&k->kobj); + kobject_init_internal(&k->kobj); INIT_LIST_HEAD(&k->list); spin_lock_init(&k->list_lock); } +/* default kobject attribute operations */ +static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; -/** - * kset_add - add a kset object to the hierarchy. - * @k: kset. - */ + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->show) + ret = kattr->show(kobj, kattr, buf); + return ret; +} -int kset_add(struct kset * k) +static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) { - return kobject_add(&k->kobj); + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->store) + ret = kattr->store(kobj, kattr, buf, count); + return ret; } +struct sysfs_ops kobj_sysfs_ops = { + .show = kobj_attr_show, + .store = kobj_attr_store, +}; /** - * kset_register - initialize and add a kset. - * @k: kset. + * kset_register - initialize and add a kset. + * @k: kset. */ - -int kset_register(struct kset * k) +int kset_register(struct kset *k) { int err; @@ -584,46 +717,42 @@ int kset_register(struct kset * k) return -EINVAL; kset_init(k); - err = kset_add(k); + err = kobject_add_internal(&k->kobj); if (err) return err; kobject_uevent(&k->kobj, KOBJ_ADD); return 0; } - /** - * kset_unregister - remove a kset. - * @k: kset. + * kset_unregister - remove a kset. + * @k: kset. */ - -void kset_unregister(struct kset * k) +void kset_unregister(struct kset *k) { if (!k) return; - kobject_unregister(&k->kobj); + kobject_put(&k->kobj); } - /** - * kset_find_obj - search for object in kset. - * @kset: kset we're looking in. - * @name: object's name. + * kset_find_obj - search for object in kset. + * @kset: kset we're looking in. + * @name: object's name. * - * Lock kset via @kset->subsys, and iterate over @kset->list, - * looking for a matching kobject. If matching object is found - * take a reference and return the object. + * Lock kset via @kset->subsys, and iterate over @kset->list, + * looking for a matching kobject. If matching object is found + * take a reference and return the object. */ - -struct kobject * kset_find_obj(struct kset * kset, const char * name) +struct kobject *kset_find_obj(struct kset *kset, const char *name) { - struct list_head * entry; - struct kobject * ret = NULL; + struct list_head *entry; + struct kobject *ret = NULL; spin_lock(&kset->list_lock); - list_for_each(entry,&kset->list) { - struct kobject * k = to_kobj(entry); - if (kobject_name(k) && !strcmp(kobject_name(k),name)) { + list_for_each(entry, &kset->list) { + struct kobject *k = to_kobj(entry); + if (kobject_name(k) && !strcmp(kobject_name(k), name)) { ret = kobject_get(k); break; } @@ -632,47 +761,94 @@ struct kobject * kset_find_obj(struct kset * kset, const char * name) return ret; } -int subsystem_register(struct kset *s) +static void kset_release(struct kobject *kobj) { - return kset_register(s); + struct kset *kset = container_of(kobj, struct kset, kobj); + pr_debug("kobject: '%s' (%p): %s\n", + kobject_name(kobj), kobj, __FUNCTION__); + kfree(kset); } -void subsystem_unregister(struct kset *s) +static struct kobj_type kset_ktype = { + .sysfs_ops = &kobj_sysfs_ops, + .release = kset_release, +}; + +/** + * kset_create - create a struct kset dynamically + * + * @name: the name for the kset + * @uevent_ops: a struct kset_uevent_ops for the kset + * @parent_kobj: the parent kobject of this kset, if any. + * + * This function creates a kset structure dynamically. This structure can + * then be registered with the system and show up in sysfs with a call to + * kset_register(). When you are finished with this structure, if + * kset_register() has been called, call kset_unregister() and the + * structure will be dynamically freed when it is no longer being used. + * + * If the kset was not able to be created, NULL will be returned. + */ +static struct kset *kset_create(const char *name, + struct kset_uevent_ops *uevent_ops, + struct kobject *parent_kobj) { - kset_unregister(s); + struct kset *kset; + + kset = kzalloc(sizeof(*kset), GFP_KERNEL); + if (!kset) + return NULL; + kobject_set_name(&kset->kobj, name); + kset->uevent_ops = uevent_ops; + kset->kobj.parent = parent_kobj; + + /* + * The kobject of this kset will have a type of kset_ktype and belong to + * no kset itself. That way we can properly free it when it is + * finished being used. + */ + kset->kobj.ktype = &kset_ktype; + kset->kobj.kset = NULL; + + return kset; } /** - * subsystem_create_file - export sysfs attribute file. - * @s: subsystem. - * @a: subsystem attribute descriptor. + * kset_create_and_add - create a struct kset dynamically and add it to sysfs + * + * @name: the name for the kset + * @uevent_ops: a struct kset_uevent_ops for the kset + * @parent_kobj: the parent kobject of this kset, if any. + * + * This function creates a kset structure dynamically and registers it + * with sysfs. When you are finished with this structure, call + * kset_unregister() and the structure will be dynamically freed when it + * is no longer being used. + * + * If the kset was not able to be created, NULL will be returned. */ - -int subsys_create_file(struct kset *s, struct subsys_attribute *a) +struct kset *kset_create_and_add(const char *name, + struct kset_uevent_ops *uevent_ops, + struct kobject *parent_kobj) { - int error = 0; - - if (!s || !a) - return -EINVAL; + struct kset *kset; + int error; - if (kset_get(s)) { - error = sysfs_create_file(&s->kobj, &a->attr); - kset_put(s); + kset = kset_create(name, uevent_ops, parent_kobj); + if (!kset) + return NULL; + error = kset_register(kset); + if (error) { + kfree(kset); + return NULL; } - return error; + return kset; } +EXPORT_SYMBOL_GPL(kset_create_and_add); -EXPORT_SYMBOL(kobject_init); -EXPORT_SYMBOL(kobject_register); -EXPORT_SYMBOL(kobject_unregister); EXPORT_SYMBOL(kobject_get); EXPORT_SYMBOL(kobject_put); -EXPORT_SYMBOL(kobject_add); EXPORT_SYMBOL(kobject_del); EXPORT_SYMBOL(kset_register); EXPORT_SYMBOL(kset_unregister); - -EXPORT_SYMBOL(subsystem_register); -EXPORT_SYMBOL(subsystem_unregister); -EXPORT_SYMBOL(subsys_create_file); diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 5886147252d0..5a402e2982af 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -98,7 +98,8 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, int i = 0; int retval = 0; - pr_debug("%s\n", __FUNCTION__); + pr_debug("kobject: '%s' (%p): %s\n", + kobject_name(kobj), kobj, __FUNCTION__); /* search the kset we belong to */ top_kobj = kobj; @@ -106,7 +107,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, top_kobj = top_kobj->parent; if (!top_kobj->kset) { - pr_debug("kobject attempted to send uevent without kset!\n"); + pr_debug("kobject: '%s' (%p): %s: attempted to send uevent " + "without kset!\n", kobject_name(kobj), kobj, + __FUNCTION__); return -EINVAL; } @@ -116,7 +119,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, /* skip the event, if the filter returns zero. */ if (uevent_ops && uevent_ops->filter) if (!uevent_ops->filter(kset, kobj)) { - pr_debug("kobject filter function caused the event to drop!\n"); + pr_debug("kobject: '%s' (%p): %s: filter function " + "caused the event to drop!\n", + kobject_name(kobj), kobj, __FUNCTION__); return 0; } @@ -126,7 +131,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, else subsystem = kobject_name(&kset->kobj); if (!subsystem) { - pr_debug("unset subsystem caused the event to drop!\n"); + pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the " + "event to drop!\n", kobject_name(kobj), kobj, + __FUNCTION__); return 0; } @@ -166,12 +173,24 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, if (uevent_ops && uevent_ops->uevent) { retval = uevent_ops->uevent(kset, kobj, env); if (retval) { - pr_debug ("%s - uevent() returned %d\n", - __FUNCTION__, retval); + pr_debug("kobject: '%s' (%p): %s: uevent() returned " + "%d\n", kobject_name(kobj), kobj, + __FUNCTION__, retval); goto exit; } } + /* + * Mark "add" and "remove" events in the object to ensure proper + * events to userspace during automatic cleanup. If the object did + * send an "add" event, "remove" will automatically generated by + * the core, if not already done by the caller. + */ + if (action == KOBJ_ADD) + kobj->state_add_uevent_sent = 1; + else if (action == KOBJ_REMOVE) + kobj->state_remove_uevent_sent = 1; + /* we will send an event, so request a new sequence number */ spin_lock(&sequence_lock); seq = ++uevent_seqnum; @@ -219,11 +238,12 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, retval = add_uevent_var(env, "HOME=/"); if (retval) goto exit; - retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); + retval = add_uevent_var(env, + "PATH=/sbin:/bin:/usr/sbin:/usr/bin"); if (retval) goto exit; - call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC); + call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC); } exit: @@ -231,7 +251,6 @@ exit: kfree(env); return retval; } - EXPORT_SYMBOL_GPL(kobject_uevent_env); /** @@ -247,7 +266,6 @@ int kobject_uevent(struct kobject *kobj, enum kobject_action action) { return kobject_uevent_env(kobj, action, NULL); } - EXPORT_SYMBOL_GPL(kobject_uevent); /** diff --git a/lib/kref.c b/lib/kref.c index a6dc3ec328e0..9ecd6e865610 100644 --- a/lib/kref.c +++ b/lib/kref.c @@ -15,13 +15,23 @@ #include <linux/module.h> /** + * kref_set - initialize object and set refcount to requested number. + * @kref: object in question. + * @num: initial reference counter + */ +void kref_set(struct kref *kref, int num) +{ + atomic_set(&kref->refcount, num); + smp_mb(); +} + +/** * kref_init - initialize object. * @kref: object in question. */ void kref_init(struct kref *kref) { - atomic_set(&kref->refcount,1); - smp_mb(); + kref_set(kref, 1); } /** @@ -61,6 +71,7 @@ int kref_put(struct kref *kref, void (*release)(struct kref *kref)) return 0; } +EXPORT_SYMBOL(kref_set); EXPORT_SYMBOL(kref_init); EXPORT_SYMBOL(kref_get); EXPORT_SYMBOL(kref_put); |