diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2007-01-08 14:16:44 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 13:37:11 -0500 |
commit | c744aeae9d173a953b771a7ad5c872f91fa99dec (patch) | |
tree | 83d36e211ff6d0109f3aeb29d4fd1bb2dbb2a9b3 | |
parent | 717e48c29d9a58f4d31c1651bec364212da5f6b2 (diff) |
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>
-rw-r--r-- | drivers/base/core.c | 76 | ||||
-rw-r--r-- | fs/sysfs/dir.c | 6 | ||||
-rw-r--r-- | lib/kobject.c | 6 |
3 files changed, 52 insertions, 36 deletions
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) | |||
390 | } | 390 | } |
391 | 391 | ||
392 | #ifdef CONFIG_SYSFS_DEPRECATED | 392 | #ifdef CONFIG_SYSFS_DEPRECATED |
393 | static int setup_parent(struct device *dev, struct device *parent) | 393 | static struct kobject * get_device_parent(struct device *dev, |
394 | struct device *parent) | ||
394 | { | 395 | { |
395 | /* Set the parent to the class, not the parent device */ | 396 | /* Set the parent to the class, not the parent device */ |
396 | /* this keeps sysfs from having a symlink to make old udevs happy */ | 397 | /* this keeps sysfs from having a symlink to make old udevs happy */ |
397 | if (dev->class) | 398 | if (dev->class) |
398 | dev->kobj.parent = &dev->class->subsys.kset.kobj; | 399 | return &dev->class->subsys.kset.kobj; |
399 | else if (parent) | 400 | else if (parent) |
400 | dev->kobj.parent = &parent->kobj; | 401 | return &parent->kobj; |
401 | 402 | ||
402 | return 0; | 403 | return NULL; |
403 | } | 404 | } |
404 | #else | 405 | #else |
405 | static int virtual_device_parent(struct device *dev) | 406 | static struct kobject * virtual_device_parent(struct device *dev) |
406 | { | 407 | { |
407 | if (!dev->class) | 408 | if (!dev->class) |
408 | return -ENODEV; | 409 | return ERR_PTR(-ENODEV); |
409 | 410 | ||
410 | if (!dev->class->virtual_dir) { | 411 | if (!dev->class->virtual_dir) { |
411 | static struct kobject *virtual_dir = NULL; | 412 | static struct kobject *virtual_dir = NULL; |
@@ -415,25 +416,31 @@ static int virtual_device_parent(struct device *dev) | |||
415 | dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); | 416 | dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); |
416 | } | 417 | } |
417 | 418 | ||
418 | dev->kobj.parent = dev->class->virtual_dir; | 419 | return dev->class->virtual_dir; |
419 | return 0; | ||
420 | } | 420 | } |
421 | 421 | ||
422 | static int setup_parent(struct device *dev, struct device *parent) | 422 | static struct kobject * get_device_parent(struct device *dev, |
423 | struct device *parent) | ||
423 | { | 424 | { |
424 | int error; | ||
425 | |||
426 | /* if this is a class device, and has no parent, create one */ | 425 | /* if this is a class device, and has no parent, create one */ |
427 | if ((dev->class) && (parent == NULL)) { | 426 | if ((dev->class) && (parent == NULL)) { |
428 | error = virtual_device_parent(dev); | 427 | return virtual_device_parent(dev); |
429 | if (error) | ||
430 | return error; | ||
431 | } else if (parent) | 428 | } else if (parent) |
432 | dev->kobj.parent = &parent->kobj; | 429 | return &parent->kobj; |
430 | return NULL; | ||
431 | } | ||
433 | 432 | ||
433 | #endif | ||
434 | static int setup_parent(struct device *dev, struct device *parent) | ||
435 | { | ||
436 | struct kobject *kobj; | ||
437 | kobj = get_device_parent(dev, parent); | ||
438 | if (IS_ERR(kobj)) | ||
439 | return PTR_ERR(kobj); | ||
440 | if (kobj) | ||
441 | dev->kobj.parent = kobj; | ||
434 | return 0; | 442 | return 0; |
435 | } | 443 | } |
436 | #endif | ||
437 | 444 | ||
438 | /** | 445 | /** |
439 | * device_add - add device to device hierarchy. | 446 | * device_add - add device to device hierarchy. |
@@ -976,12 +983,18 @@ static int device_move_class_links(struct device *dev, | |||
976 | sysfs_remove_link(&dev->kobj, "device"); | 983 | sysfs_remove_link(&dev->kobj, "device"); |
977 | sysfs_remove_link(&old_parent->kobj, class_name); | 984 | sysfs_remove_link(&old_parent->kobj, class_name); |
978 | } | 985 | } |
979 | error = sysfs_create_link(&dev->kobj, &new_parent->kobj, "device"); | 986 | if (new_parent) { |
980 | if (error) | 987 | error = sysfs_create_link(&dev->kobj, &new_parent->kobj, |
981 | goto out; | 988 | "device"); |
982 | error = sysfs_create_link(&new_parent->kobj, &dev->kobj, class_name); | 989 | if (error) |
983 | if (error) | 990 | goto out; |
984 | sysfs_remove_link(&dev->kobj, "device"); | 991 | error = sysfs_create_link(&new_parent->kobj, &dev->kobj, |
992 | class_name); | ||
993 | if (error) | ||
994 | sysfs_remove_link(&dev->kobj, "device"); | ||
995 | } | ||
996 | else | ||
997 | error = 0; | ||
985 | out: | 998 | out: |
986 | kfree(class_name); | 999 | kfree(class_name); |
987 | return error; | 1000 | return error; |
@@ -993,25 +1006,28 @@ out: | |||
993 | /** | 1006 | /** |
994 | * device_move - moves a device to a new parent | 1007 | * device_move - moves a device to a new parent |
995 | * @dev: the pointer to the struct device to be moved | 1008 | * @dev: the pointer to the struct device to be moved |
996 | * @new_parent: the new parent of the device | 1009 | * @new_parent: the new parent of the device (can by NULL) |
997 | */ | 1010 | */ |
998 | int device_move(struct device *dev, struct device *new_parent) | 1011 | int device_move(struct device *dev, struct device *new_parent) |
999 | { | 1012 | { |
1000 | int error; | 1013 | int error; |
1001 | struct device *old_parent; | 1014 | struct device *old_parent; |
1015 | struct kobject *new_parent_kobj; | ||
1002 | 1016 | ||
1003 | dev = get_device(dev); | 1017 | dev = get_device(dev); |
1004 | if (!dev) | 1018 | if (!dev) |
1005 | return -EINVAL; | 1019 | return -EINVAL; |
1006 | 1020 | ||
1007 | new_parent = get_device(new_parent); | 1021 | new_parent = get_device(new_parent); |
1008 | if (!new_parent) { | 1022 | new_parent_kobj = get_device_parent (dev, new_parent); |
1009 | error = -EINVAL; | 1023 | if (IS_ERR(new_parent_kobj)) { |
1024 | error = PTR_ERR(new_parent_kobj); | ||
1025 | put_device(new_parent); | ||
1010 | goto out; | 1026 | goto out; |
1011 | } | 1027 | } |
1012 | pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id, | 1028 | pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id, |
1013 | new_parent->bus_id); | 1029 | new_parent ? new_parent->bus_id : "<NULL>"); |
1014 | error = kobject_move(&dev->kobj, &new_parent->kobj); | 1030 | error = kobject_move(&dev->kobj, new_parent_kobj); |
1015 | if (error) { | 1031 | if (error) { |
1016 | put_device(new_parent); | 1032 | put_device(new_parent); |
1017 | goto out; | 1033 | goto out; |
@@ -1020,7 +1036,8 @@ int device_move(struct device *dev, struct device *new_parent) | |||
1020 | dev->parent = new_parent; | 1036 | dev->parent = new_parent; |
1021 | if (old_parent) | 1037 | if (old_parent) |
1022 | klist_remove(&dev->knode_parent); | 1038 | klist_remove(&dev->knode_parent); |
1023 | klist_add_tail(&dev->knode_parent, &new_parent->klist_children); | 1039 | if (new_parent) |
1040 | klist_add_tail(&dev->knode_parent, &new_parent->klist_children); | ||
1024 | if (!dev->class) | 1041 | if (!dev->class) |
1025 | goto out_put; | 1042 | goto out_put; |
1026 | error = device_move_class_links(dev, old_parent, new_parent); | 1043 | error = device_move_class_links(dev, old_parent, new_parent); |
@@ -1028,7 +1045,8 @@ int device_move(struct device *dev, struct device *new_parent) | |||
1028 | /* We ignore errors on cleanup since we're hosed anyway... */ | 1045 | /* We ignore errors on cleanup since we're hosed anyway... */ |
1029 | device_move_class_links(dev, new_parent, old_parent); | 1046 | device_move_class_links(dev, new_parent, old_parent); |
1030 | if (!kobject_move(&dev->kobj, &old_parent->kobj)) { | 1047 | if (!kobject_move(&dev->kobj, &old_parent->kobj)) { |
1031 | klist_remove(&dev->knode_parent); | 1048 | if (new_parent) |
1049 | klist_remove(&dev->knode_parent); | ||
1032 | if (old_parent) | 1050 | if (old_parent) |
1033 | klist_add_tail(&dev->knode_parent, | 1051 | klist_add_tail(&dev->knode_parent, |
1034 | &old_parent->klist_children); | 1052 | &old_parent->klist_children); |
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 511edef8b321..2bab1b4ddf5a 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -378,12 +378,10 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent) | |||
378 | struct sysfs_dirent *new_parent_sd, *sd; | 378 | struct sysfs_dirent *new_parent_sd, *sd; |
379 | int error; | 379 | int error; |
380 | 380 | ||
381 | if (!new_parent) | ||
382 | return -EINVAL; | ||
383 | |||
384 | old_parent_dentry = kobj->parent ? | 381 | old_parent_dentry = kobj->parent ? |
385 | kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; | 382 | kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; |
386 | new_parent_dentry = new_parent->dentry; | 383 | new_parent_dentry = new_parent ? |
384 | new_parent->dentry : sysfs_mount->mnt_sb->s_root; | ||
387 | 385 | ||
388 | again: | 386 | again: |
389 | mutex_lock(&old_parent_dentry->d_inode->i_mutex); | 387 | mutex_lock(&old_parent_dentry->d_inode->i_mutex); |
diff --git a/lib/kobject.c b/lib/kobject.c index 9aed594bfcac..c033dc8fa9af 100644 --- a/lib/kobject.c +++ b/lib/kobject.c | |||
@@ -314,7 +314,7 @@ int kobject_rename(struct kobject * kobj, const char *new_name) | |||
314 | /** | 314 | /** |
315 | * kobject_move - move object to another parent | 315 | * kobject_move - move object to another parent |
316 | * @kobj: object in question. | 316 | * @kobj: object in question. |
317 | * @new_parent: object's new parent | 317 | * @new_parent: object's new parent (can be NULL) |
318 | */ | 318 | */ |
319 | 319 | ||
320 | int kobject_move(struct kobject *kobj, struct kobject *new_parent) | 320 | 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 | return -EINVAL; | 330 | return -EINVAL; |
331 | new_parent = kobject_get(new_parent); | 331 | new_parent = kobject_get(new_parent); |
332 | if (!new_parent) { | 332 | if (!new_parent) { |
333 | error = -EINVAL; | 333 | if (kobj->kset) |
334 | goto out; | 334 | new_parent = kobject_get(&kobj->kset->kobj); |
335 | } | 335 | } |
336 | /* old object path */ | 336 | /* old object path */ |
337 | devpath = kobject_get_path(kobj, GFP_KERNEL); | 337 | devpath = kobject_get_path(kobj, GFP_KERNEL); |