Commit c744aeae authored by Cornelia Huck's avatar Cornelia Huck Committed by Greg Kroah-Hartman

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: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 717e48c2
...@@ -390,22 +390,23 @@ void device_initialize(struct device *dev) ...@@ -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) ...@@ -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, ...@@ -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: ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
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