Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / lib / kobject.c
index 5e8d1ed..687ab41 100644 (file)
  * about using the kobject interface.
  */
 
-#undef DEBUG
-
 #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.
@@ -37,7 +36,7 @@ static int populate_dir(struct kobject * kobj)
        int i;
        
        if (t && t->default_attrs) {
-               for (i = 0; (attr = t->default_attrs[i]); i++) {
+               for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
                        if ((error = sysfs_create_file(kobj,attr)))
                                break;
                }
@@ -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] = 0;
-
-       /* 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 = kmalloc(len, gfp_mask);
+       if (!path)
+               return NULL;
+       memset(path, 0x00, len);
+       fill_kobj_path(kobj, path, len);
+
+       return path;
 }
-#endif /* CONFIG_HOTPLUG */
 
 /**
  *     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 : "<NULL>" );
 
        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)
+                       pr_debug("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
+                       pr_debug("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,13 +296,17 @@ EXPORT_SYMBOL(kobject_set_name);
  *     @new_name: object's new name
  */
 
-void kobject_rename(struct kobject * kobj, char *new_name)
+int kobject_rename(struct kobject * kobj, const char *new_name)
 {
+       int error = 0;
+
        kobj = kobject_get(kobj);
        if (!kobj)
-               return;
-       sysfs_rename_dir(kobj, new_name);
+               return -EINVAL;
+       error = sysfs_rename_dir(kobj, new_name);
        kobject_put(kobj);
+
+       return error;
 }
 
 /**
@@ -401,7 +316,6 @@ void kobject_rename(struct kobject * kobj, char *new_name)
 
 void kobject_del(struct kobject * kobj)
 {
-       kobject_hotplug("remove", kobj);
        sysfs_remove_dir(kobj);
        unlink(kobj);
 }
@@ -414,6 +328,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);
 }
@@ -425,10 +340,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;
 }
 
@@ -455,19 +368,60 @@ 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;
+
+       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);
+       kobject_register(k);
+
+       return k;
+}
 
 /**
  *     kset_init - initialize a kset for use
@@ -478,6 +432,7 @@ void kset_init(struct kset * k)
 {
        kobject_init(&k->kobj);
        INIT_LIST_HEAD(&k->list);
+       spin_lock_init(&k->list_lock);
 }
 
 
@@ -533,7 +488,8 @@ void kset_unregister(struct kset * k)
  *     @name:  object's name.
  *
  *     Lock kset via @kset->subsys, and iterate over @kset->list,
- *     looking for a matching kobject. Return object if found.
+ *     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)
@@ -541,15 +497,15 @@ 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))) {
-                       ret = k;
+               if (kobject_name(k) && !strcmp(kobject_name(k),name)) {
+                       ret = kobject_get(k);
                        break;
                }
        }
-       up_read(&kset->subsys->rwsem);
+       spin_unlock(&kset->list_lock);
        return ret;
 }
 
@@ -612,7 +568,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)) {
@@ -620,7 +576,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);
@@ -629,15 +585,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);