* [Patch -mm 2/2] driver core: Introduce device_move(): move a device to a new parent.
@ 2006-11-16 14:42 Cornelia Huck
2006-11-16 16:47 ` Kay Sievers
0 siblings, 1 reply; 9+ messages in thread
From: Cornelia Huck @ 2006-11-16 14:42 UTC (permalink / raw)
To: linux-kernel; +Cc: Andrew Morton, Greg K-H, Kay Sievers, Martin Schwidefsky
From: Cornelia Huck <cornelia.huck@de.ibm.com>
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 (OLD_DEVPATH) in addition to the usual values.
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
---
drivers/base/core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++
fs/sysfs/dir.c | 45 +++++++++++++++++++++++
include/linux/device.h | 1
include/linux/kobject.h | 7 +++
include/linux/sysfs.h | 8 ++++
lib/kobject.c | 47 ++++++++++++++++++++++++
lib/kobject_uevent.c | 40 ++++++++++++++++----
7 files changed, 232 insertions(+), 8 deletions(-)
--- 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
@@ -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(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 *);
@@ -264,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,
@@ -271,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/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
@@ -143,6 +146,11 @@ static inline int sysfs_rename_dir(struc
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;
--- linux-2.6-CH.orig/lib/kobject.c
+++ linux-2.6-CH/lib/kobject.c
@@ -355,6 +355,53 @@ 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;
+ const char *devpath = NULL;
+ char *devpath_string = NULL;
+
+ 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, "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;
+}
+
+/**
* kobject_del - unlink kobject from hierarchy.
* @kobj: object.
*/
--- 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.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [Patch -mm 2/2] driver core: Introduce device_move(): move a device to a new parent. 2006-11-16 14:42 [Patch -mm 2/2] driver core: Introduce device_move(): move a device to a new parent Cornelia Huck @ 2006-11-16 16:47 ` Kay Sievers 2006-11-17 4:23 ` Greg KH 0 siblings, 1 reply; 9+ messages in thread From: Kay Sievers @ 2006-11-16 16:47 UTC (permalink / raw) To: Cornelia Huck; +Cc: linux-kernel, Andrew Morton, Greg K-H, Martin Schwidefsky On Thu, 2006-11-16 at 15:42 +0100, Cornelia Huck wrote: > From: Cornelia Huck <cornelia.huck@de.ibm.com> > > 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 (OLD_DEVPATH) in addition to the usual values. > + sprintf(devpath_string, "OLD_DEVPATH=%s", devpath); I think it's easier to understand, if the variable starts with the same string as original name. I prefer DEVPATH_OLD. > +void kobject_uevent_extended(struct kobject *kobj, enum kobject_action action, > + const char *string) I think we should pass an array of env vars here instead of a single string - you never know ... :) The function could probably be named kobject_uevent_env() then. Thanks, Kay ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Patch -mm 2/2] driver core: Introduce device_move(): move a device to a new parent. 2006-11-16 16:47 ` Kay Sievers @ 2006-11-17 4:23 ` Greg KH 2006-11-20 8:05 ` Cornelia Huck 0 siblings, 1 reply; 9+ messages in thread From: Greg KH @ 2006-11-17 4:23 UTC (permalink / raw) To: Kay Sievers Cc: Cornelia Huck, linux-kernel, Andrew Morton, Martin Schwidefsky On Thu, Nov 16, 2006 at 05:47:37PM +0100, Kay Sievers wrote: > On Thu, 2006-11-16 at 15:42 +0100, Cornelia Huck wrote: > > From: Cornelia Huck <cornelia.huck@de.ibm.com> > > > > 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 (OLD_DEVPATH) in addition to the usual values. > > > + sprintf(devpath_string, "OLD_DEVPATH=%s", devpath); > > I think it's easier to understand, if the variable starts with the same > string as original name. I prefer DEVPATH_OLD. > > > +void kobject_uevent_extended(struct kobject *kobj, enum kobject_action action, > > + const char *string) > > I think we should pass an array of env vars here instead of a single > string - you never know ... :) The function could probably be named > kobject_uevent_env() then. I agree, care to respin these? thanks, greg k-h ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Patch -mm 2/2] driver core: Introduce device_move(): move a device to a new parent. 2006-11-17 4:23 ` Greg KH @ 2006-11-20 8:05 ` Cornelia Huck 2006-11-20 12:55 ` [Patch -mm 1/1] " Cornelia Huck 0 siblings, 1 reply; 9+ messages in thread From: Cornelia Huck @ 2006-11-20 8:05 UTC (permalink / raw) To: Greg KH; +Cc: Kay Sievers, linux-kernel, Andrew Morton, Martin Schwidefsky On Thu, 16 Nov 2006 20:23:38 -0800, Greg KH <greg@kroah.com> wrote: > On Thu, Nov 16, 2006 at 05:47:37PM +0100, Kay Sievers wrote: > > On Thu, 2006-11-16 at 15:42 +0100, Cornelia Huck wrote: > > > From: Cornelia Huck <cornelia.huck@de.ibm.com> > > > > > > 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 (OLD_DEVPATH) in addition to the usual values. > > > > > + sprintf(devpath_string, "OLD_DEVPATH=%s", devpath); > > > > I think it's easier to understand, if the variable starts with the same > > string as original name. I prefer DEVPATH_OLD. > > > > > +void kobject_uevent_extended(struct kobject *kobj, enum kobject_action action, > > > + const char *string) > > > > I think we should pass an array of env vars here instead of a single > > string - you never know ... :) The function could probably be named > > kobject_uevent_env() then. > > I agree, care to respin these? OK, makes sense. I'll post a new one today. -- Cornelia Huck Linux for zSeries Developer Tel.: +49-7031-16-4837, Mail: cornelia.huck@de.ibm.com ^ permalink raw reply [flat|nested] 9+ messages in thread
* [Patch -mm 1/1] driver core: Introduce device_move(): move a device to a new parent. 2006-11-20 8:05 ` Cornelia Huck @ 2006-11-20 12:55 ` Cornelia Huck 2006-11-20 14:15 ` Kay Sievers 0 siblings, 1 reply; 9+ messages in thread From: Cornelia Huck @ 2006-11-20 12:55 UTC (permalink / raw) To: Greg KH; +Cc: Kay Sievers, linux-kernel, Andrew Morton, Martin Schwidefsky From: Cornelia Huck <cornelia.huck@de.ibm.com> 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> --- drivers/base/core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/dir.c | 45 +++++++++++++++++++++++ include/linux/device.h | 1 include/linux/kobject.h | 9 ++++ include/linux/sysfs.h | 8 ++++ lib/kobject.c | 49 +++++++++++++++++++++++++ lib/kobject_uevent.c | 47 +++++++++++++++++++----- 7 files changed, 242 insertions(+), 9 deletions(-) --- 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 @@ -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(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 *); @@ -264,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_env(struct kobject *kobj, enum kobject_action action, + int num_envp, 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,11 @@ 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_env(struct kobject *kobj, + enum kobject_action action, + int num_envp, + char *envp[]) +{ } 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/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 @@ -143,6 +146,11 @@ static inline int sysfs_rename_dir(struc 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; --- linux-2.6-CH.orig/lib/kobject.c +++ linux-2.6-CH/lib/kobject.c @@ -355,6 +355,55 @@ 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; + const char *devpath = NULL; + char *devpath_string = NULL; + char *envp[1]; + + 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; + 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, 1, envp); +out: + kobject_put(kobj); + kfree(devpath_string); + kfree(devpath); + return error; +} + +/** * kobject_del - unlink kobject from hierarchy. * @kobj: object. */ --- linux-2.6-CH.orig/lib/kobject_uevent.c +++ linux-2.6-CH/lib/kobject_uevent.c @@ -24,6 +24,7 @@ #define BUFFER_SIZE 2048 /* buffer for the variables */ #define NUM_ENVP 32 /* number of env pointers */ +#define NUM_EXT_ENVP 16 /* number of extended env pointers */ #if defined(CONFIG_HOTPLUG) u64 uevent_seqnum; @@ -50,18 +51,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, + int num_envp, char *envp_ext[]) { char **envp; char *buffer; @@ -76,6 +74,7 @@ void kobject_uevent(struct kobject *kobj char *seq_buff; int i = 0; int retval; + int j; pr_debug("%s\n", __FUNCTION__); @@ -102,7 +101,7 @@ void kobject_uevent(struct kobject *kobj return; /* environment index */ - envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL); + envp = kzalloc((NUM_ENVP + num_envp) * sizeof (char *), GFP_KERNEL); if (!envp) return; @@ -134,7 +133,8 @@ void kobject_uevent(struct kobject *kobj scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1; envp [i++] = scratch; scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1; - + for (j = 0; j < num_envp; j++) + envp[i++] = envp_ext[j]; /* just reserve the space, overwrite it after kset call has returned */ envp[i++] = seq_buff = scratch; scratch += strlen("SEQNUM=18446744073709551616") + 1; @@ -200,9 +200,38 @@ 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, 0, NULL); +} EXPORT_SYMBOL_GPL(kobject_uevent); /** + * kobject_uevent_env - send an uevent with environmental data + * + * @action: action that is happening (usually KOBJ_MOVE) + * @kobj: struct kobject that the action is happening to + * @num_envp: number of fields in environmental data + * @envp: pointer to environmental data + */ +void kobject_uevent_env(struct kobject *kobj, enum kobject_action action, + int num_envp, char *envp[]) +{ + /* Disallow dumb users. */ + if (num_envp > NUM_EXT_ENVP) + return; + do_kobject_uevent(kobj, action, num_envp, envp); +} +EXPORT_SYMBOL_GPL(kobject_uevent_env); + +/** * add_uevent_var - helper for creating event variables * @envp: Pointer to table of environment variables, as passed into * uevent() method. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Patch -mm 1/1] driver core: Introduce device_move(): move a device to a new parent. 2006-11-20 12:55 ` [Patch -mm 1/1] " Cornelia Huck @ 2006-11-20 14:15 ` Kay Sievers 2006-11-20 15:39 ` Cornelia Huck 0 siblings, 1 reply; 9+ messages in thread From: Kay Sievers @ 2006-11-20 14:15 UTC (permalink / raw) To: Cornelia Huck; +Cc: Greg KH, linux-kernel, Andrew Morton, Martin Schwidefsky On Mon, 2006-11-20 at 13:55 +0100, Cornelia Huck wrote: > From: Cornelia Huck <cornelia.huck@de.ibm.com> > > 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. > +void kobject_uevent_env(struct kobject *kobj, enum kobject_action action, > + int num_envp, char *envp[]) We usually use a NULL terminated array for things like this. Does passing the number of entries give us an advantage? > +{ > + /* Disallow dumb users. */ > + if (num_envp > NUM_EXT_ENVP) > + return; Why do we need such a limit? There are still thousand other ways to screw things up. :) And kobject_uevent() can just call kobject_uevent_env(), there is no need for the indirection with do_*, right? Thanks, Kay ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Patch -mm 1/1] driver core: Introduce device_move(): move a device to a new parent. 2006-11-20 14:15 ` Kay Sievers @ 2006-11-20 15:39 ` Cornelia Huck 2006-11-20 16:07 ` Cornelia Huck 0 siblings, 1 reply; 9+ messages in thread From: Cornelia Huck @ 2006-11-20 15:39 UTC (permalink / raw) To: Kay Sievers; +Cc: Greg KH, linux-kernel, Andrew Morton, Martin Schwidefsky On Mon, 20 Nov 2006 15:15:03 +0100, Kay Sievers <kay.sievers@vrfy.org> wrote: > > +void kobject_uevent_env(struct kobject *kobj, enum kobject_action action, > > + int num_envp, char *envp[]) > > We usually use a NULL terminated array for things like this. Does > passing the number of entries give us an advantage? In do_kobject_uevent: envp = kzalloc((NUM_ENVP + num_envp) * sizeof (char *), GFP_KERNEL); We would either need to allocate (NUM_ENVP + NUM_EXT_ENVP) or NUM_ENVP (as before). > > +{ > > + /* Disallow dumb users. */ > > + if (num_envp > NUM_EXT_ENVP) > > + return; > > Why do we need such a limit? There are still thousand other ways to > screw things up. :) If we removed this, we could also use only NUM_ENVP in the allocation above and kill NUM_EXT_ENVP. Hm. > And kobject_uevent() can just call kobject_uevent_env(), there is no > need for the indirection with do_*, right? OK, may look nicer. I'll respin. -- Cornelia Huck Linux for zSeries Developer Tel.: +49-7031-16-4837, Mail: cornelia.huck@de.ibm.com ^ permalink raw reply [flat|nested] 9+ messages in thread
* [Patch -mm 1/1] driver core: Introduce device_move(): move a device to a new parent. 2006-11-20 15:39 ` Cornelia Huck @ 2006-11-20 16:07 ` Cornelia Huck 2006-11-20 22:42 ` Kay Sievers 0 siblings, 1 reply; 9+ messages in thread From: Cornelia Huck @ 2006-11-20 16:07 UTC (permalink / raw) To: Kay Sievers; +Cc: Greg KH, linux-kernel, Andrew Morton, Martin Schwidefsky From: Cornelia Huck <cornelia.huck@de.ibm.com> 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> --- drivers/base/core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/dir.c | 45 +++++++++++++++++++++++ include/linux/device.h | 1 include/linux/kobject.h | 8 ++++ include/linux/sysfs.h | 8 ++++ lib/kobject.c | 50 ++++++++++++++++++++++++++ lib/kobject_uevent.c | 28 ++++++++++++-- 7 files changed, 228 insertions(+), 4 deletions(-) --- 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 @@ -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(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 *); @@ -264,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_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_ __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, --- 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 @@ -143,6 +146,11 @@ static inline int sysfs_rename_dir(struc 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; --- linux-2.6-CH.orig/lib/kobject.c +++ linux-2.6-CH/lib/kobject.c @@ -355,6 +355,56 @@ 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; + 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. * @kobj: object. */ --- linux-2.6-CH.orig/lib/kobject_uevent.c +++ linux-2.6-CH/lib/kobject_uevent.c @@ -50,18 +50,22 @@ 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 + * 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 + * @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 *buffer; @@ -76,6 +80,7 @@ void kobject_uevent(struct kobject *kobj char *seq_buff; int i = 0; int retval; + int j; pr_debug("%s\n", __FUNCTION__); @@ -134,7 +139,8 @@ void kobject_uevent(struct kobject *kobj scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1; envp [i++] = scratch; 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 */ envp[i++] = seq_buff = scratch; scratch += strlen("SEQNUM=18446744073709551616") + 1; @@ -200,6 +206,20 @@ exit: kfree(envp); 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); /** ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Patch -mm 1/1] driver core: Introduce device_move(): move a device to a new parent. 2006-11-20 16:07 ` Cornelia Huck @ 2006-11-20 22:42 ` Kay Sievers 0 siblings, 0 replies; 9+ messages in thread From: Kay Sievers @ 2006-11-20 22:42 UTC (permalink / raw) To: Cornelia Huck; +Cc: Greg KH, linux-kernel, Andrew Morton, Martin Schwidefsky On Mon, 2006-11-20 at 17:07 +0100, Cornelia Huck wrote: > From: Cornelia Huck <cornelia.huck@de.ibm.com> > > 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> Looks fine. Thanks a lot for doing the changes. Kay ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2006-11-20 22:42 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-11-16 14:42 [Patch -mm 2/2] driver core: Introduce device_move(): move a device to a new parent Cornelia Huck 2006-11-16 16:47 ` Kay Sievers 2006-11-17 4:23 ` Greg KH 2006-11-20 8:05 ` Cornelia Huck 2006-11-20 12:55 ` [Patch -mm 1/1] " Cornelia Huck 2006-11-20 14:15 ` Kay Sievers 2006-11-20 15:39 ` Cornelia Huck 2006-11-20 16:07 ` Cornelia Huck 2006-11-20 22:42 ` Kay Sievers
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox