Commit 86406245 authored by Kay Sievers's avatar Kay Sievers Committed by Greg Kroah-Hartman

driver core: fix namespace issue with devices assigned to classes

  - 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: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 00ed8e3d
...@@ -145,6 +145,7 @@ int class_register(struct class * cls) ...@@ -145,6 +145,7 @@ int class_register(struct class * cls)
INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->children);
INIT_LIST_HEAD(&cls->devices); INIT_LIST_HEAD(&cls->devices);
INIT_LIST_HEAD(&cls->interfaces); INIT_LIST_HEAD(&cls->interfaces);
kset_init(&cls->class_dirs);
init_MUTEX(&cls->sem); init_MUTEX(&cls->sem);
error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
if (error) if (error)
...@@ -163,7 +164,6 @@ int class_register(struct class * cls) ...@@ -163,7 +164,6 @@ int class_register(struct class * cls)
void class_unregister(struct class * cls) void class_unregister(struct class * cls)
{ {
pr_debug("device class '%s': unregistering\n", cls->name); pr_debug("device class '%s': unregistering\n", cls->name);
kobject_unregister(cls->virtual_dir);
remove_class_attrs(cls); remove_class_attrs(cls);
subsystem_unregister(&cls->subsys); subsystem_unregister(&cls->subsys);
} }
......
...@@ -477,34 +477,58 @@ static struct kobject * get_device_parent(struct device *dev, ...@@ -477,34 +477,58 @@ static struct kobject * get_device_parent(struct device *dev,
return NULL; return NULL;
} }
#else #else
static struct kobject * virtual_device_parent(struct device *dev) static struct kobject *virtual_device_parent(struct device *dev)
{ {
if (!dev->class) static struct kobject *virtual_dir = NULL;
return ERR_PTR(-ENODEV);
if (!dev->class->virtual_dir) {
static struct kobject *virtual_dir = NULL;
if (!virtual_dir) if (!virtual_dir)
virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
}
return dev->class->virtual_dir; return virtual_dir;
} }
static struct kobject * get_device_parent(struct device *dev, static struct kobject * get_device_parent(struct device *dev,
struct device *parent) struct device *parent)
{ {
/* if this is a class device, and has no parent, create one */ if (dev->class) {
if ((dev->class) && (parent == NULL)) { struct kobject *kobj = NULL;
return virtual_device_parent(dev); struct kobject *parent_kobj;
} else if (parent) 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 &parent->kobj;
return NULL; return NULL;
} }
#endif #endif
static int setup_parent(struct device *dev, struct device *parent) static int setup_parent(struct device *dev, struct device *parent)
{ {
struct kobject *kobj; struct kobject *kobj;
...@@ -541,7 +565,6 @@ int device_add(struct device *dev) ...@@ -541,7 +565,6 @@ int device_add(struct device *dev)
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
parent = get_device(dev->parent); parent = get_device(dev->parent);
error = setup_parent(dev, parent); error = setup_parent(dev, parent);
if (error) if (error)
goto Error; goto Error;
...@@ -787,6 +810,31 @@ void device_del(struct device * dev) ...@@ -787,6 +810,31 @@ void device_del(struct device * dev)
/* remove the device from the class list */ /* remove the device from the class list */
list_del_init(&dev->node); list_del_init(&dev->node);
up(&dev->class->sem); 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_file(dev, &dev->uevent_attr);
device_remove_groups(dev); device_remove_groups(dev);
......
...@@ -181,10 +181,9 @@ struct class { ...@@ -181,10 +181,9 @@ struct class {
struct list_head children; struct list_head children;
struct list_head devices; struct list_head devices;
struct list_head interfaces; struct list_head interfaces;
struct kset class_dirs;
struct semaphore sem; /* locks both the children and interfaces lists */ struct semaphore sem; /* locks both the children and interfaces lists */
struct kobject *virtual_dir;
struct class_attribute * class_attrs; struct class_attribute * class_attrs;
struct class_device_attribute * class_dev_attrs; struct class_device_attribute * class_dev_attrs;
struct device_attribute * dev_attrs; struct device_attribute * dev_attrs;
......
...@@ -89,6 +89,8 @@ extern void kobject_unregister(struct kobject *); ...@@ -89,6 +89,8 @@ extern void kobject_unregister(struct kobject *);
extern struct kobject * kobject_get(struct kobject *); extern struct kobject * kobject_get(struct kobject *);
extern void kobject_put(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 struct kobject *kobject_add_dir(struct kobject *, const char *);
extern char * kobject_get_path(struct kobject *, gfp_t); extern char * kobject_get_path(struct kobject *, gfp_t);
......
...@@ -488,13 +488,15 @@ static struct kobj_type dir_ktype = { ...@@ -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. * @parent: object in which a directory is created.
* @name: directory name. * @name: directory name.
* *
* Add a plain directory object as child of given object. * 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; struct kobject *k;
int ret; int ret;
...@@ -506,6 +508,7 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name) ...@@ -506,6 +508,7 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
if (!k) if (!k)
return NULL; return NULL;
k->kset = kset;
k->parent = parent; k->parent = parent;
k->ktype = &dir_ktype; k->ktype = &dir_ktype;
kobject_set_name(k, name); kobject_set_name(k, name);
...@@ -520,6 +523,11 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name) ...@@ -520,6 +523,11 @@ struct kobject *kobject_add_dir(struct kobject *parent, const char *name)
return k; 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 * kset_init - initialize a kset for use
* @k: kset * @k: kset
......
...@@ -115,6 +115,16 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, ...@@ -115,6 +115,16 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
return 0; 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 */ /* environment index */
envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
if (!envp) if (!envp)
...@@ -134,12 +144,6 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, ...@@ -134,12 +144,6 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
goto exit; 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 */ /* event environemnt for helper process only */
envp[i++] = "HOME=/"; envp[i++] = "HOME=/";
envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment