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 | /** |