X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fkobject.c;h=7ce6dc138e90ed391618b222847a664024daf402;hb=refs%2Fheads%2Fvserver;hp=781f3e8966955f4f0c109b9c19a43a4231725f81;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/lib/kobject.c b/lib/kobject.c index 781f3e896..7ce6dc138 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -10,12 +10,11 @@ * about using the kobject interface. */ -#undef DEBUG - #include #include #include #include +#include /** * populate_dir - populate directory with attributes. @@ -58,15 +57,12 @@ static int create_dir(struct kobject * kobj) return error; } - static inline struct kobject * to_kobj(struct list_head * entry) { return container_of(entry,struct kobject,entry); } - -#ifdef CONFIG_HOTPLUG -static int get_kobj_path_length(struct kset *kset, struct kobject *kobj) +static int get_kobj_path_length(struct kobject *kobj) { int length = 1; struct kobject * parent = kobj; @@ -76,13 +72,15 @@ static int get_kobj_path_length(struct kset *kset, struct kobject *kobj) * 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 kset *kset, struct kobject *kobj, char *path, int length) +static void fill_kobj_path(struct kobject *kobj, char *path, int length) { struct kobject * parent; @@ -98,138 +96,39 @@ static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, pr_debug("%s: path = '%s'\n",__FUNCTION__,path); } -#define BUFFER_SIZE 1024 /* should be enough memory for the env */ -#define NUM_ENVP 32 /* number of env pointers */ -static unsigned long sequence_num; -static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED; - -static void kset_hotplug(const char *action, struct kset *kset, - struct kobject *kobj) -{ - char *argv [3]; - char **envp = NULL; - char *buffer = NULL; - char *scratch; - int i = 0; - int retval; - int kobj_path_length; - char *kobj_path = NULL; - char *name = NULL; - unsigned long seq; - - /* If the kset has a filter operation, call it. If it returns - failure, no hotplug event is required. */ - if (kset->hotplug_ops->filter) { - if (!kset->hotplug_ops->filter(kset, kobj)) - return; - } - - pr_debug ("%s\n", __FUNCTION__); - - if (!hotplug_path[0]) - return; - - envp = kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); - if (!envp) - return; - memset (envp, 0x00, NUM_ENVP * sizeof (char *)); - - buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); - if (!buffer) - goto exit; - - if (kset->hotplug_ops->name) - name = kset->hotplug_ops->name(kset, kobj); - if (name == NULL) - name = kset->kobj.name; - - argv [0] = hotplug_path; - argv [1] = name; - argv [2] = NULL; - - /* minimal command environment */ - envp [i++] = "HOME=/"; - envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - - scratch = buffer; - - envp [i++] = scratch; - scratch += sprintf(scratch, "ACTION=%s", action) + 1; - - spin_lock(&sequence_lock); - seq = sequence_num++; - spin_unlock(&sequence_lock); - - envp [i++] = scratch; - scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1; - - kobj_path_length = get_kobj_path_length (kset, kobj); - kobj_path = kmalloc (kobj_path_length, GFP_KERNEL); - if (!kobj_path) - goto exit; - memset (kobj_path, 0x00, kobj_path_length); - fill_kobj_path (kset, kobj, kobj_path, kobj_path_length); - - envp [i++] = scratch; - scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1; - - if (kset->hotplug_ops->hotplug) { - /* have the kset specific function add its stuff */ - retval = kset->hotplug_ops->hotplug (kset, kobj, - &envp[i], NUM_ENVP - i, scratch, - BUFFER_SIZE - (scratch - buffer)); - if (retval) { - pr_debug ("%s - hotplug() returned %d\n", - __FUNCTION__, retval); - goto exit; - } - } - - pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1], - envp[0], envp[1], envp[2], envp[3], envp[4]); - retval = call_usermodehelper (argv[0], argv, envp, 0); - if (retval) - pr_debug ("%s - call_usermodehelper returned %d\n", - __FUNCTION__, retval); - -exit: - kfree(kobj_path); - kfree(buffer); - kfree(envp); - return; -} - -void kobject_hotplug(const char *action, struct kobject *kobj) -{ - struct kobject * top_kobj = kobj; - - /* If this kobj does not belong to a kset, - try to find a parent that does. */ - if (!top_kobj->kset && top_kobj->parent) { - do { - top_kobj = top_kobj->parent; - } while (!top_kobj->kset && top_kobj->parent); - } - - if (top_kobj->kset && top_kobj->kset->hotplug_ops) - kset_hotplug(action, top_kobj->kset, kobj); -} -#else -void kobject_hotplug(const char *action, struct kobject *kobj) +/** + * kobject_get_path - generate and return the path associated with a given kobj + * and kset pair. The result must be freed by the caller with kfree(). + * + * @kobj: kobject in question, with which to build the path + * @gfp_mask: the allocation type used to allocate the path + */ +char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask) { - return; + 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; } -#endif /* CONFIG_HOTPLUG */ +EXPORT_SYMBOL_GPL(kobject_get_path); /** * kobject_init - initialize object. * @kobj: object in question. */ - void kobject_init(struct kobject * kobj) { - atomic_set(&kobj->refcount,1); + kref_init(&kobj->kref); INIT_LIST_HEAD(&kobj->entry); + init_waitqueue_head(&kobj->poll); kobj->kset = kset_get(kobj->kset); } @@ -247,9 +146,9 @@ void kobject_init(struct kobject * kobj) static void unlink(struct kobject * kobj) { if (kobj->kset) { - down_write(&kobj->kset->subsys->rwsem); + spin_lock(&kobj->kset->list_lock); list_del_init(&kobj->entry); - up_write(&kobj->kset->subsys->rwsem); + spin_unlock(&kobj->kset->list_lock); } kobject_put(kobj); } @@ -268,6 +167,11 @@ int kobject_add(struct kobject * 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", @@ -275,23 +179,33 @@ int kobject_add(struct kobject * kobj) kobj->kset ? kobj->kset->kobj.name : "" ); if (kobj->kset) { - down_write(&kobj->kset->subsys->rwsem); + spin_lock(&kobj->kset->list_lock); if (!parent) parent = kobject_get(&kobj->kset->kobj); list_add_tail(&kobj->entry,&kobj->kset->list); - up_write(&kobj->kset->subsys->rwsem); + spin_unlock(&kobj->kset->list_lock); } kobj->parent = parent; error = create_dir(kobj); if (error) { + /* unlink does the kobject_put() for us */ unlink(kobj); if (parent) kobject_put(parent); - } else { - kobject_hotplug("add", kobj); + + /* 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; @@ -305,17 +219,13 @@ int kobject_add(struct kobject * kobj) int kobject_register(struct kobject * kobj) { - int error = 0; + int error = -EINVAL; if (kobj) { kobject_init(kobj); error = kobject_add(kobj); - if (error) { - printk("kobject_register failed for %s (%d)\n", - kobject_name(kobj),error); - dump_stack(); - } - } else - error = -EINVAL; + if (!error) + kobject_uevent(kobj, KOBJ_ADD); + } return error; } @@ -323,13 +233,12 @@ int kobject_register(struct kobject * kobj) /** * kobject_set_name - Set the name of an object * @kobj: object. - * @name: name. + * @fmt: format string used to build the name * - * If strlen(name) < KOBJ_NAME_LEN, then use a dynamically allocated + * 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; @@ -338,11 +247,12 @@ int kobject_set_name(struct kobject * kobj, const char * fmt, ...) va_list args; char * name; - va_start(args,fmt); /* * 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 { @@ -355,7 +265,9 @@ int kobject_set_name(struct kobject * kobj, const char * fmt, ...) error = -ENOMEM; goto Done; } + va_start(args,fmt); need = vsnprintf(name,limit,fmt,args); + va_end(args); /* Still? Give up. */ if (need >= limit) { @@ -372,7 +284,6 @@ int kobject_set_name(struct kobject * kobj, const char * fmt, ...) /* Now, set the new name */ kobj->k_name = name; Done: - va_end(args); return error; } @@ -385,7 +296,7 @@ EXPORT_SYMBOL(kobject_set_name); * @new_name: object's new name */ -int kobject_rename(struct kobject * kobj, char *new_name) +int kobject_rename(struct kobject * kobj, const char *new_name) { int error = 0; @@ -398,6 +309,56 @@ int kobject_rename(struct kobject * kobj, char *new_name) return error; } +/** + * kobject_move - move object to another parent + * @kobj: object in question. + * @new_parent: object's new parent + */ + +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) { + error = -EINVAL; + goto out; + } + /* 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. @@ -405,7 +366,6 @@ int kobject_rename(struct kobject * kobj, char *new_name) void kobject_del(struct kobject * kobj) { - kobject_hotplug("remove", kobj); sysfs_remove_dir(kobj); unlink(kobj); } @@ -418,6 +378,7 @@ void kobject_del(struct kobject * kobj) void kobject_unregister(struct kobject * kobj) { pr_debug("kobject %s: unregistering\n",kobject_name(kobj)); + kobject_uevent(kobj, KOBJ_REMOVE); kobject_del(kobj); kobject_put(kobj); } @@ -429,10 +390,8 @@ void kobject_unregister(struct kobject * kobj) struct kobject * kobject_get(struct kobject * kobj) { - if (kobj) { - WARN_ON(!atomic_read(&kobj->refcount)); - atomic_inc(&kobj->refcount); - } + if (kobj) + kref_get(&kobj->kref); return kobj; } @@ -459,19 +418,67 @@ void kobject_cleanup(struct kobject * kobj) 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 (atomic_dec_and_test(&kobj->refcount)) - kobject_cleanup(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 @@ -482,6 +489,7 @@ void kset_init(struct kset * k) { kobject_init(&k->kobj); INIT_LIST_HEAD(&k->list); + spin_lock_init(&k->list_lock); } @@ -546,7 +554,7 @@ struct kobject * kset_find_obj(struct kset * kset, const char * name) struct list_head * entry; struct kobject * ret = NULL; - down_read(&kset->subsys->rwsem); + 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)) { @@ -554,7 +562,7 @@ struct kobject * kset_find_obj(struct kset * kset, const char * name) break; } } - up_read(&kset->subsys->rwsem); + spin_unlock(&kset->list_lock); return ret; } @@ -617,7 +625,7 @@ int subsys_create_file(struct subsystem * s, struct subsys_attribute * a) * @s: subsystem. * @a: attribute desciptor. */ - +#if 0 void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a) { if (subsys_get(s)) { @@ -625,7 +633,7 @@ void subsys_remove_file(struct subsystem * s, struct subsys_attribute * a) subsys_put(s); } } - +#endif /* 0 */ EXPORT_SYMBOL(kobject_init); EXPORT_SYMBOL(kobject_register); @@ -634,15 +642,10 @@ EXPORT_SYMBOL(kobject_get); EXPORT_SYMBOL(kobject_put); EXPORT_SYMBOL(kobject_add); EXPORT_SYMBOL(kobject_del); -EXPORT_SYMBOL(kobject_rename); -EXPORT_SYMBOL(kobject_hotplug); EXPORT_SYMBOL(kset_register); EXPORT_SYMBOL(kset_unregister); -EXPORT_SYMBOL(kset_find_obj); -EXPORT_SYMBOL(subsystem_init); EXPORT_SYMBOL(subsystem_register); EXPORT_SYMBOL(subsystem_unregister); EXPORT_SYMBOL(subsys_create_file); -EXPORT_SYMBOL(subsys_remove_file);