driver core: Allow device_move(dev, NULL).
If we allow NULL as the new parent in device_move(), we need to make sure that the device is placed into the same place as it would if it was newly registered: - Consider the device virtual tree. In order to be able to reuse code, setup_parent() has been tweaked a bit. - kobject_move() can fall back to the kset's kobject. - sysfs_move_dir() uses the sysfs root dir as fallback. Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Cc: Marcel Holtmann <marcel@holtmann.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
717e48c29d
commit
c744aeae9d
@ -390,22 +390,23 @@ void device_initialize(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SYSFS_DEPRECATED
|
#ifdef CONFIG_SYSFS_DEPRECATED
|
||||||
static int setup_parent(struct device *dev, struct device *parent)
|
static struct kobject * get_device_parent(struct device *dev,
|
||||||
|
struct device *parent)
|
||||||
{
|
{
|
||||||
/* Set the parent to the class, not the parent device */
|
/* Set the parent to the class, not the parent device */
|
||||||
/* this keeps sysfs from having a symlink to make old udevs happy */
|
/* this keeps sysfs from having a symlink to make old udevs happy */
|
||||||
if (dev->class)
|
if (dev->class)
|
||||||
dev->kobj.parent = &dev->class->subsys.kset.kobj;
|
return &dev->class->subsys.kset.kobj;
|
||||||
else if (parent)
|
else if (parent)
|
||||||
dev->kobj.parent = &parent->kobj;
|
return &parent->kobj;
|
||||||
|
|
||||||
return 0;
|
return NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static int virtual_device_parent(struct device *dev)
|
static struct kobject * virtual_device_parent(struct device *dev)
|
||||||
{
|
{
|
||||||
if (!dev->class)
|
if (!dev->class)
|
||||||
return -ENODEV;
|
return ERR_PTR(-ENODEV);
|
||||||
|
|
||||||
if (!dev->class->virtual_dir) {
|
if (!dev->class->virtual_dir) {
|
||||||
static struct kobject *virtual_dir = NULL;
|
static struct kobject *virtual_dir = NULL;
|
||||||
@ -415,25 +416,31 @@ static int virtual_device_parent(struct device *dev)
|
|||||||
dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
|
dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->kobj.parent = dev->class->virtual_dir;
|
return dev->class->virtual_dir;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int setup_parent(struct device *dev, struct device *parent)
|
static struct kobject * get_device_parent(struct device *dev,
|
||||||
|
struct device *parent)
|
||||||
{
|
{
|
||||||
int error;
|
|
||||||
|
|
||||||
/* if this is a class device, and has no parent, create one */
|
/* if this is a class device, and has no parent, create one */
|
||||||
if ((dev->class) && (parent == NULL)) {
|
if ((dev->class) && (parent == NULL)) {
|
||||||
error = virtual_device_parent(dev);
|
return virtual_device_parent(dev);
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
} else if (parent)
|
} else if (parent)
|
||||||
dev->kobj.parent = &parent->kobj;
|
return &parent->kobj;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
static int setup_parent(struct device *dev, struct device *parent)
|
||||||
|
{
|
||||||
|
struct kobject *kobj;
|
||||||
|
kobj = get_device_parent(dev, parent);
|
||||||
|
if (IS_ERR(kobj))
|
||||||
|
return PTR_ERR(kobj);
|
||||||
|
if (kobj)
|
||||||
|
dev->kobj.parent = kobj;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* device_add - add device to device hierarchy.
|
* device_add - add device to device hierarchy.
|
||||||
@ -976,12 +983,18 @@ static int device_move_class_links(struct device *dev,
|
|||||||
sysfs_remove_link(&dev->kobj, "device");
|
sysfs_remove_link(&dev->kobj, "device");
|
||||||
sysfs_remove_link(&old_parent->kobj, class_name);
|
sysfs_remove_link(&old_parent->kobj, class_name);
|
||||||
}
|
}
|
||||||
error = sysfs_create_link(&dev->kobj, &new_parent->kobj, "device");
|
if (new_parent) {
|
||||||
|
error = sysfs_create_link(&dev->kobj, &new_parent->kobj,
|
||||||
|
"device");
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
error = sysfs_create_link(&new_parent->kobj, &dev->kobj, class_name);
|
error = sysfs_create_link(&new_parent->kobj, &dev->kobj,
|
||||||
|
class_name);
|
||||||
if (error)
|
if (error)
|
||||||
sysfs_remove_link(&dev->kobj, "device");
|
sysfs_remove_link(&dev->kobj, "device");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = 0;
|
||||||
out:
|
out:
|
||||||
kfree(class_name);
|
kfree(class_name);
|
||||||
return error;
|
return error;
|
||||||
@ -993,25 +1006,28 @@ out:
|
|||||||
/**
|
/**
|
||||||
* device_move - moves a device to a new parent
|
* device_move - moves a device to a new parent
|
||||||
* @dev: the pointer to the struct device to be moved
|
* @dev: the pointer to the struct device to be moved
|
||||||
* @new_parent: the new parent of the device
|
* @new_parent: the new parent of the device (can by NULL)
|
||||||
*/
|
*/
|
||||||
int device_move(struct device *dev, struct device *new_parent)
|
int device_move(struct device *dev, struct device *new_parent)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
struct device *old_parent;
|
struct device *old_parent;
|
||||||
|
struct kobject *new_parent_kobj;
|
||||||
|
|
||||||
dev = get_device(dev);
|
dev = get_device(dev);
|
||||||
if (!dev)
|
if (!dev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
new_parent = get_device(new_parent);
|
new_parent = get_device(new_parent);
|
||||||
if (!new_parent) {
|
new_parent_kobj = get_device_parent (dev, new_parent);
|
||||||
error = -EINVAL;
|
if (IS_ERR(new_parent_kobj)) {
|
||||||
|
error = PTR_ERR(new_parent_kobj);
|
||||||
|
put_device(new_parent);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id,
|
pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id,
|
||||||
new_parent->bus_id);
|
new_parent ? new_parent->bus_id : "<NULL>");
|
||||||
error = kobject_move(&dev->kobj, &new_parent->kobj);
|
error = kobject_move(&dev->kobj, new_parent_kobj);
|
||||||
if (error) {
|
if (error) {
|
||||||
put_device(new_parent);
|
put_device(new_parent);
|
||||||
goto out;
|
goto out;
|
||||||
@ -1020,6 +1036,7 @@ int device_move(struct device *dev, struct device *new_parent)
|
|||||||
dev->parent = new_parent;
|
dev->parent = new_parent;
|
||||||
if (old_parent)
|
if (old_parent)
|
||||||
klist_remove(&dev->knode_parent);
|
klist_remove(&dev->knode_parent);
|
||||||
|
if (new_parent)
|
||||||
klist_add_tail(&dev->knode_parent, &new_parent->klist_children);
|
klist_add_tail(&dev->knode_parent, &new_parent->klist_children);
|
||||||
if (!dev->class)
|
if (!dev->class)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
@ -1028,6 +1045,7 @@ int device_move(struct device *dev, struct device *new_parent)
|
|||||||
/* We ignore errors on cleanup since we're hosed anyway... */
|
/* We ignore errors on cleanup since we're hosed anyway... */
|
||||||
device_move_class_links(dev, new_parent, old_parent);
|
device_move_class_links(dev, new_parent, old_parent);
|
||||||
if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
|
if (!kobject_move(&dev->kobj, &old_parent->kobj)) {
|
||||||
|
if (new_parent)
|
||||||
klist_remove(&dev->knode_parent);
|
klist_remove(&dev->knode_parent);
|
||||||
if (old_parent)
|
if (old_parent)
|
||||||
klist_add_tail(&dev->knode_parent,
|
klist_add_tail(&dev->knode_parent,
|
||||||
|
@ -378,12 +378,10 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent)
|
|||||||
struct sysfs_dirent *new_parent_sd, *sd;
|
struct sysfs_dirent *new_parent_sd, *sd;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (!new_parent)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
old_parent_dentry = kobj->parent ?
|
old_parent_dentry = kobj->parent ?
|
||||||
kobj->parent->dentry : sysfs_mount->mnt_sb->s_root;
|
kobj->parent->dentry : sysfs_mount->mnt_sb->s_root;
|
||||||
new_parent_dentry = new_parent->dentry;
|
new_parent_dentry = new_parent ?
|
||||||
|
new_parent->dentry : sysfs_mount->mnt_sb->s_root;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
mutex_lock(&old_parent_dentry->d_inode->i_mutex);
|
mutex_lock(&old_parent_dentry->d_inode->i_mutex);
|
||||||
|
@ -314,7 +314,7 @@ int kobject_rename(struct kobject * kobj, const char *new_name)
|
|||||||
/**
|
/**
|
||||||
* kobject_move - move object to another parent
|
* kobject_move - move object to another parent
|
||||||
* @kobj: object in question.
|
* @kobj: object in question.
|
||||||
* @new_parent: object's new parent
|
* @new_parent: object's new parent (can be NULL)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int kobject_move(struct kobject *kobj, struct kobject *new_parent)
|
int kobject_move(struct kobject *kobj, struct kobject *new_parent)
|
||||||
@ -330,8 +330,8 @@ int kobject_move(struct kobject *kobj, struct kobject *new_parent)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
new_parent = kobject_get(new_parent);
|
new_parent = kobject_get(new_parent);
|
||||||
if (!new_parent) {
|
if (!new_parent) {
|
||||||
error = -EINVAL;
|
if (kobj->kset)
|
||||||
goto out;
|
new_parent = kobject_get(&kobj->kset->kobj);
|
||||||
}
|
}
|
||||||
/* old object path */
|
/* old object path */
|
||||||
devpath = kobject_get_path(kobj, GFP_KERNEL);
|
devpath = kobject_get_path(kobj, GFP_KERNEL);
|
||||||
|
Loading…
Reference in New Issue
Block a user