driver core: fix namespace issue with devices assigned to classes
Kay Sievers [Wed, 14 Mar 2007 02:25:56 +0000 (03:25 +0100)]
  - uses a kset in "struct class" to keep track of all directories
    belonging to this class
  - merges with the /sys/devices/virtual logic.
  - removes the namespace-dir if the last member of that class
    leaves the directory.

There may be locking or refcounting fixes left, I stopped when it seemed
to work with network and sound modules. :)

From: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

drivers/base/class.c
drivers/base/core.c
include/linux/device.h
include/linux/kobject.h
lib/kobject.c
lib/kobject_uevent.c

index d596812..80bbb20 100644 (file)
@@ -145,6 +145,7 @@ int class_register(struct class * cls)
        INIT_LIST_HEAD(&cls->children);
        INIT_LIST_HEAD(&cls->devices);
        INIT_LIST_HEAD(&cls->interfaces);
+       kset_init(&cls->class_dirs);
        init_MUTEX(&cls->sem);
        error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
        if (error)
@@ -163,7 +164,6 @@ int class_register(struct class * cls)
 void class_unregister(struct class * cls)
 {
        pr_debug("device class '%s': unregistering\n", cls->name);
-       kobject_unregister(cls->virtual_dir);
        remove_class_attrs(cls);
        subsystem_unregister(&cls->subsys);
 }
index db3a151..658eae5 100644 (file)
@@ -477,34 +477,58 @@ static struct kobject * get_device_parent(struct device *dev,
        return NULL;
 }
 #else
-static struct kobject * virtual_device_parent(struct device *dev)
+static struct kobject *virtual_device_parent(struct device *dev)
 {
-       if (!dev->class)
-               return ERR_PTR(-ENODEV);
-
-       if (!dev->class->virtual_dir) {
-               static struct kobject *virtual_dir = NULL;
+       static struct kobject *virtual_dir = NULL;
 
-               if (!virtual_dir)
-                       virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
-               dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
-       }
+       if (!virtual_dir)
+               virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
 
-       return dev->class->virtual_dir;
+       return virtual_dir;
 }
 
 static struct kobject * get_device_parent(struct device *dev,
                                          struct device *parent)
 {
-       /* if this is a class device, and has no parent, create one */
-       if ((dev->class) && (parent == NULL)) {
-               return virtual_device_parent(dev);
-       } else if (parent)
+       if (dev->class) {
+               struct kobject *kobj = NULL;
+               struct kobject *parent_kobj;
+               struct kobject *k;
+
+               /*
+                * If we have no parent, we live in "virtual".
+                * Class-devices with a bus-device as parent, live
+                * in a class-directory to prevent namespace collisions.
+                */
+               if (parent == NULL)
+                       parent_kobj = virtual_device_parent(dev);
+               else if (parent->class)
+                       return &parent->kobj;
+               else
+                       parent_kobj = &parent->kobj;
+
+               /* find our class-directory at the parent and reference it */
+               spin_lock(&dev->class->class_dirs.list_lock);
+               list_for_each_entry(k, &dev->class->class_dirs.list, entry)
+                       if (k->parent == parent_kobj) {
+                               kobj = kobject_get(k);
+                               break;
+                       }
+               spin_unlock(&dev->class->class_dirs.list_lock);
+               if (kobj)
+                       return kobj;
+
+               /* or create a new class-directory at the parent device */
+               return kobject_kset_add_dir(&dev->class->class_dirs,
+                                           parent_kobj, dev->class->name);
+       }
+
+       if (parent)
                return &parent->kobj;
        return NULL;
 }
-
 #endif
+
 static int setup_parent(struct device *dev, struct device *parent)
 {
        struct kobject *kobj;
@@ -541,7 +565,6 @@ int device_add(struct device *dev)
        pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
 
        parent = get_device(dev->parent);
-
        error = setup_parent(dev, parent);
        if (error)
                goto Error;
@@ -787,6 +810,31 @@ void device_del(struct device * dev)
                /* remove the device from the class list */
                list_del_init(&dev->node);
                up(&dev->class->sem);
+
+               /* If we live in a parent class-directory, unreference it */
+               if (dev->kobj.parent->kset == &dev->class->class_dirs) {
+                       struct device *d;
+                       int other = 0;
+
+                       /*
+                        * if we are the last child of our class, delete
+                        * our class-directory at this parent
+                        */
+                       down(&dev->class->sem);
+                       list_for_each_entry(d, &dev->class->devices, node) {
+                               if (d == dev)
+                                       continue;
+                               if (d->kobj.parent == dev->kobj.parent) {
+                                       other = 1;
+                                       break;
+                               }
+                       }
+                       if (!other)
+                               kobject_del(dev->kobj.parent);
+
+                       kobject_put(dev->kobj.parent);
+                       up(&dev->class->sem);
+               }
        }
        device_remove_file(dev, &dev->uevent_attr);
        device_remove_groups(dev);
index 5cf30e9..de0e73e 100644 (file)
@@ -181,10 +181,9 @@ struct class {
        struct list_head        children;
        struct list_head        devices;
        struct list_head        interfaces;
+       struct kset             class_dirs;
        struct semaphore        sem;    /* locks both the children and interfaces lists */
 
-       struct kobject          *virtual_dir;
-
        struct class_attribute          * class_attrs;
        struct class_device_attribute   * class_dev_attrs;
        struct device_attribute         * dev_attrs;
index b850e03..d37cd7f 100644 (file)
@@ -89,6 +89,8 @@ extern void kobject_unregister(struct kobject *);
 extern struct kobject * kobject_get(struct kobject *);
 extern void kobject_put(struct kobject *);
 
+extern struct kobject *kobject_kset_add_dir(struct kset *kset,
+                                           struct kobject *, const char *);
 extern struct kobject *kobject_add_dir(struct kobject *, const char *);
 
 extern char * kobject_get_path(struct kobject *, gfp_t);
index 057921c..f664551 100644 (file)
@@ -488,13 +488,15 @@ static struct kobj_type dir_ktype = {
 };
 
 /**
- *     kobject_add_dir - add sub directory of object.
+ *     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.
  *
  *     Add a plain directory object as child of given object.
  */
-struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
+struct kobject *kobject_kset_add_dir(struct kset *kset,
+                                    struct kobject *parent, const char *name)
 {
        struct kobject *k;
        int ret;
@@ -506,6 +508,7 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
        if (!k)
                return NULL;
 
+       k->kset = kset;
        k->parent = parent;
        k->ktype = &dir_ktype;
        kobject_set_name(k, name);
@@ -520,6 +523,11 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
        return k;
 }
 
+struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
+{
+       return kobject_kset_add_dir(NULL, parent, name);
+}
+
 /**
  *     kset_init - initialize a kset for use
  *     @k:     kset 
index 82fc179..4122f38 100644 (file)
@@ -115,6 +115,16 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                        return 0;
                }
 
+       /* originating subsystem */
+       if (uevent_ops && uevent_ops->name)
+               subsystem = uevent_ops->name(kset, kobj);
+       else
+               subsystem = kobject_name(&kset->kobj);
+       if (!subsystem) {
+               pr_debug("unset subsytem caused the event to drop!\n");
+               return 0;
+       }
+
        /* environment index */
        envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
        if (!envp)
@@ -134,12 +144,6 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                goto exit;
        }
 
-       /* originating subsystem */
-       if (uevent_ops && uevent_ops->name)
-               subsystem = uevent_ops->name(kset, kobj);
-       else
-               subsystem = kobject_name(&kset->kobj);
-
        /* event environemnt for helper process only */
        envp[i++] = "HOME=/";
        envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";