Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8a82472f authored by Cornelia Huck's avatar Cornelia Huck Committed by Greg Kroah-Hartman
Browse files

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: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Acked-by: default avatarKay Sievers <kay.sievers@vrfy.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent af9e0765
Loading
Loading
Loading
Loading
+92 −0
Original line number Diff line number Diff line
@@ -955,3 +955,95 @@ int device_rename(struct device *dev, char *new_name)

	return error;
}


static int device_move_class_links(struct device *dev,
				   struct device *old_parent,
				   struct device *new_parent)
{
#ifdef CONFIG_SYSFS_DEPRECATED
	int error;
	char *class_name;

	class_name = make_class_name(dev->class->name, &dev->kobj);
	if (!class_name) {
		error = PTR_ERR(class_name);
		class_name = NULL;
		goto out;
	}
	if (old_parent) {
		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");
out:
	kfree(class_name);
	return error;
#else
	return 0;
#endif
}

/**
 * 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
 */
int device_move(struct device *dev, struct device *new_parent)
{
	int error;
	struct device *old_parent;

	dev = get_device(dev);
	if (!dev)
		return -EINVAL;

	if (!device_is_registered(dev)) {
		error = -EINVAL;
		goto out;
	}
	new_parent = get_device(new_parent);
	if (!new_parent) {
		error = -EINVAL;
		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);
	if (error) {
		put_device(new_parent);
		goto out;
	}
	old_parent = dev->parent;
	dev->parent = new_parent;
	if (old_parent)
		klist_del(&dev->knode_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);
	if (error) {
		/* 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_del(&dev->knode_parent);
			if (old_parent)
				klist_add_tail(&dev->knode_parent,
					       &old_parent->klist_children);
		}
		put_device(new_parent);
		goto out;
	}
out_put:
	put_device(old_parent);
out:
	put_device(dev);
	return error;
}

EXPORT_SYMBOL_GPL(device_move);
+45 −0
Original line number Diff line number Diff line
@@ -372,6 +372,51 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
	return error;
}

int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent)
{
	struct dentry *old_parent_dentry, *new_parent_dentry, *new_dentry;
	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;

again:
	mutex_lock(&old_parent_dentry->d_inode->i_mutex);
	if (!mutex_trylock(&new_parent_dentry->d_inode->i_mutex)) {
		mutex_unlock(&old_parent_dentry->d_inode->i_mutex);
		goto again;
	}

	new_parent_sd = new_parent_dentry->d_fsdata;
	sd = kobj->dentry->d_fsdata;

	new_dentry = lookup_one_len(kobj->name, new_parent_dentry,
				    strlen(kobj->name));
	if (IS_ERR(new_dentry)) {
		error = PTR_ERR(new_dentry);
		goto out;
	} else
		error = 0;
	d_add(new_dentry, NULL);
	d_move(kobj->dentry, new_dentry);
	dput(new_dentry);

	/* Remove from old parent's list and insert into new parent's list. */
	list_del_init(&sd->s_sibling);
	list_add(&sd->s_sibling, &new_parent_sd->s_children);

out:
	mutex_unlock(&new_parent_dentry->d_inode->i_mutex);
	mutex_unlock(&old_parent_dentry->d_inode->i_mutex);

	return error;
}

static int sysfs_dir_open(struct inode *inode, struct file *file)
{
	struct dentry * dentry = file->f_dentry;
+1 −0
Original line number Diff line number Diff line
@@ -424,6 +424,7 @@ extern int device_for_each_child(struct device *, void *,
extern struct device *device_find_child(struct device *, void *data,
					int (*match)(struct device *, void *));
extern int device_rename(struct device *dev, char *new_name);
extern int device_move(struct device *dev, struct device *new_parent);

/*
 * Manual binding of a device to driver. See drivers/base/bus.c
+8 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ enum kobject_action {
	KOBJ_UMOUNT	= (__force kobject_action_t) 0x05,	/* umount event for block devices (broken) */
	KOBJ_OFFLINE	= (__force kobject_action_t) 0x06,	/* device offline */
	KOBJ_ONLINE	= (__force kobject_action_t) 0x07,	/* device online */
	KOBJ_MOVE	= (__force kobject_action_t) 0x08,	/* device move */
};

struct kobject {
@@ -76,6 +77,7 @@ extern int __must_check kobject_add(struct kobject *);
extern void kobject_del(struct kobject *);

extern int __must_check kobject_rename(struct kobject *, const char *new_name);
extern int __must_check kobject_move(struct kobject *, struct kobject *);

extern int __must_check kobject_register(struct kobject *);
extern void kobject_unregister(struct kobject *);
@@ -264,6 +266,8 @@ extern int __must_check subsys_create_file(struct subsystem * ,

#if defined(CONFIG_HOTPLUG)
void kobject_uevent(struct kobject *kobj, enum kobject_action action);
void kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
			char *envp[]);

int add_uevent_var(char **envp, int num_envp, int *cur_index,
			char *buffer, int buffer_size, int *cur_len,
@@ -271,6 +275,10 @@ int add_uevent_var(char **envp, int num_envp, int *cur_index,
	__attribute__((format (printf, 7, 8)));
#else
static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { }
static inline void kobject_uevent_env(struct kobject *kobj,
				      enum kobject_action action,
				      char *envp[])
{ }

static inline int add_uevent_var(char **envp, int num_envp, int *cur_index,
				      char *buffer, int buffer_size, int *cur_len, 
+8 −0
Original line number Diff line number Diff line
@@ -96,6 +96,9 @@ sysfs_remove_dir(struct kobject *);
extern int __must_check
sysfs_rename_dir(struct kobject *, const char *new_name);

extern int __must_check
sysfs_move_dir(struct kobject *, struct kobject *);

extern int __must_check
sysfs_create_file(struct kobject *, const struct attribute *);

@@ -142,6 +145,11 @@ static inline int sysfs_rename_dir(struct kobject * k, const char *new_name)
	return 0;
}

static inline int sysfs_move_dir(struct kobject * k, struct kobject * new_parent)
{
	return 0;
}

static inline int sysfs_create_file(struct kobject * k, const struct attribute * a)
{
	return 0;
Loading