123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701 |
- /*
- * kobject.c - library routines for handling generic kernel objects
- *
- * Copyright (c) 2002-2003 Patrick Mochel <mochel@osdl.org>
- *
- * This file is released under the GPLv2.
- *
- *
- * Please see the file Documentation/kobject.txt for critical information
- * about using the kobject interface.
- */
- #include <linux/kobject.h>
- #include <linux/string.h>
- #include <linux/module.h>
- #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.
- *
- */
- static int populate_dir(struct kobject * kobj)
- {
- 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)))
- break;
- }
- }
- return error;
- }
- static int create_dir(struct kobject * kobj, struct dentry *shadow_parent)
- {
- int error = 0;
- if (kobject_name(kobj)) {
- error = sysfs_create_dir(kobj, shadow_parent);
- if (!error) {
- if ((error = populate_dir(kobj)))
- sysfs_remove_dir(kobj);
- }
- }
- return error;
- }
- static inline struct kobject * to_kobj(struct list_head * entry)
- {
- return container_of(entry,struct kobject,entry);
- }
- static int get_kobj_path_length(struct kobject *kobj)
- {
- int length = 1;
- struct kobject * parent = kobj;
- /* walk up the ancestors until we hit the one pointing to the
- * root.
- * Add 1 to strlen for leading '/' of each level.
- */
- do {
- if (kobject_name(parent) == NULL)
- return 0;
- length += strlen(kobject_name(parent)) + 1;
- parent = parent->parent;
- } while (parent);
- return length;
- }
- static void fill_kobj_path(struct kobject *kobj, char *path, int length)
- {
- 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);
- *(path + --length) = '/';
- }
- pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
- }
- /**
- * kobject_get_path - generate and return the path associated with a given kobj and kset pair.
- *
- * @kobj: kobject in question, with which to build the path
- * @gfp_mask: the allocation type used to allocate the path
- *
- * The result must be freed by the caller with kfree().
- */
- char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
- {
- char *path;
- int len;
- len = get_kobj_path_length(kobj);
- if (len == 0)
- return NULL;
- path = kzalloc(len, gfp_mask);
- if (!path)
- return NULL;
- fill_kobj_path(kobj, path, len);
- return path;
- }
- EXPORT_SYMBOL_GPL(kobject_get_path);
- /**
- * kobject_init - initialize object.
- * @kobj: object in question.
- */
- void kobject_init(struct kobject * kobj)
- {
- if (!kobj)
- return;
- kref_init(&kobj->kref);
- INIT_LIST_HEAD(&kobj->entry);
- init_waitqueue_head(&kobj->poll);
- kobj->kset = kset_get(kobj->kset);
- }
- /**
- * 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.
- */
- static void unlink(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);
- }
- /**
- * kobject_add - add an object to the hierarchy.
- * @kobj: object.
- * @shadow_parent: sysfs directory to add to.
- */
- int kobject_shadow_add(struct kobject * kobj, struct dentry *shadow_parent)
- {
- int error = 0;
- struct kobject * parent;
- if (!(kobj = kobject_get(kobj)))
- return -ENOENT;
- if (!kobj->k_name)
- kobj->k_name = kobj->name;
- if (!kobj->k_name) {
- pr_debug("kobject attempted to be registered with no name!\n");
- WARN_ON(1);
- 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 ? kobj->kset->kobj.name : "<NULL>" );
- 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->parent = parent;
- error = create_dir(kobj, shadow_parent);
- if (error) {
- /* unlink does the kobject_put() for us */
- unlink(kobj);
- kobject_put(parent);
- /* be noisy on error issues */
- if (error == -EEXIST)
- printk("kobject_add failed for %s with -EEXIST, "
- "don't try to register things with the "
- "same name in the same directory.\n",
- kobject_name(kobj));
- else
- printk("kobject_add failed for %s (%d)\n",
- kobject_name(kobj), error);
- dump_stack();
- }
- return error;
- }
- /**
- * kobject_add - add an object to the hierarchy.
- * @kobj: object.
- */
- int kobject_add(struct kobject * kobj)
- {
- return kobject_shadow_add(kobj, NULL);
- }
- /**
- * kobject_register - initialize and add an object.
- * @kobj: object in question.
- */
- int kobject_register(struct kobject * kobj)
- {
- int error = -EINVAL;
- if (kobj) {
- kobject_init(kobj);
- error = kobject_add(kobj);
- if (!error)
- kobject_uevent(kobj, KOBJ_ADD);
- }
- return error;
- }
- /**
- * kobject_set_name - Set the name of an object
- * @kobj: object.
- * @fmt: format string used to build the name
- *
- * If strlen(name) >= KOBJ_NAME_LEN, then use a dynamically allocated
- * string that @kobj->k_name points to. Otherwise, use the static
- * @kobj->name array.
- */
- int kobject_set_name(struct kobject * kobj, const char * fmt, ...)
- {
- int error = 0;
- int limit = KOBJ_NAME_LEN;
- int need;
- va_list args;
- char * name;
- /*
- * First, try the static array
- */
- va_start(args,fmt);
- need = vsnprintf(kobj->name,limit,fmt,args);
- va_end(args);
- if (need < limit)
- name = kobj->name;
- else {
- /*
- * Need more space? Allocate it and try again
- */
- limit = need + 1;
- name = kmalloc(limit,GFP_KERNEL);
- if (!name) {
- error = -ENOMEM;
- goto Done;
- }
- va_start(args,fmt);
- need = vsnprintf(name,limit,fmt,args);
- va_end(args);
- /* Still? Give up. */
- if (need >= limit) {
- kfree(name);
- error = -EFAULT;
- goto Done;
- }
- }
- /* Free the old name, if necessary. */
- if (kobj->k_name && kobj->k_name != kobj->name)
- kfree(kobj->k_name);
- /* Now, set the new name */
- kobj->k_name = name;
- Done:
- return error;
- }
- EXPORT_SYMBOL(kobject_set_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;
- kobj = kobject_get(kobj);
- if (!kobj)
- return -EINVAL;
- if (!kobj->parent)
- return -EINVAL;
- error = sysfs_rename_dir(kobj, kobj->parent->dentry, new_name);
- kobject_put(kobj);
- return error;
- }
- /**
- * kobject_rename - change the name of an object
- * @kobj: object in question.
- * @new_name: object's new name
- */
- int kobject_shadow_rename(struct kobject * kobj, struct dentry *new_parent,
- const char *new_name)
- {
- int error = 0;
- kobj = kobject_get(kobj);
- if (!kobj)
- return -EINVAL;
- error = sysfs_rename_dir(kobj, new_parent, new_name);
- kobject_put(kobj);
- return error;
- }
- /**
- * 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;
- struct kobject *old_parent;
- const char *devpath = NULL;
- char *devpath_string = NULL;
- char *envp[2];
- kobj = kobject_get(kobj);
- if (!kobj)
- return -EINVAL;
- new_parent = kobject_get(new_parent);
- if (!new_parent) {
- if (kobj->kset)
- new_parent = kobject_get(&kobj->kset->kobj);
- }
- /* old object path */
- devpath = kobject_get_path(kobj, GFP_KERNEL);
- if (!devpath) {
- error = -ENOMEM;
- goto out;
- }
- devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
- if (!devpath_string) {
- error = -ENOMEM;
- goto out;
- }
- sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
- envp[0] = devpath_string;
- envp[1] = NULL;
- error = sysfs_move_dir(kobj, new_parent);
- if (error)
- goto out;
- old_parent = kobj->parent;
- kobj->parent = new_parent;
- kobject_put(old_parent);
- kobject_uevent_env(kobj, KOBJ_MOVE, envp);
- out:
- kobject_put(kobj);
- kfree(devpath_string);
- kfree(devpath);
- return error;
- }
- /**
- * kobject_del - unlink kobject from hierarchy.
- * @kobj: object.
- */
- 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);
- }
- /**
- * kobject_get - increment refcount for object.
- * @kobj: object.
- */
- struct kobject * kobject_get(struct kobject * kobj)
- {
- if (kobj)
- kref_get(&kobj->kref);
- return kobj;
- }
- /**
- * kobject_cleanup - free kobject resources.
- * @kobj: object.
- */
- void kobject_cleanup(struct kobject * kobj)
- {
- struct kobj_type * t = get_ktype(kobj);
- struct kset * s = kobj->kset;
- struct kobject * parent = kobj->parent;
- pr_debug("kobject %s: cleaning up\n",kobject_name(kobj));
- if (kobj->k_name != kobj->name)
- kfree(kobj->k_name);
- kobj->k_name = NULL;
- if (t && t->release)
- t->release(kobj);
- if (s)
- kset_put(s);
- kobject_put(parent);
- }
- static void kobject_release(struct kref *kref)
- {
- kobject_cleanup(container_of(kref, struct kobject, kref));
- }
- /**
- * kobject_put - decrement refcount for object.
- * @kobj: object.
- *
- * Decrement the refcount, and if 0, call kobject_cleanup().
- */
- void kobject_put(struct kobject * kobj)
- {
- if (kobj)
- kref_put(&kobj->kref, kobject_release);
- }
- static void dir_release(struct kobject *kobj)
- {
- kfree(kobj);
- }
- static struct kobj_type dir_ktype = {
- .release = dir_release,
- .sysfs_ops = NULL,
- .default_attrs = NULL,
- };
- /**
- * kobject_add_dir - add sub directory of object.
- * @parent: object in which a directory is created.
- * @name: directory name.
- *
- * Add a plain directory object as child of given object.
- */
- struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
- {
- struct kobject *k;
- int ret;
- if (!parent)
- return NULL;
- k = kzalloc(sizeof(*k), GFP_KERNEL);
- if (!k)
- return NULL;
- k->parent = parent;
- k->ktype = &dir_ktype;
- kobject_set_name(k, name);
- ret = kobject_register(k);
- if (ret < 0) {
- printk(KERN_WARNING "kobject_add_dir: "
- "kobject_register error: %d\n", ret);
- kobject_del(k);
- return NULL;
- }
- return k;
- }
- /**
- * kset_init - initialize a kset for use
- * @k: kset
- */
- void kset_init(struct kset * k)
- {
- kobject_init(&k->kobj);
- INIT_LIST_HEAD(&k->list);
- spin_lock_init(&k->list_lock);
- }
- /**
- * kset_add - add a kset object to the hierarchy.
- * @k: kset.
- *
- * Simply, this adds the kset's embedded kobject to the
- * hierarchy.
- * We also try to make sure that the kset's embedded kobject
- * has a parent before it is added. We only care if the embedded
- * kobject is not part of a kset itself, since kobject_add()
- * assigns a parent in that case.
- * If that is the case, and the kset has a controlling subsystem,
- * then we set the kset's parent to be said subsystem.
- */
- int kset_add(struct kset * k)
- {
- if (!k->kobj.parent && !k->kobj.kset && k->subsys)
- k->kobj.parent = &k->subsys->kset.kobj;
- return kobject_add(&k->kobj);
- }
- /**
- * kset_register - initialize and add a kset.
- * @k: kset.
- */
- int kset_register(struct kset * k)
- {
- if (!k)
- return -EINVAL;
- kset_init(k);
- return kset_add(k);
- }
- /**
- * kset_unregister - remove a kset.
- * @k: kset.
- */
- void kset_unregister(struct kset * k)
- {
- if (!k)
- return;
- kobject_unregister(&k->kobj);
- }
- /**
- * 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.
- */
- struct kobject * kset_find_obj(struct kset * kset, const char * name)
- {
- 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)) {
- ret = kobject_get(k);
- break;
- }
- }
- spin_unlock(&kset->list_lock);
- return ret;
- }
- void subsystem_init(struct subsystem * s)
- {
- init_rwsem(&s->rwsem);
- kset_init(&s->kset);
- }
- /**
- * subsystem_register - register a subsystem.
- * @s: the subsystem we're registering.
- *
- * Once we register the subsystem, we want to make sure that
- * the kset points back to this subsystem for correct usage of
- * the rwsem.
- */
- int subsystem_register(struct subsystem * s)
- {
- int error;
- if (!s)
- return -EINVAL;
- subsystem_init(s);
- pr_debug("subsystem %s: registering\n",s->kset.kobj.name);
- if (!(error = kset_add(&s->kset))) {
- if (!s->kset.subsys)
- s->kset.subsys = s;
- }
- return error;
- }
- void subsystem_unregister(struct subsystem * s)
- {
- if (!s)
- return;
- pr_debug("subsystem %s: unregistering\n",s->kset.kobj.name);
- kset_unregister(&s->kset);
- }
- /**
- * subsystem_create_file - export sysfs attribute file.
- * @s: subsystem.
- * @a: subsystem attribute descriptor.
- */
- int subsys_create_file(struct subsystem * s, struct subsys_attribute * a)
- {
- int error = 0;
- if (!s || !a)
- return -EINVAL;
- if (subsys_get(s)) {
- error = sysfs_create_file(&s->kset.kobj,&a->attr);
- subsys_put(s);
- }
- return error;
- }
- /**
- * subsystem_remove_file - remove sysfs attribute file.
- * @s: subsystem.
- * @a: attribute desciptor.
- */
- #if 0
- void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a)
- {
- if (subsys_get(s)) {
- sysfs_remove_file(&s->kset.kobj,&a->attr);
- subsys_put(s);
- }
- }
- #endif /* 0 */
- 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);
|