[PATCH 6/28] driver core: Allow device_move(dev, NULL).

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Cornelia Huck <[email protected]>

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 <[email protected]>
Cc: Marcel Holtmann <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
 drivers/base/core.c |   76 +++++++++++++++++++++++++++++++-------------------
 fs/sysfs/dir.c      |    6 +---
 lib/kobject.c       |    6 ++--
 3 files changed, 52 insertions(+), 36 deletions(-)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index 36eedfd..c60114d 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 : "<NULL>");
+	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);
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index 511edef..2bab1b4 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)
 	struct sysfs_dirent *new_parent_sd, *sd;
 	int error;
 
-	if (!new_parent)
-		return -EINVAL;
-
 	old_parent_dentry = kobj->parent ?
 		kobj->parent->dentry : sysfs_mount->mnt_sb->s_root;
-	new_parent_dentry = new_parent->dentry;
+	new_parent_dentry = new_parent ?
+		new_parent->dentry : sysfs_mount->mnt_sb->s_root;
 
 again:
 	mutex_lock(&old_parent_dentry->d_inode->i_mutex);
diff --git a/lib/kobject.c b/lib/kobject.c
index 9aed594..c033dc8 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -314,7 +314,7 @@ int kobject_rename(struct kobject * kobj, const char *new_name)
 /**
  *	kobject_move - move object to another parent
  *	@kobj:	object in question.
- *	@new_parent: object's new parent
+ *	@new_parent: object's new parent (can be NULL)
  */
 
 int kobject_move(struct kobject *kobj, struct kobject *new_parent)
@@ -330,8 +330,8 @@ int kobject_move(struct kobject *kobj, struct kobject *new_parent)
 		return -EINVAL;
 	new_parent = kobject_get(new_parent);
 	if (!new_parent) {
-		error = -EINVAL;
-		goto out;
+		if (kobj->kset)
+			new_parent = kobject_get(&kobj->kset->kobj);
 	}
 	/* old object path */
 	devpath = kobject_get_path(kobj, GFP_KERNEL);
-- 
1.4.4.4

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux