aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCornelia Huck <cornelia.huck@de.ibm.com>2006-11-20 11:07:51 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-12-01 17:52:01 -0500
commit8a82472f86bf693b8e91ed56c9ca4f62fbbdcfa3 (patch)
tree79d148ee548f4b57e6f5a4a69cf6cdb81e7a1bf2
parentaf9e0765362151b27372c14d9d6dc417184182d3 (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.c92
-rw-r--r--fs/sysfs/dir.c45
-rw-r--r--include/linux/device.h1
-rw-r--r--include/linux/kobject.h8
-rw-r--r--include/linux/sysfs.h8
-rw-r--r--lib/kobject.c50
-rw-r--r--lib/kobject_uevent.c28
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
960static 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");
984out:
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 */
997int 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 }
1042out_put:
1043 put_device(old_parent);
1044out:
1045 put_device(dev);
1046 return error;
1047}
1048
1049EXPORT_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
375int 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
388again:
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
413out:
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
375static int sysfs_dir_open(struct inode *inode, struct file *file) 420static 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 *,
424extern struct device *device_find_child(struct device *, void *data, 424extern struct device *device_find_child(struct device *, void *data,
425 int (*match)(struct device *, void *)); 425 int (*match)(struct device *, void *));
426extern int device_rename(struct device *dev, char *new_name); 426extern int device_rename(struct device *dev, char *new_name);
427extern 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
52struct kobject { 53struct kobject {
@@ -76,6 +77,7 @@ extern int __must_check kobject_add(struct kobject *);
76extern void kobject_del(struct kobject *); 77extern void kobject_del(struct kobject *);
77 78
78extern int __must_check kobject_rename(struct kobject *, const char *new_name); 79extern int __must_check kobject_rename(struct kobject *, const char *new_name);
80extern int __must_check kobject_move(struct kobject *, struct kobject *);
79 81
80extern int __must_check kobject_register(struct kobject *); 82extern int __must_check kobject_register(struct kobject *);
81extern void kobject_unregister(struct kobject *); 83extern 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)
266void kobject_uevent(struct kobject *kobj, enum kobject_action action); 268void kobject_uevent(struct kobject *kobj, enum kobject_action action);
269void kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
270 char *envp[]);
267 271
268int add_uevent_var(char **envp, int num_envp, int *cur_index, 272int 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
273static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { } 277static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { }
278static inline void kobject_uevent_env(struct kobject *kobj,
279 enum kobject_action action,
280 char *envp[])
281{ }
274 282
275static inline int add_uevent_var(char **envp, int num_envp, int *cur_index, 283static 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
97sysfs_rename_dir(struct kobject *, const char *new_name); 97sysfs_rename_dir(struct kobject *, const char *new_name);
98 98
99extern int __must_check 99extern int __must_check
100sysfs_move_dir(struct kobject *, struct kobject *);
101
102extern int __must_check
100sysfs_create_file(struct kobject *, const struct attribute *); 103sysfs_create_file(struct kobject *, const struct attribute *);
101 104
102extern int __must_check 105extern 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
148static inline int sysfs_move_dir(struct kobject * k, struct kobject * new_parent)
149{
150 return 0;
151}
152
145static inline int sysfs_create_file(struct kobject * k, const struct attribute * a) 153static 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
319int 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);
356out:
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 */
64void kobject_uevent(struct kobject *kobj, enum kobject_action action) 67void 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
210EXPORT_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 */
218void kobject_uevent(struct kobject *kobj, enum kobject_action action)
219{
220 kobject_uevent_env(kobj, action, NULL);
221}
222
203EXPORT_SYMBOL_GPL(kobject_uevent); 223EXPORT_SYMBOL_GPL(kobject_uevent);
204 224
205/** 225/**