2006-11-14 10:32:32

by Cornelia Huck

[permalink] [raw]
Subject: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

From: Cornelia Huck <[email protected]>

Provide a function device_move() to move a device to a new parent device. Add
auxilliary functions kobject_move() and sysfs_move_dir().

Signed-off-by: Cornelia Huck <[email protected]>

---
drivers/base/core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/sysfs/dir.c | 45 +++++++++++++++++++++++
include/linux/device.h | 1
include/linux/kobject.h | 1
include/linux/sysfs.h | 3 +
lib/kobject.c | 30 +++++++++++++++
6 files changed, 172 insertions(+)

--- linux-2.6-CH.orig/drivers/base/core.c
+++ linux-2.6-CH/drivers/base/core.c
@@ -955,3 +955,95 @@ int device_rename(struct device *dev, ch

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);
--- linux-2.6-CH.orig/fs/sysfs/dir.c
+++ linux-2.6-CH/fs/sysfs/dir.c
@@ -372,6 +372,51 @@ int sysfs_rename_dir(struct kobject * ko
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_path.dentry;
--- linux-2.6-CH.orig/include/linux/device.h
+++ linux-2.6-CH/include/linux/device.h
@@ -423,6 +423,7 @@ extern int device_for_each_child(struct
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
--- linux-2.6-CH.orig/include/linux/kobject.h
+++ linux-2.6-CH/include/linux/kobject.h
@@ -76,6 +76,7 @@ extern int __must_check kobject_add(stru
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 *);
--- linux-2.6-CH.orig/include/linux/sysfs.h
+++ linux-2.6-CH/include/linux/sysfs.h
@@ -97,6 +97,9 @@ 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 *);

extern int __must_check
--- linux-2.6-CH.orig/lib/kobject.c
+++ linux-2.6-CH/lib/kobject.c
@@ -355,6 +355,36 @@ int kobject_rename(struct kobject * kobj
}

/**
+ * 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;
+
+ kobj = kobject_get(kobj);
+ if (!kobj)
+ return -EINVAL;
+ new_parent = kobject_get(new_parent);
+ if (!new_parent) {
+ error = -EINVAL;
+ goto out;
+ }
+ error = sysfs_move_dir(kobj, new_parent);
+ if (error)
+ goto out;
+ old_parent = kobj->parent;
+ kobj->parent = new_parent;
+ kobject_put(old_parent);
+out:
+ kobject_put(kobj);
+ return error;
+}
+
+/**
* kobject_del - unlink kobject from hierarchy.
* @kobj: object.
*/


2006-11-15 07:06:33

by Greg KH

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On Tue, Nov 14, 2006 at 11:32:08AM +0100, Cornelia Huck wrote:
> From: Cornelia Huck <[email protected]>
>
> Provide a function device_move() to move a device to a new parent device. Add
> auxilliary functions kobject_move() and sysfs_move_dir().

At first glance, this looks sane, but for the kobject_move function, we
are not notifying userspace that something has changed here.

Is that ok?

How will udev and HAL handle something like this without being told
about it? When the device eventually goes away, I think they will be
very confused.

thanks,

greg k-h

2006-11-15 07:28:25

by Cornelia Huck

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On Tue, 14 Nov 2006 22:50:52 -0800,
Greg KH <[email protected]> wrote:

> On Tue, Nov 14, 2006 at 11:32:08AM +0100, Cornelia Huck wrote:
> > From: Cornelia Huck <[email protected]>
> >
> > Provide a function device_move() to move a device to a new parent device. Add
> > auxilliary functions kobject_move() and sysfs_move_dir().
>
> At first glance, this looks sane, but for the kobject_move function, we
> are not notifying userspace that something has changed here.
>
> Is that ok?
>
> How will udev and HAL handle something like this without being told
> about it? When the device eventually goes away, I think they will be
> very confused.

Hm. I don't think we want to trigger udev with some remove/add events
(especially since it is still the same device, it just has been moved
around). A change event doesn't sound quite right either. But I guess
we need to do something, at least to make HAL happy since it remembers
the path in sysfs (although I seem to remember a HAL patch that got rid
of it?)

--
Cornelia Huck
Linux for zSeries Developer
Tel.: +49-7031-16-4837, Mail: [email protected]

2006-11-15 08:44:35

by Kay Sievers

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On 11/15/06, Cornelia Huck <[email protected]> wrote:
> On Tue, 14 Nov 2006 22:50:52 -0800,
> Greg KH <[email protected]> wrote:
>
> > On Tue, Nov 14, 2006 at 11:32:08AM +0100, Cornelia Huck wrote:
> > > From: Cornelia Huck <[email protected]>
> > >
> > > Provide a function device_move() to move a device to a new parent device. Add
> > > auxilliary functions kobject_move() and sysfs_move_dir().
> >
> > At first glance, this looks sane, but for the kobject_move function, we
> > are not notifying userspace that something has changed here.
> >
> > Is that ok?
> >
> > How will udev and HAL handle something like this without being told
> > about it? When the device eventually goes away, I think they will be
> > very confused.

Yes, userspace will get confused, if we we don't get proper
notification. We require to update the udev and HAL database with the
new devpath, to find the current device context on device events, or
for "remove".

> Hm. I don't think we want to trigger udev with some remove/add events
> (especially since it is still the same device, it just has been moved
> around). A change event doesn't sound quite right either. But I guess
> we need to do something, at least to make HAL happy since it remembers
> the path in sysfs (although I seem to remember a HAL patch that got rid
> of it?)

Udev and HAL, both will need an event for the moving, with the old
DEVPATH value in the environment. We want something like a "rename" or
"move" event. Without that, weird things will happen in userspace,
because the devpath is used as the key to the device during the whole
device lifetime. The only weird exception today is the netif rename
case, which is already handled by special code in udev.

Thanks,
Kay

2006-11-15 09:23:37

by Cornelia Huck

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On Wed, 15 Nov 2006 09:44:33 +0100,
"Kay Sievers" <[email protected]> wrote:

> Udev and HAL, both will need an event for the moving, with the old
> DEVPATH value in the environment. We want something like a "rename" or
> "move" event. Without that, weird things will happen in userspace,
> because the devpath is used as the key to the device during the whole
> device lifetime. The only weird exception today is the netif rename
> case, which is already handled by special code in udev.

Something like below (completely untested as my test box is currently
inaccessible)? Wouldn't we need something similar for kobject_rename()
as well?

---
include/linux/kobject.h | 1 +
lib/kobject.c | 1 +
2 files changed, 2 insertions(+)

--- linux-2.6-CH.orig/include/linux/kobject.h
+++ linux-2.6-CH/include/linux/kobject.h
@@ -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 {
--- linux-2.6-CH.orig/lib/kobject.c
+++ linux-2.6-CH/lib/kobject.c
@@ -379,6 +379,7 @@ int kobject_move(struct kobject *kobj, s
old_parent = kobj->parent;
kobj->parent = new_parent;
kobject_put(old_parent);
+ kobject_uevent(kobj, KOBJ_MOVE);
out:
kobject_put(kobj);
return error;

2006-11-15 09:32:09

by Kay Sievers

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On Wed, 2006-11-15 at 10:24 +0100, Cornelia Huck wrote:
> On Wed, 15 Nov 2006 09:44:33 +0100,
> "Kay Sievers" <[email protected]> wrote:
>
> > Udev and HAL, both will need an event for the moving, with the old
> > DEVPATH value in the environment. We want something like a "rename" or
> > "move" event. Without that, weird things will happen in userspace,
> > because the devpath is used as the key to the device during the whole
> > device lifetime. The only weird exception today is the netif rename
> > case, which is already handled by special code in udev.
>
> Something like below (completely untested as my test box is currently
> inaccessible)?

We need the old DEVPATH in the environment (or something similar),
otherwise we can't connect the event with the new device location to the
current device. :)

> Wouldn't we need something similar for kobject_rename()
> as well?

Maybe kobject_rename() can go, if we have a move function which can be
used. In any case, the events should look identical to userspace, yes.

Thanks,
Kay

2006-11-15 10:11:09

by Cornelia Huck

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On Wed, 15 Nov 2006 10:31:59 +0100,
Kay Sievers <[email protected]> wrote:

> We need the old DEVPATH in the environment (or something similar),
> otherwise we can't connect the event with the new device location to the
> current device. :)

Duh. I've attached another completely untested patch below.

> > Wouldn't we need something similar for kobject_rename()
> > as well?
>
> Maybe kobject_rename() can go, if we have a move function which can be
> used. In any case, the events should look identical to userspace, yes.

I think kobject_move() and kobject_rename() are two different beasts.
kobject_move() changes the topology, kobject_rename() changes an
identifier. Shouldn't they be reported in two different ways to
userspace?

---
include/linux/kobject.h | 6 ++++++
lib/kobject.c | 13 +++++++++++++
lib/kobject_uevent.c | 38 ++++++++++++++++++++++++++++++--------
3 files changed, 49 insertions(+), 8 deletions(-)

--- linux-2.6-CH.orig/include/linux/kobject.h
+++ linux-2.6-CH/include/linux/kobject.h
@@ -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 {
@@ -265,6 +266,8 @@ extern int __must_check subsys_create_fi

#if defined(CONFIG_HOTPLUG)
void kobject_uevent(struct kobject *kobj, enum kobject_action action);
+void kobject_uevent_extended(struct kobject *kobj, enum kobject_action action,
+ const char *string);

int add_uevent_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
@@ -272,6 +275,9 @@ int add_uevent_var(char **envp, int num_
__attribute__((format (printf, 7, 8)));
#else
static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { }
+static inline void kobject_uevent_extended(struct kobject *kobj,
+ enum kobject_action action,
+ const char *string) { }

static inline int add_uevent_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
--- linux-2.6-CH.orig/lib/kobject.c
+++ linux-2.6-CH/lib/kobject.c
@@ -364,6 +364,8 @@ int kobject_move(struct kobject *kobj, s
{
int error;
struct kobject *old_parent;
+ const char *devpath = NULL;
+ const char *devpath_string = NULL;

kobj = kobject_get(kobj);
if (!kobj)
@@ -373,14 +375,25 @@ int kobject_move(struct kobject *kobj, s
error = -EINVAL;
goto out;
}
+ /* old object path */
+ devpath = kobject_get_path(kobj, GFP_KERNEL);
+ if (!devpath)
+ goto out;
+ devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);
+ if (!devpath_string)
+ goto out;
+ sprintf(devpath_string, "OLD_DEVPATH=%s", devpath);
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_extended(kobj, KOBJ_MOVE, devpath);
out:
kobject_put(kobj);
+ kfree(devpath_string);
+ kfree(devpath);
return error;
}

--- linux-2.6-CH.orig/lib/kobject_uevent.c
+++ linux-2.6-CH/lib/kobject_uevent.c
@@ -55,13 +55,8 @@ static char *action_to_string(enum kobje
}
}

-/**
- * 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)
+static void do_kobject_uevent(struct kobject *kobj, enum kobject_action action,
+ const char *string)
{
char **envp;
char *buffer;
@@ -134,7 +129,10 @@ void kobject_uevent(struct kobject *kobj
scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;
envp [i++] = scratch;
scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;
-
+ if (string != NULL) {
+ envp [i++] = scratch;
+ scratch += sprintf(scratch, "%s", string) + 1;
+ }
/* just reserve the space, overwrite it after kset call has returned */
envp[i++] = seq_buff = scratch;
scratch += strlen("SEQNUM=18446744073709551616") + 1;
@@ -200,9 +198,33 @@ exit:
kfree(envp);
return;
}
+
+/**
+ * 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)
+{
+ do_kobject_uevent(kobj, action, NULL);
+}
EXPORT_SYMBOL_GPL(kobject_uevent);

/**
+ * kobject_uevent_extended - send an uevent with extended data
+ *
+ * @action: action that is happening (usually KOBJ_MOVE)
+ * @kobj: struct kobject that the action is happening to
+ * @string: string containing additional data
+ */
+void kobject_uevent_extended(struct kobject *kobj, enum kobject_action action,
+ const char *string)
+{
+ do_kobject_uevent(kobj, action, string);
+}
+
+/**
* add_uevent_var - helper for creating event variables
* @envp: Pointer to table of environment variables, as passed into
* uevent() method.

2006-11-15 16:44:39

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On 11/15/06, Cornelia Huck <[email protected]> wrote:
> On Wed, 15 Nov 2006 10:31:59 +0100,
> Kay Sievers <[email protected]> wrote:
>
> > We need the old DEVPATH in the environment (or something similar),
> > otherwise we can't connect the event with the new device location to the
> > current device. :)
>
> Duh. I've attached another completely untested patch below.
>
> > > Wouldn't we need something similar for kobject_rename()
> > > as well?
> >
> > Maybe kobject_rename() can go, if we have a move function which can be
> > used. In any case, the events should look identical to userspace, yes.
>
> I think kobject_move() and kobject_rename() are two different beasts.
> kobject_move() changes the topology, kobject_rename() changes an
> identifier. Shouldn't they be reported in two different ways to
> userspace?
>

Why do we need to have them at all? Devices should not "move" in the
trees - it it moves we should just treat them as old devices going
away and new devices appearing... Renames - they are only used to
rename net devices, don't they? I wonder if we could have just a
separate "alias" or "name" sysfs attribute for them and get away with
renaming of devices.

--
Dmitry

2006-11-15 17:22:39

by Cornelia Huck

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On Wed, 15 Nov 2006 11:44:36 -0500,
"Dmitry Torokhov" <[email protected]> wrote:

> Why do we need to have them at all? Devices should not "move" in the
> trees - it it moves we should just treat them as old devices going
> away and new devices appearing...

But unregistering and re-registering is exactly what we want to avoid
in our case. We have still the same ccw device, it's only now
operational via another subchannel (i. e. the topology changed, but not
the device). If we unregistered the device, we would also kill the
associated block device(s), and if it had been mounted, we go boom.
(This is what currently happens without this patchset, and it may be
triggered by a short hardware outage we'd otherwise survive without a
problem.)

--
Cornelia Huck
Linux for zSeries Developer
Tel.: +49-7031-16-4837, Mail: [email protected]

2006-11-15 17:38:51

by Cornelia Huck

[permalink] [raw]
Subject: Re: [Patch -mm 2/5] driver core: Introduce device_move(): move a device to a new parent.

On Wed, 15 Nov 2006 11:11:36 +0100,
Cornelia Huck <[email protected]> wrote:

> On Wed, 15 Nov 2006 10:31:59 +0100,
> Kay Sievers <[email protected]> wrote:
>
> > We need the old DEVPATH in the environment (or something similar),
> > otherwise we can't connect the event with the new device location to the
> > current device. :)
>
> Duh. I've attached another completely untested patch below.

And this one may have an actual chance of doing something useful :/

---
include/linux/kobject.h | 6 ++++++
lib/kobject.c | 17 +++++++++++++++++
lib/kobject_uevent.c | 40 ++++++++++++++++++++++++++++++++--------
3 files changed, 55 insertions(+), 8 deletions(-)

--- linux-2.6-CH.orig/include/linux/kobject.h
+++ linux-2.6-CH/include/linux/kobject.h
@@ -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 {
@@ -265,6 +266,8 @@ extern int __must_check subsys_create_fi

#if defined(CONFIG_HOTPLUG)
void kobject_uevent(struct kobject *kobj, enum kobject_action action);
+void kobject_uevent_extended(struct kobject *kobj, enum kobject_action action,
+ const char *string);

int add_uevent_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
@@ -272,6 +275,9 @@ int add_uevent_var(char **envp, int num_
__attribute__((format (printf, 7, 8)));
#else
static inline void kobject_uevent(struct kobject *kobj, enum kobject_action action) { }
+static inline void kobject_uevent_extended(struct kobject *kobj,
+ enum kobject_action action,
+ const char *string) { }

static inline int add_uevent_var(char **envp, int num_envp, int *cur_index,
char *buffer, int buffer_size, int *cur_len,
--- linux-2.6-CH.orig/lib/kobject.c
+++ linux-2.6-CH/lib/kobject.c
@@ -364,6 +364,8 @@ int kobject_move(struct kobject *kobj, s
{
int error;
struct kobject *old_parent;
+ const char *devpath = NULL;
+ char *devpath_string = NULL;

kobj = kobject_get(kobj);
if (!kobj)
@@ -373,14 +375,29 @@ int kobject_move(struct kobject *kobj, s
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, "OLD_DEVPATH=%s", devpath);
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_extended(kobj, KOBJ_MOVE, devpath);
out:
kobject_put(kobj);
+ kfree(devpath_string);
+ kfree(devpath);
return error;
}

--- linux-2.6-CH.orig/lib/kobject_uevent.c
+++ linux-2.6-CH/lib/kobject_uevent.c
@@ -50,18 +50,15 @@ static char *action_to_string(enum kobje
return "offline";
case KOBJ_ONLINE:
return "online";
+ case KOBJ_MOVE:
+ return "move";
default:
return NULL;
}
}

-/**
- * 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)
+static void do_kobject_uevent(struct kobject *kobj, enum kobject_action action,
+ const char *string)
{
char **envp;
char *buffer;
@@ -134,7 +131,10 @@ void kobject_uevent(struct kobject *kobj
scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;
envp [i++] = scratch;
scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;
-
+ if (string != NULL) {
+ envp [i++] = scratch;
+ scratch += sprintf(scratch, "%s", string) + 1;
+ }
/* just reserve the space, overwrite it after kset call has returned */
envp[i++] = seq_buff = scratch;
scratch += strlen("SEQNUM=18446744073709551616") + 1;
@@ -200,9 +200,33 @@ exit:
kfree(envp);
return;
}
+
+/**
+ * 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)
+{
+ do_kobject_uevent(kobj, action, NULL);
+}
EXPORT_SYMBOL_GPL(kobject_uevent);

/**
+ * kobject_uevent_extended - send an uevent with extended data
+ *
+ * @action: action that is happening (usually KOBJ_MOVE)
+ * @kobj: struct kobject that the action is happening to
+ * @string: string containing additional data
+ */
+void kobject_uevent_extended(struct kobject *kobj, enum kobject_action action,
+ const char *string)
+{
+ do_kobject_uevent(kobj, action, string);
+}
+
+/**
* add_uevent_var - helper for creating event variables
* @envp: Pointer to table of environment variables, as passed into
* uevent() method.