diff options
| author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2006-11-20 11:07:51 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-12-01 17:52:01 -0500 |
| commit | 8a82472f86bf693b8e91ed56c9ca4f62fbbdcfa3 (patch) | |
| tree | 79d148ee548f4b57e6f5a4a69cf6cdb81e7a1bf2 | |
| parent | af9e0765362151b27372c14d9d6dc417184182d3 (diff) | |
driver core: Introduce device_move(): move a device to a new parent.
Provide a function device_move() to move a device to a new parent device. Add
auxilliary functions kobject_move() and sysfs_move_dir().
kobject_move() generates a new uevent of type KOBJ_MOVE, containing the
previous path (DEVPATH_OLD) in addition to the usual values. For this, a new
interface kobject_uevent_env() is created that allows to add further
environmental data to the uevent at the kobject layer.
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Acked-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/base/core.c | 92 | ||||
| -rw-r--r-- | fs/sysfs/dir.c | 45 | ||||
| -rw-r--r-- | include/linux/device.h | 1 | ||||
| -rw-r--r-- | include/linux/kobject.h | 8 | ||||
| -rw-r--r-- | include/linux/sysfs.h | 8 | ||||
| -rw-r--r-- | lib/kobject.c | 50 | ||||
| -rw-r--r-- | lib/kobject_uevent.c | 28 |
7 files changed, 228 insertions, 4 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 75b45a10935a..e4eaf46c4d93 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
| @@ -955,3 +955,95 @@ int device_rename(struct device *dev, char *new_name) | |||
| 955 | 955 | ||
| 956 | return error; | 956 | return error; |
| 957 | } | 957 | } |
| 958 | |||
| 959 | |||
| 960 | static int device_move_class_links(struct device *dev, | ||
| 961 | struct device *old_parent, | ||
| 962 | struct device *new_parent) | ||
| 963 | { | ||
| 964 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
| 965 | int error; | ||
| 966 | char *class_name; | ||
| 967 | |||
| 968 | class_name = make_class_name(dev->class->name, &dev->kobj); | ||
| 969 | if (!class_name) { | ||
| 970 | error = PTR_ERR(class_name); | ||
| 971 | class_name = NULL; | ||
| 972 | goto out; | ||
| 973 | } | ||
| 974 | if (old_parent) { | ||
| 975 | sysfs_remove_link(&dev->kobj, "device"); | ||
| 976 | sysfs_remove_link(&old_parent->kobj, class_name); | ||
| 977 | } | ||
| 978 | error = sysfs_create_link(&dev->kobj, &new_parent->kobj, "device"); | ||
| 979 | if (error) | ||
| 980 | goto out; | ||
| 981 | error = sysfs_create_link(&new_parent->kobj, &dev->kobj, class_name); | ||
| 982 | if (error) | ||
| 983 | sysfs_remove_link(&dev->kobj, "device"); | ||
| 984 | out: | ||
| 985 | kfree(class_name); | ||
| 986 | return error; | ||
| 987 | #else | ||
| 988 | return 0; | ||
| 989 | #endif | ||
| 990 | } | ||
| 991 | |||
| 992 | /** | ||
| 993 | * device_move - moves a device to a new parent | ||
| 994 | * @dev: the pointer to the struct device to be moved | ||
| 995 | * @new_parent: the new parent of the device | ||
| 996 | */ | ||
| 997 | int device_move(struct device *dev, struct device *new_parent) | ||
| 998 | { | ||
| 999 | int error; | ||
| 1000 | struct device *old_parent; | ||
| 1001 | |||
| 1002 | dev = get_device(dev); | ||
| 1003 | if (!dev) | ||
| 1004 | return -EINVAL; | ||
| 1005 | |||
| 1006 | if (!device_is_registered(dev)) { | ||
| 1007 | error = -EINVAL; | ||
| 1008 | goto out; | ||
| 1009 | } | ||
| 1010 | new_parent = get_device(new_parent); | ||
| 1011 | if (!new_parent) { | ||
| 1012 | error = -EINVAL; | ||
| 1013 | goto out; | ||
| 1014 | } | ||
| 1015 | pr_debug("DEVICE: moving '%s' to '%s'\n", dev->bus_id, | ||
| 1016 | new_parent->bus_id); | ||
| 1017 | error = kobject_move(&dev->kobj, &new_parent->kobj); | ||
| 1018 | if (error) { | ||
| 1019 | put_device(new_parent); | ||
| 1020 | goto out; | ||
| 1021 | } | ||
| 1022 | old_parent = dev->parent; | ||
| 1023 | dev->parent = new_parent; | ||
| 1024 | if (old_parent) | ||
| 1025 | klist_del(&dev->knode_parent); | ||
| 1026 | klist_add_tail(&dev->knode_parent, &new_parent->klist_children); | ||
| 1027 | if (!dev->class) | ||
| 1028 | goto out_put; | ||
| 1029 | error = device_move_class_links(dev, old_parent, new_parent); | ||
| 1030 | if (error) { | ||
| 1031 | /* We ignore errors on cleanup since we're hosed anyway... */ | ||
| 1032 | device_move_class_links(dev, new_parent, old_parent); | ||
| 1033 | if (!kobject_move(&dev->kobj, &old_parent->kobj)) { | ||
| 1034 | klist_del(&dev->knode_parent); | ||
| 1035 | if (old_parent) | ||
| 1036 | klist_add_tail(&dev->knode_parent, | ||
| 1037 | &old_parent->klist_children); | ||
| 1038 | } | ||
| 1039 | put_device(new_parent); | ||
| 1040 | goto out; | ||
| 1041 | } | ||
| 1042 | out_put: | ||
| 1043 | put_device(old_parent); | ||
| 1044 | out: | ||
| 1045 | put_device(dev); | ||
| 1046 | return error; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | EXPORT_SYMBOL_GPL(device_move); | ||
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 3aa3434621ca..a5782e8c7f07 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
| @@ -372,6 +372,51 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name) | |||
| 372 | return error; | 372 | return error; |
| 373 | } | 373 | } |
| 374 | 374 | ||
| 375 | int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent) | ||
| 376 | { | ||
| 377 | struct dentry *old_parent_dentry, *new_parent_dentry, *new_dentry; | ||
| 378 | struct sysfs_dirent *new_parent_sd, *sd; | ||
| 379 | int error; | ||
| 380 | |||
| 381 | if (!new_parent) | ||
| 382 | return -EINVAL; | ||
| 383 | |||
| 384 | old_parent_dentry = kobj->parent ? | ||
| 385 | kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; | ||
| 386 | new_parent_dentry = new_parent->dentry; | ||
| 387 | |||
| 388 | again: | ||
| 389 | mutex_lock(&old_parent_dentry->d_inode->i_mutex); | ||
| 390 | if (!mutex_trylock(&new_parent_dentry->d_inode->i_mutex)) { | ||
| 391 | mutex_unlock(&old_parent_dentry->d_inode->i_mutex); | ||
| 392 | goto again; | ||
| 393 | } | ||
| 394 | |||
| 395 | new_parent_sd = new_parent_dentry->d_fsdata; | ||
| 396 | sd = kobj->dentry->d_fsdata; | ||
| 397 | |||
| 398 | new_dentry = lookup_one_len(kobj->name, new_parent_dentry, | ||
| 399 | strlen(kobj->name)); | ||
| 400 | if (IS_ERR(new_dentry)) { | ||
| 401 | error = PTR_ERR(new_dentry); | ||
| 402 | goto out; | ||
| 403 | } else | ||
| 404 | error = 0; | ||
| 405 | d_add(new_dentry, NULL); | ||
| 406 | d_move(kobj->dentry, new_dentry); | ||
| 407 | dput(new_dentry); | ||
| 408 | |||
| 409 | /* Remove from old parent's list and insert into new parent's list. */ | ||
| 410 | list_del_init(&sd->s_sibling); | ||
| 411 | list_add(&sd->s_sibling, &new_parent_sd->s_children); | ||
| 412 | |||
| 413 | out: | ||
| 414 | mutex_unlock(&new_parent_dentry->d_inode->i_mutex); | ||
| 415 | mutex_unlock(&old_parent_dentry->d_inode->i_mutex); | ||
| 416 | |||
| 417 | return error; | ||
| 418 | } | ||
| 419 | |||
| 375 | static int sysfs_dir_open(struct inode *inode, struct file *file) | 420 | static int sysfs_dir_open(struct inode *inode, struct file *file) |
| 376 | { | 421 | { |
| 377 | struct dentry * dentry = file->f_dentry; | 422 | struct dentry * dentry = file->f_dentry; |
diff --git a/include/linux/device.h b/include/linux/device.h index 0a0370c74181..583a341e016c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h | |||
| @@ -424,6 +424,7 @@ extern int device_for_each_child(struct device *, void *, | |||
| 424 | extern struct device *device_find_child(struct device *, void *data, | 424 | extern struct device *device_find_child(struct device *, void *data, |
| 425 | int (*match)(struct device *, void *)); | 425 | int (*match)(struct device *, void *)); |
| 426 | extern int device_rename(struct device *dev, char *new_name); | 426 | extern int device_rename(struct device *dev, char *new_name); |
| 427 | extern int device_move(struct device *dev, struct device *new_parent); | ||
| 427 | 428 | ||
| 428 | /* | 429 | /* |
| 429 | * Manual binding of a device to driver. See drivers/base/bus.c | 430 | * Manual binding of a device to driver. See drivers/base/bus.c |
diff --git a/include/linux/kobject.h b/include/linux/kobject.h index bcd9cd173c2c..d1c8d28fa92e 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h | |||
| @@ -47,6 +47,7 @@ enum kobject_action { | |||
| 47 | KOBJ_UMOUNT = (__force kobject_action_t) 0x05, /* umount event for block devices (broken) */ | 47 | KOBJ_UMOUNT = (__force kobject_action_t) 0x05, /* umount event for block devices (broken) */ |
| 48 | KOBJ_OFFLINE = (__force kobject_action_t) 0x06, /* device offline */ | 48 | KOBJ_OFFLINE = (__force kobject_action_t) 0x06, /* device offline */ |
| 49 | KOBJ_ONLINE = (__force kobject_action_t) 0x07, /* device online */ | 49 | KOBJ_ONLINE = (__force kobject_action_t) 0x07, /* device online */ |
| 50 | KOBJ_MOVE = (__force kobject_action_t) 0x08, /* device move */ | ||
| 50 | }; | 51 | }; |
| 51 | 52 | ||
| 52 | struct kobject { | 53 | struct kobject { |
| @@ -76,6 +77,7 @@ extern int __must_check kobject_add(struct kobject *); | |||
| 76 | extern void kobject_del(struct kobject *); | 77 | extern void kobject_del(struct kobject *); |
| 77 | 78 | ||
| 78 | extern int __must_check kobject_rename(struct kobject *, const char *new_name); | 79 | extern int __must_check kobject_rename(struct kobject *, const char *new_name); |
| 80 | extern int __must_check kobject_move(struct kobject *, struct kobject *); | ||
| 79 | 81 | ||
| 80 | extern int __must_check kobject_register(struct kobject *); | 82 | extern int __must_check kobject_register(struct kobject *); |
| 81 | extern void kobject_unregister(struct kobject *); | 83 | extern void kobject_unregister(struct kobject *); |
| @@ -264,6 +266,8 @@ extern int __must_check subsys_create_file(struct subsystem * , | |||
| 264 | 266 | ||
| 265 | #if defined(CONFIG_HOTPLUG) | 267 | #if defined(CONFIG_HOTPLUG) |
| 266 | void kobject_uevent(struct kobject *kobj, enum kobject_action action); | 268 | void kobject_uevent(struct kobject *kobj, enum kobject_action action); |
| 269 | void kobject_uevent_env(struct kobject *kobj, enum kobject_action action, | ||
| 270 | char *envp[]); | ||
| 267 | 271 | ||
| 268 | int add_uevent_var(char **envp, int num_envp, int *cur_index, | 272 | int add_uevent_var(char **envp, int num_envp, int *cur_index, |
| 269 | char *buffer, int buffer_size, int *cur_len, | 273 | char *buffer, int buffer_size, int *cur_len, |
| @@ -271,6 +275,10 @@ int add_uevent_var(char **envp, int num_envp, int *cur_index, | |||
| 271 | __attribute__((format (printf, 7, 8))); | 275 | __attribute__((format (printf, 7, 8))); |
| 272 | #else | 276 | #else |
| 273 | static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { } | 277 | static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { } |
| 278 | static inline void kobject_uevent_env(struct kobject *kobj, | ||
| 279 | enum kobject_action action, | ||
| 280 | char *envp[]) | ||
| 281 | { } | ||
| 274 | 282 | ||
| 275 | static inline int add_uevent_var(char **envp, int num_envp, int *cur_index, | 283 | static inline int add_uevent_var(char **envp, int num_envp, int *cur_index, |
| 276 | char *buffer, int buffer_size, int *cur_len, | 284 | char *buffer, int buffer_size, int *cur_len, |
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 6d5c43d31dec..2129d1b6c874 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h | |||
| @@ -97,6 +97,9 @@ extern int __must_check | |||
| 97 | sysfs_rename_dir(struct kobject *, const char *new_name); | 97 | sysfs_rename_dir(struct kobject *, const char *new_name); |
| 98 | 98 | ||
| 99 | extern int __must_check | 99 | extern int __must_check |
| 100 | sysfs_move_dir(struct kobject *, struct kobject *); | ||
| 101 | |||
| 102 | extern int __must_check | ||
| 100 | sysfs_create_file(struct kobject *, const struct attribute *); | 103 | sysfs_create_file(struct kobject *, const struct attribute *); |
| 101 | 104 | ||
| 102 | extern int __must_check | 105 | extern int __must_check |
| @@ -142,6 +145,11 @@ static inline int sysfs_rename_dir(struct kobject * k, const char *new_name) | |||
| 142 | return 0; | 145 | return 0; |
| 143 | } | 146 | } |
| 144 | 147 | ||
| 148 | static inline int sysfs_move_dir(struct kobject * k, struct kobject * new_parent) | ||
| 149 | { | ||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 145 | static inline int sysfs_create_file(struct kobject * k, const struct attribute * a) | 153 | static inline int sysfs_create_file(struct kobject * k, const struct attribute * a) |
| 146 | { | 154 | { |
| 147 | return 0; | 155 | return 0; |
diff --git a/lib/kobject.c b/lib/kobject.c index 7dd5c0e9d996..744a4b102c7f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c | |||
| @@ -311,6 +311,56 @@ int kobject_rename(struct kobject * kobj, const char *new_name) | |||
| 311 | } | 311 | } |
| 312 | 312 | ||
| 313 | /** | 313 | /** |
| 314 | * kobject_move - move object to another parent | ||
| 315 | * @kobj: object in question. | ||
| 316 | * @new_parent: object's new parent | ||
| 317 | */ | ||
| 318 | |||
| 319 | int kobject_move(struct kobject *kobj, struct kobject *new_parent) | ||
| 320 | { | ||
| 321 | int error; | ||
| 322 | struct kobject *old_parent; | ||
| 323 | const char *devpath = NULL; | ||
| 324 | char *devpath_string = NULL; | ||
| 325 | char *envp[2]; | ||
| 326 | |||
| 327 | kobj = kobject_get(kobj); | ||
| 328 | if (!kobj) | ||
| 329 | return -EINVAL; | ||
| 330 | new_parent = kobject_get(new_parent); | ||
| 331 | if (!new_parent) { | ||
| 332 | error = -EINVAL; | ||
| 333 | goto out; | ||
| 334 | } | ||
| 335 | /* old object path */ | ||
| 336 | devpath = kobject_get_path(kobj, GFP_KERNEL); | ||
| 337 | if (!devpath) { | ||
| 338 | error = -ENOMEM; | ||
| 339 | goto out; | ||
| 340 | } | ||
| 341 | devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL); | ||
| 342 | if (!devpath_string) { | ||
| 343 | error = -ENOMEM; | ||
| 344 | goto out; | ||
| 345 | } | ||
| 346 | sprintf(devpath_string, "DEVPATH_OLD=%s", devpath); | ||
| 347 | envp[0] = devpath_string; | ||
| 348 | envp[1] = NULL; | ||
| 349 | error = sysfs_move_dir(kobj, new_parent); | ||
| 350 | if (error) | ||
| 351 | goto out; | ||
| 352 | old_parent = kobj->parent; | ||
| 353 | kobj->parent = new_parent; | ||
| 354 | kobject_put(old_parent); | ||
| 355 | kobject_uevent_env(kobj, KOBJ_MOVE, envp); | ||
| 356 | out: | ||
| 357 | kobject_put(kobj); | ||
| 358 | kfree(devpath_string); | ||
| 359 | kfree(devpath); | ||
| 360 | return error; | ||
| 361 | } | ||
| 362 | |||
| 363 | /** | ||
| 314 | * kobject_del - unlink kobject from hierarchy. | 364 | * kobject_del - unlink kobject from hierarchy. |
| 315 | * @kobj: object. | 365 | * @kobj: object. |
| 316 | */ | 366 | */ |
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 7f20e7b857cb..a1922765ff31 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c | |||
| @@ -50,18 +50,22 @@ static char *action_to_string(enum kobject_action action) | |||
| 50 | return "offline"; | 50 | return "offline"; |
| 51 | case KOBJ_ONLINE: | 51 | case KOBJ_ONLINE: |
| 52 | return "online"; | 52 | return "online"; |
| 53 | case KOBJ_MOVE: | ||
| 54 | return "move"; | ||
| 53 | default: | 55 | default: |
| 54 | return NULL; | 56 | return NULL; |
| 55 | } | 57 | } |
| 56 | } | 58 | } |
| 57 | 59 | ||
| 58 | /** | 60 | /** |
| 59 | * kobject_uevent - notify userspace by ending an uevent | 61 | * kobject_uevent_env - send an uevent with environmental data |
| 60 | * | 62 | * |
| 61 | * @action: action that is happening (usually KOBJ_ADD and KOBJ_REMOVE) | 63 | * @action: action that is happening (usually KOBJ_MOVE) |
| 62 | * @kobj: struct kobject that the action is happening to | 64 | * @kobj: struct kobject that the action is happening to |
| 65 | * @envp_ext: pointer to environmental data | ||
| 63 | */ | 66 | */ |
| 64 | void kobject_uevent(struct kobject *kobj, enum kobject_action action) | 67 | void kobject_uevent_env(struct kobject *kobj, enum kobject_action action, |
| 68 | char *envp_ext[]) | ||
| 65 | { | 69 | { |
| 66 | char **envp; | 70 | char **envp; |
| 67 | char *buffer; | 71 | char *buffer; |
| @@ -76,6 +80,7 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action) | |||
| 76 | char *seq_buff; | 80 | char *seq_buff; |
| 77 | int i = 0; | 81 | int i = 0; |
| 78 | int retval; | 82 | int retval; |
| 83 | int j; | ||
| 79 | 84 | ||
| 80 | pr_debug("%s\n", __FUNCTION__); | 85 | pr_debug("%s\n", __FUNCTION__); |
| 81 | 86 | ||
| @@ -134,7 +139,8 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action) | |||
| 134 | scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1; | 139 | scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1; |
| 135 | envp [i++] = scratch; | 140 | envp [i++] = scratch; |
| 136 | scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1; | 141 | scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1; |
| 137 | 142 | for (j = 0; envp_ext && envp_ext[j]; j++) | |
| 143 | envp[i++] = envp_ext[j]; | ||
| 138 | /* just reserve the space, overwrite it after kset call has returned */ | 144 | /* just reserve the space, overwrite it after kset call has returned */ |
| 139 | envp[i++] = seq_buff = scratch; | 145 | envp[i++] = seq_buff = scratch; |
| 140 | scratch += strlen("SEQNUM=18446744073709551616") + 1; | 146 | scratch += strlen("SEQNUM=18446744073709551616") + 1; |
| @@ -200,6 +206,20 @@ exit: | |||
| 200 | kfree(envp); | 206 | kfree(envp); |
| 201 | return; | 207 | return; |
| 202 | } | 208 | } |
| 209 | |||
| 210 | EXPORT_SYMBOL_GPL(kobject_uevent_env); | ||
| 211 | |||
| 212 | /** | ||
| 213 | * kobject_uevent - notify userspace by ending an uevent | ||
| 214 | * | ||
| 215 | * @action: action that is happening (usually KOBJ_ADD and KOBJ_REMOVE) | ||
| 216 | * @kobj: struct kobject that the action is happening to | ||
| 217 | */ | ||
| 218 | void kobject_uevent(struct kobject *kobj, enum kobject_action action) | ||
| 219 | { | ||
| 220 | kobject_uevent_env(kobj, action, NULL); | ||
| 221 | } | ||
| 222 | |||
| 203 | EXPORT_SYMBOL_GPL(kobject_uevent); | 223 | EXPORT_SYMBOL_GPL(kobject_uevent); |
| 204 | 224 | ||
| 205 | /** | 225 | /** |
