mirror of
https://github.com/adulau/aha.git
synced 2024-12-28 03:36:19 +00:00
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>
This commit is contained in:
parent
af9e076536
commit
8a82472f86
7 changed files with 228 additions and 4 deletions
|
@ -955,3 +955,95 @@ int device_rename(struct device *dev, char *new_name)
|
||||||
|
|
||||||
return error;
|
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);
|
||||||
|
|
|
@ -372,6 +372,51 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
|
||||||
return error;
|
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)
|
static int sysfs_dir_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct dentry * dentry = file->f_dentry;
|
struct dentry * dentry = file->f_dentry;
|
||||||
|
|
|
@ -424,6 +424,7 @@ extern int device_for_each_child(struct device *, void *,
|
||||||
extern struct device *device_find_child(struct device *, void *data,
|
extern struct device *device_find_child(struct device *, void *data,
|
||||||
int (*match)(struct device *, void *));
|
int (*match)(struct device *, void *));
|
||||||
extern int device_rename(struct device *dev, char *new_name);
|
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
|
* Manual binding of a device to driver. See drivers/base/bus.c
|
||||||
|
|
|
@ -47,6 +47,7 @@ enum kobject_action {
|
||||||
KOBJ_UMOUNT = (__force kobject_action_t) 0x05, /* umount event for block devices (broken) */
|
KOBJ_UMOUNT = (__force kobject_action_t) 0x05, /* umount event for block devices (broken) */
|
||||||
KOBJ_OFFLINE = (__force kobject_action_t) 0x06, /* device offline */
|
KOBJ_OFFLINE = (__force kobject_action_t) 0x06, /* device offline */
|
||||||
KOBJ_ONLINE = (__force kobject_action_t) 0x07, /* device online */
|
KOBJ_ONLINE = (__force kobject_action_t) 0x07, /* device online */
|
||||||
|
KOBJ_MOVE = (__force kobject_action_t) 0x08, /* device move */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kobject {
|
struct kobject {
|
||||||
|
@ -76,6 +77,7 @@ extern int __must_check kobject_add(struct kobject *);
|
||||||
extern void kobject_del(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_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 int __must_check kobject_register(struct kobject *);
|
||||||
extern void kobject_unregister(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)
|
#if defined(CONFIG_HOTPLUG)
|
||||||
void kobject_uevent(struct kobject *kobj, enum kobject_action action);
|
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,
|
int add_uevent_var(char **envp, int num_envp, int *cur_index,
|
||||||
char *buffer, int buffer_size, int *cur_len,
|
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)));
|
__attribute__((format (printf, 7, 8)));
|
||||||
#else
|
#else
|
||||||
static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { }
|
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,
|
static inline int add_uevent_var(char **envp, int num_envp, int *cur_index,
|
||||||
char *buffer, int buffer_size, int *cur_len,
|
char *buffer, int buffer_size, int *cur_len,
|
||||||
|
|
|
@ -96,6 +96,9 @@ sysfs_remove_dir(struct kobject *);
|
||||||
extern int __must_check
|
extern int __must_check
|
||||||
sysfs_rename_dir(struct kobject *, const char *new_name);
|
sysfs_rename_dir(struct kobject *, const char *new_name);
|
||||||
|
|
||||||
|
extern int __must_check
|
||||||
|
sysfs_move_dir(struct kobject *, struct kobject *);
|
||||||
|
|
||||||
extern int __must_check
|
extern int __must_check
|
||||||
sysfs_create_file(struct kobject *, const struct attribute *);
|
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;
|
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)
|
static inline int sysfs_create_file(struct kobject * k, const struct attribute * a)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -310,6 +310,56 @@ int kobject_rename(struct kobject * kobj, const char *new_name)
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kobject_move - move object to another parent
|
||||||
|
* @kobj: object in question.
|
||||||
|
* @new_parent: object's new parent
|
||||||
|
*/
|
||||||
|
|
||||||
|
int kobject_move(struct kobject *kobj, struct kobject *new_parent)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
struct kobject *old_parent;
|
||||||
|
const char *devpath = NULL;
|
||||||
|
char *devpath_string = NULL;
|
||||||
|
char *envp[2];
|
||||||
|
|
||||||
|
kobj = kobject_get(kobj);
|
||||||
|
if (!kobj)
|
||||||
|
return -EINVAL;
|
||||||
|
new_parent = kobject_get(new_parent);
|
||||||
|
if (!new_parent) {
|
||||||
|
error = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* old object path */
|
||||||
|
devpath = kobject_get_path(kobj, GFP_KERNEL);
|
||||||
|
if (!devpath) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
|
||||||
|
if (!devpath_string) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
|
||||||
|
envp[0] = devpath_string;
|
||||||
|
envp[1] = NULL;
|
||||||
|
error = sysfs_move_dir(kobj, new_parent);
|
||||||
|
if (error)
|
||||||
|
goto out;
|
||||||
|
old_parent = kobj->parent;
|
||||||
|
kobj->parent = new_parent;
|
||||||
|
kobject_put(old_parent);
|
||||||
|
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
|
||||||
|
out:
|
||||||
|
kobject_put(kobj);
|
||||||
|
kfree(devpath_string);
|
||||||
|
kfree(devpath);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kobject_del - unlink kobject from hierarchy.
|
* kobject_del - unlink kobject from hierarchy.
|
||||||
* @kobj: object.
|
* @kobj: object.
|
||||||
|
|
|
@ -50,18 +50,22 @@ static char *action_to_string(enum kobject_action action)
|
||||||
return "offline";
|
return "offline";
|
||||||
case KOBJ_ONLINE:
|
case KOBJ_ONLINE:
|
||||||
return "online";
|
return "online";
|
||||||
|
case KOBJ_MOVE:
|
||||||
|
return "move";
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kobject_uevent - notify userspace by ending an uevent
|
* kobject_uevent_env - send an uevent with environmental data
|
||||||
*
|
*
|
||||||
* @action: action that is happening (usually KOBJ_ADD and KOBJ_REMOVE)
|
* @action: action that is happening (usually KOBJ_MOVE)
|
||||||
* @kobj: struct kobject that the action is happening to
|
* @kobj: struct kobject that the action is happening to
|
||||||
|
* @envp_ext: pointer to environmental data
|
||||||
*/
|
*/
|
||||||
void kobject_uevent(struct kobject *kobj, enum kobject_action action)
|
void kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
|
||||||
|
char *envp_ext[])
|
||||||
{
|
{
|
||||||
char **envp;
|
char **envp;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
|
@ -76,6 +80,7 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action)
|
||||||
char *seq_buff;
|
char *seq_buff;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int retval;
|
int retval;
|
||||||
|
int j;
|
||||||
|
|
||||||
pr_debug("%s\n", __FUNCTION__);
|
pr_debug("%s\n", __FUNCTION__);
|
||||||
|
|
||||||
|
@ -134,7 +139,8 @@ void kobject_uevent(struct kobject *kobj, enum kobject_action action)
|
||||||
scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;
|
scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;
|
||||||
envp [i++] = scratch;
|
envp [i++] = scratch;
|
||||||
scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;
|
scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;
|
||||||
|
for (j = 0; envp_ext && envp_ext[j]; j++)
|
||||||
|
envp[i++] = envp_ext[j];
|
||||||
/* just reserve the space, overwrite it after kset call has returned */
|
/* just reserve the space, overwrite it after kset call has returned */
|
||||||
envp[i++] = seq_buff = scratch;
|
envp[i++] = seq_buff = scratch;
|
||||||
scratch += strlen("SEQNUM=18446744073709551616") + 1;
|
scratch += strlen("SEQNUM=18446744073709551616") + 1;
|
||||||
|
@ -200,6 +206,20 @@ exit:
|
||||||
kfree(envp);
|
kfree(envp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL_GPL(kobject_uevent_env);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kobject_uevent - notify userspace by ending an uevent
|
||||||
|
*
|
||||||
|
* @action: action that is happening (usually KOBJ_ADD and KOBJ_REMOVE)
|
||||||
|
* @kobj: struct kobject that the action is happening to
|
||||||
|
*/
|
||||||
|
void kobject_uevent(struct kobject *kobj, enum kobject_action action)
|
||||||
|
{
|
||||||
|
kobject_uevent_env(kobj, action, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(kobject_uevent);
|
EXPORT_SYMBOL_GPL(kobject_uevent);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue