From c744aeae9d173a953b771a7ad5c872f91fa99dec Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 8 Jan 2007 20:16:44 +0100 Subject: 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 Cc: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 76 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 29 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 36eedfdeb547..c60114dbe5f5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -390,22 +390,23 @@ void device_initialize(struct device *dev) } #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 */ /* this keeps sysfs from having a symlink to make old udevs happy */ if (dev->class) - dev->kobj.parent = &dev->class->subsys.kset.kobj; + return &dev->class->subsys.kset.kobj; else if (parent) - dev->kobj.parent = &parent->kobj; + return &parent->kobj; - return 0; + return NULL; } #else -static int virtual_device_parent(struct device *dev) +static struct kobject * virtual_device_parent(struct device *dev) { if (!dev->class) - return -ENODEV; + return ERR_PTR(-ENODEV); if (!dev->class->virtual_dir) { 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->kobj.parent = dev->class->virtual_dir; - return 0; + return dev->class->virtual_dir; } -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 ((dev->class) && (parent == NULL)) { - error = virtual_device_parent(dev); - if (error) - return error; + return virtual_device_parent(dev); } 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; } -#endif /** * 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(&old_parent->kobj, class_name); } - error = sysfs_create_link(&dev->kobj, &new_parent->kobj, "device"); - if (error) - goto out; - error = sysfs_create_link(&new_parent->kobj, &dev->kobj, class_name); - if (error) - sysfs_remove_link(&dev->kobj, "device"); + if (new_parent) { + error = sysfs_create_link(&dev->kobj, &new_parent->kobj, + "device"); + if (error) + goto out; + error = sysfs_create_link(&new_parent->kobj, &dev->kobj, + class_name); + if (error) + sysfs_remove_link(&dev->kobj, "device"); + } + else + error = 0; out: kfree(class_name); return error; @@ -993,25 +1006,28 @@ out: /** * device_move - moves a device to a new parent * @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 error; struct device *old_parent; + struct kobject *new_parent_kobj; dev = get_device(dev); if (!dev) return -EINVAL; new_parent = get_device(new_parent); - if (!new_parent) { - error = -EINVAL; + new_parent_kobj = get_device_parent (dev, new_parent); + if (IS_ERR(new_parent_kobj)) { + error = PTR_ERR(new_parent_kobj); + put_device(new_parent); goto out; } pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id, - new_parent->bus_id); - error = kobject_move(&dev->kobj, &new_parent->kobj); + new_parent ? new_parent->bus_id : ""); + error = kobject_move(&dev->kobj, new_parent_kobj); if (error) { put_device(new_parent); goto out; @@ -1020,7 +1036,8 @@ int device_move(struct device *dev, struct device *new_parent) dev->parent = new_parent; if (old_parent) klist_remove(&dev->knode_parent); - klist_add_tail(&dev->knode_parent, &new_parent->klist_children); + if (new_parent) + klist_add_tail(&dev->knode_parent, &new_parent->klist_children); if (!dev->class) goto out_put; error = device_move_class_links(dev, old_parent, new_parent); @@ -1028,7 +1045,8 @@ int device_move(struct device *dev, struct device *new_parent) /* We ignore errors on cleanup since we're hosed anyway... */ device_move_class_links(dev, new_parent, old_parent); if (!kobject_move(&dev->kobj, &old_parent->kobj)) { - klist_remove(&dev->knode_parent); + if (new_parent) + klist_remove(&dev->knode_parent); if (old_parent) klist_add_tail(&dev->knode_parent, &old_parent->klist_children); -- cgit v1.2.3-59-g8ed1b