mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-07-05 05:42:36 +00:00
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: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
00ed8e3dda
commit
864062457a
6 changed files with 89 additions and 28 deletions
|
@ -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)
|
||||||
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,
|
||||||
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) {
|
if (!virtual_dir)
|
||||||
static struct kobject *virtual_dir = NULL;
|
virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
|
||||||
|
|
||||||
if (!virtual_dir)
|
return virtual_dir;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
/* 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 {
|
||||||
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 *);
|
||||||
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 = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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)
|
||||||
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)
|
||||||
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,
|
||||||
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,
|
||||||
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";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue