Driver core: change add_uevent_var to use a struct

This changes the uevent buffer functions to use a struct instead of a
long list of parameters. It does no longer require the caller to do the
proper buffer termination and size accounting, which is currently wrong
in some places. It fixes a known bug where parts of the uevent
environment are overwritten because of wrong index calculations.

Many thanks to Mathieu Desnoyers for finding bugs and improving the
error handling.

Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Cc: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


diff --git a/arch/ia64/sn/kernel/tiocx.c b/arch/ia64/sn/kernel/tiocx.c
index 5a289e4..a88eba3 100644
--- a/arch/ia64/sn/kernel/tiocx.c
+++ b/arch/ia64/sn/kernel/tiocx.c
@@ -66,8 +66,7 @@
 
 }
 
-static int tiocx_uevent(struct device *dev, char **envp, int num_envp,
-			 char *buffer, int buffer_size)
+static int tiocx_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return -ENODEV;
 }
diff --git a/arch/powerpc/kernel/of_device.c b/arch/powerpc/kernel/of_device.c
index 89b911e..8f3db32 100644
--- a/arch/powerpc/kernel/of_device.c
+++ b/arch/powerpc/kernel/of_device.c
@@ -57,26 +57,21 @@
 	return tsize;
 }
 
-int of_device_uevent(struct device *dev,
-		char **envp, int num_envp, char *buffer, int buffer_size)
+int of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct of_device *ofdev;
 	const char *compat;
-	int i = 0, length = 0, seen = 0, cplen, sl;
+	int seen = 0, cplen, sl;
 
 	if (!dev)
 		return -ENODEV;
 
 	ofdev = to_of_device(dev);
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "OF_NAME=%s", ofdev->node->name))
+	if (add_uevent_var(env, "OF_NAME=%s", ofdev->node->name))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "OF_TYPE=%s", ofdev->node->type))
+	if (add_uevent_var(env, "OF_TYPE=%s", ofdev->node->type))
 		return -ENOMEM;
 
         /* Since the compatible field can contain pretty much anything
@@ -85,9 +80,7 @@
 
 	compat = of_get_property(ofdev->node, "compatible", &cplen);
 	while (compat && *compat && cplen > 0) {
-		if (add_uevent_var(envp, num_envp, &i,
-				   buffer, buffer_size, &length,
-				   "OF_COMPATIBLE_%d=%s", seen, compat))
+		if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat))
 			return -ENOMEM;
 
 		sl = strlen (compat) + 1;
@@ -96,25 +89,17 @@
 		seen++;
 	}
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "OF_COMPATIBLE_N=%d", seen))
+	if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen))
 		return -ENOMEM;
 
 	/* modalias is trickier, we add it in 2 steps */
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "MODALIAS="))
+	if (add_uevent_var(env, "MODALIAS="))
 		return -ENOMEM;
-
-	sl = of_device_get_modalias(ofdev, &buffer[length-1],
-					buffer_size-length);
-	if (sl >= (buffer_size-length))
+	sl = of_device_get_modalias(ofdev, &env->buf[env->buflen-1],
+				    sizeof(env->buf) - env->buflen);
+	if (sl >= (sizeof(env->buf) - env->buflen))
 		return -ENOMEM;
-
-	length += sl;
-
-	envp[i] = NULL;
+	env->buflen += sl;
 
 	return 0;
 }
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index cb22a35..19a5656 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -317,30 +317,20 @@
 	return (ids != NULL) && (vio_match_device(ids, vio_dev) != NULL);
 }
 
-static int vio_hotplug(struct device *dev, char **envp, int num_envp,
-			char *buffer, int buffer_size)
+static int vio_hotplug(struct device *dev, struct kobj_uevent_env *env)
 {
 	const struct vio_dev *vio_dev = to_vio_dev(dev);
 	struct device_node *dn;
 	const char *cp;
-	int length;
-
-	if (!num_envp)
-		return -ENOMEM;
 
 	dn = dev->archdata.of_node;
 	if (!dn)
 		return -ENODEV;
-	cp = of_get_property(dn, "compatible", &length);
+	cp = of_get_property(dn, "compatible", NULL);
 	if (!cp)
 		return -ENODEV;
 
-	envp[0] = buffer;
-	length = scnprintf(buffer, buffer_size, "MODALIAS=vio:T%sS%s",
-				vio_dev->type, cp);
-	if ((buffer_size - length) <= 0)
-		return -ENOMEM;
-	envp[1] = NULL;
+	add_uevent_var(env, "MODALIAS=vio:T%sS%s", vio_dev->type, cp);
 	return 0;
 }
 
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
index 4bb634a..ea0b2c7 100644
--- a/arch/powerpc/platforms/ps3/system-bus.c
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -437,18 +437,13 @@
 	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
 }
 
-static int ps3_system_bus_uevent(struct device *_dev, char **envp,
-				 int num_envp, char *buffer, int buffer_size)
+static int ps3_system_bus_uevent(struct device *_dev, struct kobj_uevent_env *env)
 {
 	struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
 	int i = 0, length = 0;
 
-	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-			   &length, "MODALIAS=ps3:%d",
-			   dev->match_id))
+	if (add_uevent_var(env, "MODALIAS=ps3:%d", dev->match_id))
 		return -ENOMEM;
-
-	envp[i] = NULL;
 	return 0;
 }
 
diff --git a/block/genhd.c b/block/genhd.c
index 3af1e7a..e609996 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -540,61 +540,42 @@
 	return ((ktype == &ktype_block) || (ktype == &ktype_part));
 }
 
-static int block_uevent(struct kset *kset, struct kobject *kobj, char **envp,
-			 int num_envp, char *buffer, int buffer_size)
+static int block_uevent(struct kset *kset, struct kobject *kobj,
+			struct kobj_uevent_env *env)
 {
 	struct kobj_type *ktype = get_ktype(kobj);
 	struct device *physdev;
 	struct gendisk *disk;
 	struct hd_struct *part;
-	int length = 0;
-	int i = 0;
 
 	if (ktype == &ktype_block) {
 		disk = container_of(kobj, struct gendisk, kobj);
-		add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-			       &length, "MINOR=%u", disk->first_minor);
+		add_uevent_var(env, "MINOR=%u", disk->first_minor);
 	} else if (ktype == &ktype_part) {
 		disk = container_of(kobj->parent, struct gendisk, kobj);
 		part = container_of(kobj, struct hd_struct, kobj);
-		add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-			       &length, "MINOR=%u",
+		add_uevent_var(env, "MINOR=%u",
 			       disk->first_minor + part->partno);
 	} else
 		return 0;
 
-	add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-		       "MAJOR=%u", disk->major);
+	add_uevent_var(env, "MAJOR=%u", disk->major);
 
 	/* add physical device, backing this device  */
 	physdev = disk->driverfs_dev;
 	if (physdev) {
 		char *path = kobject_get_path(&physdev->kobj, GFP_KERNEL);
 
-		add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-			       &length, "PHYSDEVPATH=%s", path);
+		add_uevent_var(env, "PHYSDEVPATH=%s", path);
 		kfree(path);
 
 		if (physdev->bus)
-			add_uevent_var(envp, num_envp, &i,
-				       buffer, buffer_size, &length,
-				       "PHYSDEVBUS=%s",
-				       physdev->bus->name);
+			add_uevent_var(env, "PHYSDEVBUS=%s", physdev->bus->name);
 
 		if (physdev->driver)
-			add_uevent_var(envp, num_envp, &i,
-				       buffer, buffer_size, &length,
-				       "PHYSDEVDRIVER=%s",
-				       physdev->driver->name);
+			add_uevent_var(env, physdev->driver->name);
 	}
 
-	/* terminate, set to next free slot, shrink available space */
-	envp[i] = NULL;
-	envp = &envp[i];
-	num_envp -= i;
-	buffer = &buffer[length];
-	buffer_size -= length;
-
 	return 0;
 }
 
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 64620d6..5b4d462 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -319,16 +319,18 @@
 	return !acpi_match_device_ids(acpi_dev, acpi_drv->ids);
 }
 
-static int acpi_device_uevent(struct device *dev, char **envp, int num_envp,
-			      char *buffer, int buffer_size)
+static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct acpi_device *acpi_dev = to_acpi_device(dev);
+	int len;
 
-	strcpy(buffer, "MODALIAS=");
-	if (create_modalias(acpi_dev, buffer + 9, buffer_size - 9) > 0) {
-		envp[0] = buffer;
-		envp[1] = NULL;
-	}
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+	len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],
+			      sizeof(env->buf) - env->buflen);
+	if (len >= (sizeof(env->buf) - env->buflen))
+		return -ENOMEM;
+	env->buflen += len;
 	return 0;
 }
 
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 268e301..6b94fb7 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -44,15 +44,12 @@
 }
 
 #ifdef CONFIG_HOTPLUG
-static int amba_uevent(struct device *dev, char **envp, int nr_env, char *buf, int bufsz)
+static int amba_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct amba_device *pcdev = to_amba_device(dev);
-	int retval = 0, i = 0, len = 0;
+	int retval = 0;
 
-	retval = add_uevent_var(envp, nr_env, &i,
-				buf, bufsz, &len,
-				"AMBA_ID=%08x", pcdev->periphid);
-	envp[i] = NULL;
+	retval = add_uevent_var(env, "AMBA_ID=%08x", pcdev->periphid);
 	return retval;
 }
 #else
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 4d22226..ecd6336 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -180,8 +180,7 @@
 
 /* needed to allow these devices to have parent class devices */
 static int class_device_create_uevent(struct class_device *class_dev,
-				       char **envp, int num_envp,
-				       char *buffer, int buffer_size)
+				      struct kobj_uevent_env *env)
 {
 	pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id);
 	return 0;
@@ -403,64 +402,43 @@
 { }
 #endif
 
-static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
-			 int num_envp, char *buffer, int buffer_size)
+static int class_uevent(struct kset *kset, struct kobject *kobj,
+			struct kobj_uevent_env *env)
 {
 	struct class_device *class_dev = to_class_dev(kobj);
 	struct device *dev = class_dev->dev;
-	int i = 0;
-	int length = 0;
 	int retval = 0;
 
 	pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
 
 	if (MAJOR(class_dev->devt)) {
-		add_uevent_var(envp, num_envp, &i,
-			       buffer, buffer_size, &length,
-			       "MAJOR=%u", MAJOR(class_dev->devt));
+		add_uevent_var(env, "MAJOR=%u", MAJOR(class_dev->devt));
 
-		add_uevent_var(envp, num_envp, &i,
-			       buffer, buffer_size, &length,
-			       "MINOR=%u", MINOR(class_dev->devt));
+		add_uevent_var(env, "MINOR=%u", MINOR(class_dev->devt));
 	}
 
 	if (dev) {
 		const char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
 		if (path) {
-			add_uevent_var(envp, num_envp, &i,
-				       buffer, buffer_size, &length,
-				       "PHYSDEVPATH=%s", path);
+			add_uevent_var(env, "PHYSDEVPATH=%s", path);
 			kfree(path);
 		}
 
 		if (dev->bus)
-			add_uevent_var(envp, num_envp, &i,
-				       buffer, buffer_size, &length,
-				       "PHYSDEVBUS=%s", dev->bus->name);
+			add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
 
 		if (dev->driver)
-			add_uevent_var(envp, num_envp, &i,
-				       buffer, buffer_size, &length,
-				       "PHYSDEVDRIVER=%s", dev->driver->name);
+			add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name);
 	}
 
-	/* terminate, set to next free slot, shrink available space */
-	envp[i] = NULL;
-	envp = &envp[i];
-	num_envp -= i;
-	buffer = &buffer[length];
-	buffer_size -= length;
-
 	if (class_dev->uevent) {
 		/* have the class device specific function add its stuff */
-		retval = class_dev->uevent(class_dev, envp, num_envp,
-					    buffer, buffer_size);
+		retval = class_dev->uevent(class_dev, env);
 		if (retval)
 			pr_debug("class_dev->uevent() returned %d\n", retval);
 	} else if (class_dev->class->uevent) {
 		/* have the class specific function add its stuff */
-		retval = class_dev->class->uevent(class_dev, envp, num_envp,
-						   buffer, buffer_size);
+		retval = class_dev->class->uevent(class_dev, env);
 		if (retval)
 			pr_debug("class->uevent() returned %d\n", retval);
 	}
diff --git a/drivers/base/core.c b/drivers/base/core.c
index ec86d6f..d487c03 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -141,33 +141,23 @@
 	return NULL;
 }
 
-static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
-			int num_envp, char *buffer, int buffer_size)
+static int dev_uevent(struct kset *kset, struct kobject *kobj,
+		      struct kobj_uevent_env *env)
 {
 	struct device *dev = to_dev(kobj);
-	int i = 0;
-	int length = 0;
 	int retval = 0;
 
 	/* add the major/minor if present */
 	if (MAJOR(dev->devt)) {
-		add_uevent_var(envp, num_envp, &i,
-			       buffer, buffer_size, &length,
-			       "MAJOR=%u", MAJOR(dev->devt));
-		add_uevent_var(envp, num_envp, &i,
-			       buffer, buffer_size, &length,
-			       "MINOR=%u", MINOR(dev->devt));
+		add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
+		add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
 	}
 
 	if (dev->type && dev->type->name)
-		add_uevent_var(envp, num_envp, &i,
-			       buffer, buffer_size, &length,
-			       "DEVTYPE=%s", dev->type->name);
+		add_uevent_var(env, "DEVTYPE=%s", dev->type->name);
 
 	if (dev->driver)
-		add_uevent_var(envp, num_envp, &i,
-			       buffer, buffer_size, &length,
-			       "DRIVER=%s", dev->driver->name);
+		add_uevent_var(env, "DRIVER=%s", dev->driver->name);
 
 #ifdef CONFIG_SYSFS_DEPRECATED
 	if (dev->class) {
@@ -181,59 +171,43 @@
 
 			path = kobject_get_path(&parent->kobj, GFP_KERNEL);
 			if (path) {
-				add_uevent_var(envp, num_envp, &i,
-					       buffer, buffer_size, &length,
-					       "PHYSDEVPATH=%s", path);
+				add_uevent_var(env, "PHYSDEVPATH=%s", path);
 				kfree(path);
 			}
 
-			add_uevent_var(envp, num_envp, &i,
-				       buffer, buffer_size, &length,
-				       "PHYSDEVBUS=%s", parent->bus->name);
+			add_uevent_var(env, "PHYSDEVBUS=%s", parent->bus->name);
 
 			if (parent->driver)
-				add_uevent_var(envp, num_envp, &i,
-					       buffer, buffer_size, &length,
-					       "PHYSDEVDRIVER=%s", parent->driver->name);
+				add_uevent_var(env, "PHYSDEVDRIVER=%s",
+					       parent->driver->name);
 		}
 	} else if (dev->bus) {
-		add_uevent_var(envp, num_envp, &i,
-			       buffer, buffer_size, &length,
-			       "PHYSDEVBUS=%s", dev->bus->name);
+		add_uevent_var(env, "PHYSDEVBUS=%s", dev->bus->name);
 
 		if (dev->driver)
-			add_uevent_var(envp, num_envp, &i,
-				       buffer, buffer_size, &length,
-				       "PHYSDEVDRIVER=%s", dev->driver->name);
+			add_uevent_var(env, "PHYSDEVDRIVER=%s", dev->driver->name);
 	}
 #endif
 
-	/* terminate, set to next free slot, shrink available space */
-	envp[i] = NULL;
-	envp = &envp[i];
-	num_envp -= i;
-	buffer = &buffer[length];
-	buffer_size -= length;
-
+	/* have the bus specific function add its stuff */
 	if (dev->bus && dev->bus->uevent) {
-		/* have the bus specific function add its stuff */
-		retval = dev->bus->uevent(dev, envp, num_envp, buffer, buffer_size);
+		retval = dev->bus->uevent(dev, env);
 		if (retval)
 			pr_debug ("%s: bus uevent() returned %d\n",
 				  __FUNCTION__, retval);
 	}
 
+	/* have the class specific function add its stuff */
 	if (dev->class && dev->class->dev_uevent) {
-		/* have the class specific function add its stuff */
-		retval = dev->class->dev_uevent(dev, envp, num_envp, buffer, buffer_size);
+		retval = dev->class->dev_uevent(dev, env);
 		if (retval)
 			pr_debug("%s: class uevent() returned %d\n",
 				 __FUNCTION__, retval);
 	}
 
+	/* have the device type specific fuction add its stuff */
 	if (dev->type && dev->type->uevent) {
-		/* have the device type specific fuction add its stuff */
-		retval = dev->type->uevent(dev, envp, num_envp, buffer, buffer_size);
+		retval = dev->type->uevent(dev, env);
 		if (retval)
 			pr_debug("%s: dev_type uevent() returned %d\n",
 				 __FUNCTION__, retval);
@@ -253,9 +227,7 @@
 {
 	struct kobject *top_kobj;
 	struct kset *kset;
-	char *envp[32];
-	char *data = NULL;
-	char *pos;
+	struct kobj_uevent_env *env = NULL;
 	int i;
 	size_t count = 0;
 	int retval;
@@ -278,26 +250,20 @@
 		if (!kset->uevent_ops->filter(kset, &dev->kobj))
 			goto out;
 
-	data = (char *)get_zeroed_page(GFP_KERNEL);
-	if (!data)
+	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
+	if (!env)
 		return -ENOMEM;
 
 	/* let the kset specific function add its keys */
-	pos = data;
-	memset(envp, 0, sizeof(envp));
-	retval = kset->uevent_ops->uevent(kset, &dev->kobj,
-					  envp, ARRAY_SIZE(envp),
-					  pos, PAGE_SIZE);
+	retval = kset->uevent_ops->uevent(kset, &dev->kobj, env);
 	if (retval)
 		goto out;
 
 	/* copy keys to file */
-	for (i = 0; envp[i]; i++) {
-		pos = &buf[count];
-		count += sprintf(pos, "%s\n", envp[i]);
-	}
+	for (i = 0; i < env->envp_idx; i++)
+		count += sprintf(&buf[count], "%s\n", env->envp[i]);
 out:
-	free_page((unsigned long)data);
+	kfree(env);
 	return count;
 }
 
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index b24efd4..4a1b9bf 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -88,19 +88,14 @@
 
 static void fw_dev_release(struct device *dev);
 
-static int firmware_uevent(struct device *dev, char **envp, int num_envp,
-			   char *buffer, int buffer_size)
+static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct firmware_priv *fw_priv = dev_get_drvdata(dev);
-	int i = 0, len = 0;
 
-	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			   "FIRMWARE=%s", fw_priv->fw_id))
+	if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
 		return -ENOMEM;
-	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			   "TIMEOUT=%i", loading_timeout))
+	if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
 		return -ENOMEM;
-	envp[i] = NULL;
 
 	return 0;
 }
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 74b9679..cb99dae 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -34,8 +34,7 @@
 	return MEMORY_CLASS_NAME;
 }
 
-static int memory_uevent(struct kset *kset, struct kobject *kobj, char **envp,
-			int num_envp, char *buffer, int buffer_size)
+static int memory_uevent(struct kset *kset, struct kobj_uevent_env *env)
 {
 	int retval = 0;
 
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 9bfc434..a2e3910 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -529,13 +529,11 @@
 	__ATTR_NULL,
 };
 
-static int platform_uevent(struct device *dev, char **envp, int num_envp,
-		char *buffer, int buffer_size)
+static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct platform_device	*pdev = to_platform_device(dev);
 
-	envp[0] = buffer;
-	snprintf(buffer, buffer_size, "MODALIAS=platform:%s", pdev->name);
+	add_uevent_var(env, "MODALIAS=platform:%s", pdev->name);
 	return 0;
 }
 
diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c
index d944647..4d4a473 100644
--- a/drivers/eisa/eisa-bus.c
+++ b/drivers/eisa/eisa-bus.c
@@ -128,16 +128,11 @@
 	return 0;
 }
 
-static int eisa_bus_uevent(struct device *dev, char **envp, int num_envp,
-			   char *buffer, int buffer_size)
+static int eisa_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct eisa_device *edev = to_eisa_device(dev);
-	int i = 0;
-	int length = 0;
 
-	add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-		       "MODALIAS=" EISA_DEVICE_MODALIAS_FMT, edev->id.sig);
-	envp[i] = NULL;
+	add_uevent_var(env, "MODALIAS=" EISA_DEVICE_MODALIAS_FMT, edev->id.sig);
 	return 0;
 }
 
diff --git a/drivers/firewire/fw-device.c b/drivers/firewire/fw-device.c
index 2b65863..56681b3 100644
--- a/drivers/firewire/fw-device.c
+++ b/drivers/firewire/fw-device.c
@@ -130,23 +130,16 @@
 }
 
 static int
-fw_unit_uevent(struct device *dev, char **envp, int num_envp,
-	       char *buffer, int buffer_size)
+fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct fw_unit *unit = fw_unit(dev);
 	char modalias[64];
-	int length = 0;
-	int i = 0;
 
 	get_modalias(unit, modalias, sizeof(modalias));
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "MODALIAS=%s", modalias))
+	if (add_uevent_var(env, "MODALIAS=%s", modalias))
 		return -ENOMEM;
 
-	envp[i] = NULL;
-
 	return 0;
 }
 
diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c
index 59c3b5a..2678098 100644
--- a/drivers/firmware/dmi-id.c
+++ b/drivers/firmware/dmi-id.c
@@ -134,14 +134,17 @@
 	NULL
 };
 
-static int dmi_dev_uevent(struct device *dev, char **envp,
-			    int num_envp, char *buffer, int buffer_size)
+static int dmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
-	strcpy(buffer, "MODALIAS=");
-	get_modalias(buffer+9, buffer_size-9);
-	envp[0] = buffer;
-	envp[1] = NULL;
+	ssize_t len;
 
+	if (add_uevent_var(env, "MODALIAS="))
+		return -ENOMEM;
+	len = get_modalias(&env->buf[env->buflen - 1],
+			   sizeof(env->buf) - env->buflen);
+	if (len >= (sizeof(env->buf) - env->buflen))
+		return -ENOMEM;
+	env->buflen += len;
 	return 0;
 }
 
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index d663e69..910a62d 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -67,20 +67,16 @@
 #ifdef	CONFIG_HOTPLUG
 
 /* uevent helps with hotplug: modprobe -q $(MODALIAS) */
-static int i2c_device_uevent(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size)
+static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct i2c_client	*client = to_i2c_client(dev);
-	int			i = 0, length = 0;
 
 	/* by definition, legacy drivers can't hotplug */
 	if (dev->driver || !client->driver_name)
 		return 0;
 
-	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-			"MODALIAS=%s", client->driver_name))
+	if (add_uevent_var(env, "MODALIAS=%s", client->driver_name))
 		return -ENOMEM;
-	envp[i] = NULL;
 	dev_dbg(dev, "uevent\n");
 	return 0;
 }
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index e96212c..a96a8b1 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -1663,20 +1663,13 @@
 	__ATTR_NULL
 };
 
-static int ide_uevent(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size)
+static int ide_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	ide_drive_t *drive = to_ide_device(dev);
-	int i = 0;
-	int length = 0;
 
-	add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-		       "MEDIA=%s", media_string(drive));
-	add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-		       "DRIVENAME=%s", drive->name);
-	add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-		       "MODALIAS=ide:m-%s", media_string(drive));
-	envp[i] = NULL;
+	add_uevent_var(env, "MEDIA=%s", media_string(drive));
+	add_uevent_var(env, "DRIVENAME=%s", drive->name);
+	add_uevent_var(env, "MODALIAS=ide:m-%s", media_string(drive));
 	return 0;
 }
 
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index 2ffd534..1939fee 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -153,8 +153,7 @@
 };
 
 static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
-static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
-			  char *buffer, int buffer_size);
+static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env);
 static void nodemgr_resume_ne(struct node_entry *ne);
 static void nodemgr_remove_ne(struct node_entry *ne);
 static struct node_entry *find_entry_by_guid(u64 guid);
@@ -1160,12 +1159,9 @@
 
 #ifdef CONFIG_HOTPLUG
 
-static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
-			  char *buffer, int buffer_size)
+static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct unit_directory *ud;
-	int i = 0;
-	int length = 0;
 	int retval = 0;
 	/* ieee1394:venNmoNspNverN */
 	char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1];
@@ -1180,9 +1176,7 @@
 
 #define PUT_ENVP(fmt,val) 					\
 do {								\
-	retval = add_uevent_var(envp, num_envp, &i,		\
-				buffer, buffer_size, &length,	\
-				fmt, val);			\
+	retval = add_uevent_var(env, fmt, val);		\
 	if (retval)						\
 		return retval;					\
 } while (0)
@@ -1201,15 +1195,12 @@
 
 #undef PUT_ENVP
 
-	envp[i] = NULL;
-
 	return 0;
 }
 
 #else
 
-static int nodemgr_uevent(struct device *dev, char **envp, int num_envp,
-			  char *buffer, int buffer_size)
+static int nodemgr_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return -ENODEV;
 }
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 70b77ae..3d40506 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -434,21 +434,18 @@
 	kfree(dev);
 }
 
-static int ib_device_uevent(struct class_device *cdev, char **envp,
-			    int num_envp, char *buf, int size)
+static int ib_device_uevent(struct class_device *cdev,
+			    struct kobj_uevent_env *env)
 {
 	struct ib_device *dev = container_of(cdev, struct ib_device, class_dev);
-	int i = 0, len = 0;
 
-	if (add_uevent_var(envp, num_envp, &i, buf, size, &len,
-			   "NAME=%s", dev->name))
+	if (add_uevent_var(env, "NAME=%s", dev->name))
 		return -ENOMEM;
 
 	/*
 	 * It would be nice to pass the node GUID with the event...
 	 */
 
-	envp[i] = NULL;
 	return 0;
 }
 
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 5fe7555..5dc361c 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -859,87 +859,66 @@
  * Input uevent interface - loading event handlers based on
  * device bitfields.
  */
-static int input_add_uevent_bm_var(char **envp, int num_envp, int *cur_index,
-				   char *buffer, int buffer_size, int *cur_len,
+static int input_add_uevent_bm_var(struct kobj_uevent_env *env,
 				   const char *name, unsigned long *bitmap, int max)
 {
-	if (*cur_index >= num_envp - 1)
+	int len;
+
+	if (add_uevent_var(env, "%s=", name))
 		return -ENOMEM;
 
-	envp[*cur_index] = buffer + *cur_len;
-
-	*cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0), name);
-	if (*cur_len >= buffer_size)
+	len = input_print_bitmap(&env->buf[env->buflen - 1],
+				 sizeof(env->buf) - env->buflen,
+				 bitmap, max, 0);
+	if (len >= (sizeof(env->buf) - env->buflen))
 		return -ENOMEM;
 
-	*cur_len += input_print_bitmap(buffer + *cur_len,
-					max(buffer_size - *cur_len, 0),
-					bitmap, max, 0) + 1;
-	if (*cur_len > buffer_size)
-		return -ENOMEM;
-
-	(*cur_index)++;
+	env->buflen += len;
 	return 0;
 }
 
-static int input_add_uevent_modalias_var(char **envp, int num_envp, int *cur_index,
-					 char *buffer, int buffer_size, int *cur_len,
+static int input_add_uevent_modalias_var(struct kobj_uevent_env *env,
 					 struct input_dev *dev)
 {
-	if (*cur_index >= num_envp - 1)
+	int len;
+
+	if (add_uevent_var(env, "MODALIAS="))
 		return -ENOMEM;
 
-	envp[*cur_index] = buffer + *cur_len;
-
-	*cur_len += snprintf(buffer + *cur_len, max(buffer_size - *cur_len, 0),
-			     "MODALIAS=");
-	if (*cur_len >= buffer_size)
+	len = input_print_modalias(&env->buf[env->buflen - 1],
+				   sizeof(env->buf) - env->buflen,
+				   dev, 0);
+	if (len >= (sizeof(env->buf) - env->buflen))
 		return -ENOMEM;
 
-	*cur_len += input_print_modalias(buffer + *cur_len,
-					 max(buffer_size - *cur_len, 0),
-					 dev, 0) + 1;
-	if (*cur_len > buffer_size)
-		return -ENOMEM;
-
-	(*cur_index)++;
+	env->buflen += len;
 	return 0;
 }
 
 #define INPUT_ADD_HOTPLUG_VAR(fmt, val...)				\
 	do {								\
-		int err = add_uevent_var(envp, num_envp, &i,		\
-					buffer, buffer_size, &len,	\
-					fmt, val);			\
+		int err = add_uevent_var(env, fmt, val);		\
 		if (err)						\
 			return err;					\
 	} while (0)
 
 #define INPUT_ADD_HOTPLUG_BM_VAR(name, bm, max)				\
 	do {								\
-		int err = input_add_uevent_bm_var(envp, num_envp, &i,	\
-					buffer, buffer_size, &len,	\
-					name, bm, max);			\
+		int err = input_add_uevent_bm_var(env, name, bm, max);	\
 		if (err)						\
 			return err;					\
 	} while (0)
 
 #define INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev)				\
 	do {								\
-		int err = input_add_uevent_modalias_var(envp,		\
-					num_envp, &i,			\
-					buffer, buffer_size, &len,	\
-					dev);				\
+		int err = input_add_uevent_modalias_var(env, dev);	\
 		if (err)						\
 			return err;					\
 	} while (0)
 
-static int input_dev_uevent(struct device *device, char **envp,
-			    int num_envp, char *buffer, int buffer_size)
+static int input_dev_uevent(struct device *device, struct kobj_uevent_env *env)
 {
 	struct input_dev *dev = to_input_dev(device);
-	int i = 0;
-	int len = 0;
 
 	INPUT_ADD_HOTPLUG_VAR("PRODUCT=%x/%x/%x/%x",
 				dev->id.bustype, dev->id.vendor,
@@ -971,7 +950,6 @@
 
 	INPUT_ADD_HOTPLUG_MODALIAS_VAR(dev);
 
-	envp[i] = NULL;
 	return 0;
 }
 
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index 372ca49..b3bc15a 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -876,18 +876,14 @@
 
 #define SERIO_ADD_UEVENT_VAR(fmt, val...)				\
 	do {								\
-		int err = add_uevent_var(envp, num_envp, &i,	\
-					buffer, buffer_size, &len,	\
-					fmt, val);			\
+		int err = add_uevent_var(env, fmt, val);		\
 		if (err)						\
 			return err;					\
 	} while (0)
 
-static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct serio *serio;
-	int i = 0;
-	int len = 0;
 
 	if (!dev)
 		return -ENODEV;
@@ -900,7 +896,6 @@
 	SERIO_ADD_UEVENT_VAR("SERIO_EXTRA=%02x", serio->id.extra);
 	SERIO_ADD_UEVENT_VAR("MODALIAS=serio:ty%02Xpr%02Xid%02Xex%02X",
 				serio->id.type, serio->id.proto, serio->id.id, serio->id.extra);
-	envp[i] = NULL;
 
 	return 0;
 }
@@ -908,7 +903,7 @@
 
 #else
 
-static int serio_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return -ENODEV;
 }
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 7a78d6b..2ee3c30 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -905,8 +905,8 @@
 }
 
 
-static int pvr2_sysfs_hotplug(struct device *cd,char **envp,
-			      int numenvp,char *buf,int size)
+static int pvr2_sysfs_hotplug(struct device *d,
+			      struct kobj_uevent_env *env)
 {
 	/* Even though we don't do anything here, we still need this function
 	   because sysfs will still try to call it. */
diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c
index d195fb0..8f77949 100644
--- a/drivers/misc/tifm_core.c
+++ b/drivers/misc/tifm_core.c
@@ -57,16 +57,11 @@
 	return 0;
 }
 
-static int tifm_uevent(struct device *dev, char **envp, int num_envp,
-		       char *buffer, int buffer_size)
+static int tifm_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev);
-	int i = 0;
-	int length = 0;
 
-	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-			   "TIFM_CARD_TYPE=%s",
-			   tifm_media_type_name(sock->type, 1)))
+	if (add_uevent_var(env, "TIFM_CARD_TYPE=%s", tifm_media_type_name(sock->type, 1)))
 		return -ENOMEM;
 
 	return 0;
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 8d6f601..b0c22ca 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -58,12 +58,11 @@
 }
 
 static int
-mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
-		int buf_size)
+mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct mmc_card *card = dev_to_mmc_card(dev);
 	const char *type;
-	int i = 0, length = 0;
+	int retval = 0;
 
 	switch (card->type) {
 	case MMC_TYPE_MMC:
@@ -80,20 +79,14 @@
 	}
 
 	if (type) {
-		if (add_uevent_var(envp, num_envp, &i,
-				buf, buf_size, &length,
-				"MMC_TYPE=%s", type))
-			return -ENOMEM;
+		retval = add_uevent_var(env, "MMC_TYPE=%s", type);
+		if (retval)
+			return retval;
 	}
 
-	if (add_uevent_var(envp, num_envp, &i,
-			buf, buf_size, &length,
-			"MMC_NAME=%s", mmc_card_name(card)))
-		return -ENOMEM;
+	retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
 
-	envp[i] = NULL;
-
-	return 0;
+	return retval;
 }
 
 static int mmc_bus_probe(struct device *dev)
diff --git a/drivers/pci/hotplug.c b/drivers/pci/hotplug.c
index 1c97e7d..2b5352a 100644
--- a/drivers/pci/hotplug.c
+++ b/drivers/pci/hotplug.c
@@ -3,12 +3,9 @@
 #include <linux/module.h>
 #include "pci.h"
 
-int pci_uevent(struct device *dev, char **envp, int num_envp,
-	       char *buffer, int buffer_size)
+int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct pci_dev *pdev;
-	int i = 0;
-	int length = 0;
 
 	if (!dev)
 		return -ENODEV;
@@ -17,37 +14,24 @@
 	if (!pdev)
 		return -ENODEV;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "PCI_CLASS=%04X", pdev->class))
+	if (add_uevent_var(env, "PCI_CLASS=%04X", pdev->class))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "PCI_ID=%04X:%04X", pdev->vendor, pdev->device))
+	if (add_uevent_var(env, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor,
+	if (add_uevent_var(env, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor,
 			   pdev->subsystem_device))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "PCI_SLOT_NAME=%s", pci_name(pdev)))
+	if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev)))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x",
+	if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x",
 			   pdev->vendor, pdev->device,
 			   pdev->subsystem_vendor, pdev->subsystem_device,
 			   (u8)(pdev->class >> 16), (u8)(pdev->class >> 8),
 			   (u8)(pdev->class)))
 		return -ENOMEM;
-
-	envp[i] = NULL;
-
 	return 0;
 }
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 004bc24..f61be3a 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -532,8 +532,7 @@
 }
 
 #ifndef CONFIG_HOTPLUG
-int pci_uevent(struct device *dev, char **envp, int num_envp,
-	       char *buffer, int buffer_size)
+int pci_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return -ENODEV;
 }
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 4c36e80..b3a7d5b 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -1,7 +1,6 @@
 /* Functions internal to the PCI core code */
 
-extern int pci_uevent(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size);
+extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env);
 extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
 extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
 extern void pci_cleanup_rom(struct pci_dev *dev);
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index f8b13f0..a0aca46 100644
--- a/drivers/pcmcia/cs.c
+++ b/drivers/pcmcia/cs.c
@@ -907,18 +907,14 @@
 EXPORT_SYMBOL(pcmcia_insert_card);
 
 
-static int pcmcia_socket_uevent(struct device *dev, char **envp,
-			        int num_envp, char *buffer, int buffer_size)
+static int pcmcia_socket_uevent(struct device *dev,
+				struct kobj_uevent_env *env)
 {
 	struct pcmcia_socket *s = container_of(dev, struct pcmcia_socket, dev);
-	int i = 0, length = 0;
 
-	if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-			   &length, "SOCKET_NO=%u", s->sock))
+	if (add_uevent_var(env, "SOCKET_NO=%u", s->sock))
 		return -ENOMEM;
 
-	envp[i] = NULL;
-
 	return 0;
 }
 
diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
index a996071..55baa1f 100644
--- a/drivers/pcmcia/ds.c
+++ b/drivers/pcmcia/ds.c
@@ -1064,11 +1064,10 @@
 
 #ifdef CONFIG_HOTPLUG
 
-static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
-			     char *buffer, int buffer_size)
+static int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct pcmcia_device *p_dev;
-	int i, length = 0;
+	int i;
 	u32 hash[4] = { 0, 0, 0, 0};
 
 	if (!dev)
@@ -1083,23 +1082,13 @@
 		hash[i] = crc32(0, p_dev->prod_id[i], strlen(p_dev->prod_id[i]));
 	}
 
-	i = 0;
-
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "SOCKET_NO=%u",
-			   p_dev->socket->sock))
+	if (add_uevent_var(env, "SOCKET_NO=%u", p_dev->socket->sock))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "DEVICE_NO=%02X",
-			   p_dev->device_no))
+	if (add_uevent_var(env, "DEVICE_NO=%02X", p_dev->device_no))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
+	if (add_uevent_var(env, "MODALIAS=pcmcia:m%04Xc%04Xf%02Xfn%02Xpfn%02X"
 			   "pa%08Xpb%08Xpc%08Xpd%08X",
 			   p_dev->has_manf_id ? p_dev->manf_id : 0,
 			   p_dev->has_card_id ? p_dev->card_id : 0,
@@ -1112,15 +1101,12 @@
 			   hash[3]))
 		return -ENOMEM;
 
-	envp[i] = NULL;
-
 	return 0;
 }
 
 #else
 
-static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp,
-			      char *buffer, int buffer_size)
+static int pcmcia_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return -ENODEV;
 }
diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
index a9880d4..f38ba48 100644
--- a/drivers/power/power_supply.h
+++ b/drivers/power/power_supply.h
@@ -14,8 +14,7 @@
 
 extern int power_supply_create_attrs(struct power_supply *psy);
 extern void power_supply_remove_attrs(struct power_supply *psy);
-extern int power_supply_uevent(struct device *dev, char **envp, int num_envp,
-			       char *buffer, int buffer_size);
+extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
 
 #else
 
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index de3155b..249f61b 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -195,11 +195,10 @@
 	return ret;
 }
 
-int power_supply_uevent(struct device *dev, char **envp, int num_envp,
-			char *buffer, int buffer_size)
+int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct power_supply *psy = dev_get_drvdata(dev);
-	int i = 0, length = 0, ret = 0, j;
+	int ret = 0, j;
 	char *prop_buf;
 	char *attrname;
 
@@ -212,8 +211,7 @@
 
 	dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);
 
-	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-			     &length, "POWER_SUPPLY_NAME=%s", psy->name);
+	ret = add_uevent_var(env, "POWER_SUPPLY_NAME=%s", psy->name);
 	if (ret)
 		return ret;
 
@@ -243,9 +241,7 @@
 
 		dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf);
 
-		ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-				     &length, "POWER_SUPPLY_%s=%s",
-				     attrname, prop_buf);
+		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
 		kfree(attrname);
 		if (ret)
 			goto out;
@@ -282,14 +278,11 @@
 
 		dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
 
-		ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
-				     &length, "POWER_SUPPLY_%s=%s",
-				     attrname, prop_buf);
+		ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
 		kfree(attrname);
 		if (ret)
 			goto out;
 	}
-	envp[i] = NULL;
 
 out:
 	free_page((unsigned long)prop_buf);
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index b0a18f5..5d967c4 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -44,8 +44,7 @@
 	return 0;
 }
 static int
-ccwgroup_uevent (struct device *dev, char **envp, int num_envp, char *buffer,
-		  int buffer_size)
+ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env)
 {
 	/* TODO */
 	return 0;
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e44d92e..606bb53 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -78,49 +78,38 @@
 
 /* Set up environment variables for ccw device uevent. Return 0 on success,
  * non-zero otherwise. */
-static int ccw_uevent(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size)
+static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct ccw_device *cdev = to_ccwdev(dev);
 	struct ccw_device_id *id = &(cdev->id);
-	int i = 0;
-	int len = 0;
 	int ret;
 	char modalias_buf[30];
 
 	/* CU_TYPE= */
-	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			     "CU_TYPE=%04X", id->cu_type);
+	ret = add_uevent_var(env, "CU_TYPE=%04X", id->cu_type);
 	if (ret)
 		return ret;
 
 	/* CU_MODEL= */
-	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			     "CU_MODEL=%02X", id->cu_model);
+	ret = add_uevent_var(env, "CU_MODEL=%02X", id->cu_model);
 	if (ret)
 		return ret;
 
 	/* The next two can be zero, that's ok for us */
 	/* DEV_TYPE= */
-	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			     "DEV_TYPE=%04X", id->dev_type);
+	ret = add_uevent_var(env, "DEV_TYPE=%04X", id->dev_type);
 	if (ret)
 		return ret;
 
 	/* DEV_MODEL= */
-	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			     "DEV_MODEL=%02X", id->dev_model);
+	ret = add_uevent_var(env, "DEV_MODEL=%02X", id->dev_model);
 	if (ret)
 		return ret;
 
 	/* MODALIAS=  */
 	snprint_alias(modalias_buf, sizeof(modalias_buf), id, "");
-	ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
-			     "MODALIAS=%s", modalias_buf);
-	if (ret)
-		return ret;
-	envp[i] = NULL;
-	return 0;
+	ret = add_uevent_var(env, "MODALIAS=%s", modalias_buf);
+	return ret;
 }
 
 struct bus_type ccw_bus_type;
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 90bd220..e997130 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -458,28 +458,22 @@
  * uevent function for AP devices. It sets up a single environment
  * variable DEV_TYPE which contains the hardware device type.
  */
-static int ap_uevent (struct device *dev, char **envp, int num_envp,
-		       char *buffer, int buffer_size)
+static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
 {
 	struct ap_device *ap_dev = to_ap_dev(dev);
-	int retval = 0, length = 0, i = 0;
+	int retval = 0;
 
 	if (!ap_dev)
 		return -ENODEV;
 
 	/* Set up DEV_TYPE environment variable. */
-	retval = add_uevent_var(envp, num_envp, &i,
-				buffer, buffer_size, &length,
-				"DEV_TYPE=%04X", ap_dev->device_type);
+	retval = add_uevent_var(env, "DEV_TYPE=%04X", ap_dev->device_type);
 	if (retval)
 		return retval;
 
 	/* Add MODALIAS= */
-	retval = add_uevent_var(envp, num_envp, &i,
-				buffer, buffer_size, &length,
-				"MODALIAS=ap:t%02X", ap_dev->device_type);
+	retval = add_uevent_var(env, "MODALIAS=ap:t%02X", ap_dev->device_type);
 
-	envp[i] = NULL;
 	return retval;
 }
 
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 34cdce6..ede9986 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -277,16 +277,11 @@
 	return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
 }
 
-static int scsi_bus_uevent(struct device *dev, char **envp, int num_envp,
-		           char *buffer, int buffer_size)
+static int scsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct scsi_device *sdev = to_scsi_device(dev);
-	int i = 0;
-	int length = 0;
 
-	add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
-		       "MODALIAS=" SCSI_DEVICE_MODALIAS_FMT, sdev->type);
-	envp[i] = NULL;
+	add_uevent_var(env, "MODALIAS=" SCSI_DEVICE_MODALIAS_FMT, sdev->type);
 	return 0;
 }
 
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index e84d215..bcb8dd5 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -67,14 +67,11 @@
 	return strncmp(spi->modalias, drv->name, BUS_ID_SIZE) == 0;
 }
 
-static int spi_uevent(struct device *dev, char **envp, int num_envp,
-		char *buffer, int buffer_size)
+static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	const struct spi_device		*spi = to_spi_device(dev);
 
-	envp[0] = buffer;
-	snprintf(buffer, buffer_size, "MODALIAS=%s", spi->modalias);
-	envp[1] = NULL;
+	add_uevent_var(env, "MODALIAS=%s", spi->modalias);
 	return 0;
 }
 
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 63b1243..6273a51 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -576,12 +576,9 @@
 }
 
 #ifdef	CONFIG_HOTPLUG
-static int usb_uevent(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size)
+static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct usb_device *usb_dev;
-	int i = 0;
-	int length = 0;
 
 	if (!dev)
 		return -ENODEV;
@@ -610,51 +607,39 @@
 	 * all the device descriptors we don't tell them about.  Or
 	 * act as usermode drivers.
 	 */
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "DEVICE=/proc/bus/usb/%03d/%03d",
+	if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d",
 			   usb_dev->bus->busnum, usb_dev->devnum))
 		return -ENOMEM;
 #endif
 
 	/* per-device configurations are common */
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "PRODUCT=%x/%x/%x",
+	if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
 			   le16_to_cpu(usb_dev->descriptor.idVendor),
 			   le16_to_cpu(usb_dev->descriptor.idProduct),
 			   le16_to_cpu(usb_dev->descriptor.bcdDevice)))
 		return -ENOMEM;
 
 	/* class-based driver binding models */
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "TYPE=%d/%d/%d",
+	if (add_uevent_var(env, "TYPE=%d/%d/%d",
 			   usb_dev->descriptor.bDeviceClass,
 			   usb_dev->descriptor.bDeviceSubClass,
 			   usb_dev->descriptor.bDeviceProtocol))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "BUSNUM=%03d",
+	if (add_uevent_var(env, "BUSNUM=%03d",
 			   usb_dev->bus->busnum))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "DEVNUM=%03d",
+	if (add_uevent_var(env, "DEVNUM=%03d",
 			   usb_dev->devnum))
 		return -ENOMEM;
 
-	envp[i] = NULL;
 	return 0;
 }
 
 #else
 
-static int usb_uevent(struct device *dev, char **envp,
-		      int num_envp, char *buffer, int buffer_size)
+static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return -ENODEV;
 }
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index d8f7b08..95a49d8 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1339,14 +1339,11 @@
 }
 
 #ifdef	CONFIG_HOTPLUG
-static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
-		 char *buffer, int buffer_size)
+static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct usb_device *usb_dev;
 	struct usb_interface *intf;
 	struct usb_host_interface *alt;
-	int i = 0;
-	int length = 0;
 
 	if (!dev)
 		return -ENODEV;
@@ -1359,39 +1356,30 @@
 	alt = intf->cur_altsetting;
 
 #ifdef CONFIG_USB_DEVICEFS
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "DEVICE=/proc/bus/usb/%03d/%03d",
+	if (add_uevent_var(env, "DEVICE=/proc/bus/usb/%03d/%03d",
 			   usb_dev->bus->busnum, usb_dev->devnum))
 		return -ENOMEM;
 #endif
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "PRODUCT=%x/%x/%x",
+	if (add_uevent_var(env, "PRODUCT=%x/%x/%x",
 			   le16_to_cpu(usb_dev->descriptor.idVendor),
 			   le16_to_cpu(usb_dev->descriptor.idProduct),
 			   le16_to_cpu(usb_dev->descriptor.bcdDevice)))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-			   buffer, buffer_size, &length,
-			   "TYPE=%d/%d/%d",
+	if (add_uevent_var(env, "TYPE=%d/%d/%d",
 			   usb_dev->descriptor.bDeviceClass,
 			   usb_dev->descriptor.bDeviceSubClass,
 			   usb_dev->descriptor.bDeviceProtocol))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-		   buffer, buffer_size, &length,
-		   "INTERFACE=%d/%d/%d",
+	if (add_uevent_var(env, "INTERFACE=%d/%d/%d",
 		   alt->desc.bInterfaceClass,
 		   alt->desc.bInterfaceSubClass,
 		   alt->desc.bInterfaceProtocol))
 		return -ENOMEM;
 
-	if (add_uevent_var(envp, num_envp, &i,
-		   buffer, buffer_size, &length,
+	if (add_uevent_var(env,
 		   "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
 		   le16_to_cpu(usb_dev->descriptor.idVendor),
 		   le16_to_cpu(usb_dev->descriptor.idProduct),
@@ -1404,14 +1392,12 @@
 		   alt->desc.bInterfaceProtocol))
 		return -ENOMEM;
 
-	envp[i] = NULL;
 	return 0;
 }
 
 #else
 
-static int usb_if_uevent(struct device *dev, char **envp,
-			 int num_envp, char *buffer, int buffer_size)
+static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return -ENODEV;
 }
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index a593f90..0702173 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -197,7 +197,7 @@
 	.fops = &w1_default_fops,
 };
 
-static int w1_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
+static int w1_uevent(struct device *dev, struct kobj_uevent_env *env);
 
 static struct bus_type w1_bus_type = {
 	.name = "w1",
@@ -396,13 +396,12 @@
 }
 
 #ifdef CONFIG_HOTPLUG
-static int w1_uevent(struct device *dev, char **envp, int num_envp,
-			char *buffer, int buffer_size)
+static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct w1_master *md = NULL;
 	struct w1_slave *sl = NULL;
 	char *event_owner, *name;
-	int err, cur_index=0, cur_len=0;
+	int err;
 
 	if (dev->driver == &w1_master_driver) {
 		md = container_of(dev, struct w1_master, dev);
@@ -423,23 +422,19 @@
 	if (dev->driver != &w1_slave_driver || !sl)
 		return 0;
 
-	err = add_uevent_var(envp, num_envp, &cur_index, buffer, buffer_size,
-			&cur_len, "W1_FID=%02X", sl->reg_num.family);
+	err = add_uevent_var(env, "W1_FID=%02X", sl->reg_num.family);
 	if (err)
 		return err;
 
-	err = add_uevent_var(envp, num_envp, &cur_index, buffer, buffer_size,
-			&cur_len, "W1_SLAVE_ID=%024LX",
-			(unsigned long long)sl->reg_num.id);
-	envp[cur_index] = NULL;
+	err = add_uevent_var(env, "W1_SLAVE_ID=%024LX",
+			     (unsigned long long)sl->reg_num.id);
 	if (err)
 		return err;
 
 	return 0;
 };
 #else
-static int w1_uevent(struct device *dev, char **envp, int num_envp,
-			char *buffer, int buffer_size)
+static int w1_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	return 0;
 }
diff --git a/include/asm-powerpc/of_device.h b/include/asm-powerpc/of_device.h
index ec2a8a2..93262f2 100644
--- a/include/asm-powerpc/of_device.h
+++ b/include/asm-powerpc/of_device.h
@@ -20,7 +20,7 @@
 extern ssize_t of_device_get_modalias(struct of_device *ofdev,
 					char *str, ssize_t len);
 extern int of_device_uevent(struct device *dev,
-	char **envp, int num_envp, char *buffer, int buffer_size);
+			    struct kobj_uevent_env *env);
 
 /* This is just here during the transition */
 #include <linux/of_device.h>
diff --git a/include/linux/device.h b/include/linux/device.h
index 5d97ca6..2e15822 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -66,8 +66,7 @@
 	struct driver_attribute	* drv_attrs;
 
 	int		(*match)(struct device * dev, struct device_driver * drv);
-	int		(*uevent)(struct device *dev, char **envp,
-				  int num_envp, char *buffer, int buffer_size);
+	int		(*uevent)(struct device *dev, struct kobj_uevent_env *env);
 	int		(*probe)(struct device * dev);
 	int		(*remove)(struct device * dev);
 	void		(*shutdown)(struct device * dev);
@@ -187,10 +186,8 @@
 	struct class_device_attribute	* class_dev_attrs;
 	struct device_attribute		* dev_attrs;
 
-	int	(*uevent)(struct class_device *dev, char **envp,
-			   int num_envp, char *buffer, int buffer_size);
-	int	(*dev_uevent)(struct device *dev, char **envp, int num_envp,
-				char *buffer, int buffer_size);
+	int	(*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
+	int	(*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
 
 	void	(*release)(struct class_device *dev);
 	void	(*class_release)(struct class *class);
@@ -266,8 +263,7 @@
 	struct attribute_group  ** groups;	/* optional groups */
 
 	void	(*release)(struct class_device *dev);
-	int	(*uevent)(struct class_device *dev, char **envp,
-			   int num_envp, char *buffer, int buffer_size);
+	int	(*uevent)(struct class_device *dev, struct kobj_uevent_env *env);
 	char	class_id[BUS_ID_SIZE];	/* unique to this class */
 };
 
@@ -335,8 +331,7 @@
 struct device_type {
 	const char *name;
 	struct attribute_group **groups;
-	int (*uevent)(struct device *dev, char **envp, int num_envp,
-		      char *buffer, int buffer_size);
+	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
 	void (*release)(struct device *dev);
 	int (*suspend)(struct device * dev, pm_message_t state);
 	int (*resume)(struct device * dev);
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 949706c..626bdd3 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -29,6 +29,8 @@
 
 #define KOBJ_NAME_LEN			20
 #define UEVENT_HELPER_PATH_LEN		256
+#define UEVENT_NUM_ENVP			32	/* number of env pointers */
+#define UEVENT_BUFFER_SIZE		2048	/* buffer for the variables */
 
 /* path to the userspace helper executed on an event */
 extern char uevent_helper[];
@@ -111,11 +113,18 @@
 	struct attribute	** default_attrs;
 };
 
+struct kobj_uevent_env {
+	char *envp[UEVENT_NUM_ENVP];
+	int envp_idx;
+	char buf[UEVENT_BUFFER_SIZE];
+	int buflen;
+};
+
 struct kset_uevent_ops {
 	int (*filter)(struct kset *kset, struct kobject *kobj);
 	const char *(*name)(struct kset *kset, struct kobject *kobj);
-	int (*uevent)(struct kset *kset, struct kobject *kobj, char **envp,
-			int num_envp, char *buffer, int buffer_size);
+	int (*uevent)(struct kset *kset, struct kobject *kobj,
+		      struct kobj_uevent_env *env);
 };
 
 /*
@@ -275,10 +284,8 @@
 int 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,
-			const char *format, ...)
-	__attribute__((format (printf, 7, 8)));
+int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
+	__attribute__((format (printf, 2, 3)));
 #else
 static inline int kobject_uevent(struct kobject *kobj, enum kobject_action action)
 { return 0; }
@@ -287,9 +294,7 @@
 				      char *envp[])
 { return 0; }
 
-static inline int add_uevent_var(char **envp, int num_envp, int *cur_index,
-				      char *buffer, int buffer_size, int *cur_len, 
-				      const char *format, ...)
+static inline int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 { return 0; }
 #endif
 
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
index e06a8dc..7d8aeb3 100644
--- a/lib/kobject_uevent.c
+++ b/lib/kobject_uevent.c
@@ -22,8 +22,6 @@
 #include <linux/kobject.h>
 #include <net/sock.h>
 
-#define BUFFER_SIZE	2048	/* buffer for the variables */
-#define NUM_ENVP	32	/* number of env pointers */
 
 /* the strings here must match the enum in include/linux/kobject.h */
 const char *kobject_actions[] = {
@@ -54,31 +52,21 @@
  * corresponding error when it fails.
  */
 int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
-			char *envp_ext[])
+		       char *envp_ext[])
 {
-	char **envp;
-	char *buffer;
-	char *scratch;
-	const char *action_string;
+	struct kobj_uevent_env *env;
+	const char *action_string = kobject_actions[action];
 	const char *devpath = NULL;
 	const char *subsystem;
 	struct kobject *top_kobj;
 	struct kset *kset;
 	struct kset_uevent_ops *uevent_ops;
 	u64 seq;
-	char *seq_buff;
 	int i = 0;
 	int retval = 0;
-	int j;
 
 	pr_debug("%s\n", __FUNCTION__);
 
-	action_string = kobject_actions[action];
-	if (!action_string) {
-		pr_debug("kobject attempted to send uevent without action_string!\n");
-		return -EINVAL;
-	}
-
 	/* search the kset we belong to */
 	top_kobj = kobj;
 	while (!top_kobj->kset && top_kobj->parent) {
@@ -92,7 +80,7 @@
 	kset = top_kobj->kset;
 	uevent_ops = kset->uevent_ops;
 
-	/*  skip the event, if the filter returns zero. */
+	/* skip the event, if the filter returns zero. */
 	if (uevent_ops && uevent_ops->filter)
 		if (!uevent_ops->filter(kset, kobj)) {
 			pr_debug("kobject filter function caused the event to drop!\n");
@@ -109,18 +97,11 @@
 		return 0;
 	}
 
-	/* environment index */
-	envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
-	if (!envp)
+	/* environment buffer */
+	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
+	if (!env)
 		return -ENOMEM;
 
-	/* environment values */
-	buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
-	if (!buffer) {
-		retval = -ENOMEM;
-		goto exit;
-	}
-
 	/* complete object path */
 	devpath = kobject_get_path(kobj, GFP_KERNEL);
 	if (!devpath) {
@@ -128,29 +109,29 @@
 		goto exit;
 	}
 
-	/* event environemnt for helper process only */
-	envp[i++] = "HOME=/";
-	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
-
 	/* default keys */
-	scratch = buffer;
-	envp [i++] = scratch;
-	scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;
-	envp [i++] = scratch;
-	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;
+	retval = add_uevent_var(env, "ACTION=%s", action_string);
+	if (retval)
+		goto exit;
+	retval = add_uevent_var(env, "DEVPATH=%s", devpath);
+	if (retval)
+		goto exit;
+	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
+	if (retval)
+		goto exit;
+
+	/* keys passed in from the caller */
+	if (envp_ext) {
+		for (i = 0; envp_ext[i]; i++) {
+			retval = add_uevent_var(env, envp_ext[i]);
+			if (retval)
+				goto exit;
+		}
+	}
 
 	/* let the kset specific function add its stuff */
 	if (uevent_ops && uevent_ops->uevent) {
-		retval = uevent_ops->uevent(kset, kobj,
-				  &envp[i], NUM_ENVP - i, scratch,
-				  BUFFER_SIZE - (scratch - buffer));
+		retval = uevent_ops->uevent(kset, kobj, env);
 		if (retval) {
 			pr_debug ("%s - uevent() returned %d\n",
 				  __FUNCTION__, retval);
@@ -158,11 +139,13 @@
 		}
 	}
 
-	/* we will send an event, request a new sequence number */
+	/* we will send an event, so request a new sequence number */
 	spin_lock(&sequence_lock);
 	seq = ++uevent_seqnum;
 	spin_unlock(&sequence_lock);
-	sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq);
+	retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
+	if (retval)
+		goto exit;
 
 #if defined(CONFIG_NET)
 	/* send netlink message */
@@ -172,17 +155,19 @@
 
 		/* allocate message with the maximum possible size */
 		len = strlen(action_string) + strlen(devpath) + 2;
-		skb = alloc_skb(len + BUFFER_SIZE, GFP_KERNEL);
+		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
 		if (skb) {
+			char *scratch;
+
 			/* add header */
 			scratch = skb_put(skb, len);
 			sprintf(scratch, "%s@%s", action_string, devpath);
 
 			/* copy keys to our continuous event payload buffer */
-			for (i = 2; envp[i]; i++) {
-				len = strlen(envp[i]) + 1;
+			for (i = 0; i < env->envp_idx; i++) {
+				len = strlen(env->envp[i]) + 1;
 				scratch = skb_put(skb, len);
-				strcpy(scratch, envp[i]);
+				strcpy(scratch, env->envp[i]);
 			}
 
 			NETLINK_CB(skb).dst_group = 1;
@@ -198,13 +183,19 @@
 		argv [0] = uevent_helper;
 		argv [1] = (char *)subsystem;
 		argv [2] = NULL;
-		call_usermodehelper (argv[0], argv, envp, UMH_WAIT_EXEC);
+		retval = add_uevent_var(env, "HOME=/");
+		if (retval)
+			goto exit;
+		retval = add_uevent_var(env, "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
+		if (retval)
+			goto exit;
+
+		call_usermodehelper (argv[0], argv, env->envp, UMH_WAIT_EXEC);
 	}
 
 exit:
 	kfree(devpath);
-	kfree(buffer);
-	kfree(envp);
+	kfree(env);
 	return retval;
 }
 
@@ -227,52 +218,38 @@
 EXPORT_SYMBOL_GPL(kobject_uevent);
 
 /**
- * add_uevent_var - helper for creating event variables
- * @envp: Pointer to table of environment variables, as passed into
- * uevent() method.
- * @num_envp: Number of environment variable slots available, as
- * passed into uevent() method.
- * @cur_index: Pointer to current index into @envp.  It should be
- * initialized to 0 before the first call to add_uevent_var(),
- * and will be incremented on success.
- * @buffer: Pointer to buffer for environment variables, as passed
- * into uevent() method.
- * @buffer_size: Length of @buffer, as passed into uevent() method.
- * @cur_len: Pointer to current length of space used in @buffer.
- * Should be initialized to 0 before the first call to
- * add_uevent_var(), and will be incremented on success.
- * @format: Format for creating environment variable (of the form
- * "XXX=%x") for snprintf().
+ * add_uevent_var - add key value string to the environment buffer
+ * @env: environment buffer structure
+ * @format: printf format for the key=value pair
  *
  * Returns 0 if environment variable was added successfully or -ENOMEM
  * if no space was available.
  */
-int add_uevent_var(char **envp, int num_envp, int *cur_index,
-		   char *buffer, int buffer_size, int *cur_len,
-		   const char *format, ...)
+int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...)
 {
 	va_list args;
+	int len;
 
-	/*
-	 * We check against num_envp - 1 to make sure there is at
-	 * least one slot left after we return, since kobject_uevent()
-	 * needs to set the last slot to NULL.
-	 */
-	if (*cur_index >= num_envp - 1)
+	if (env->envp_idx >= ARRAY_SIZE(env->envp)) {
+		printk(KERN_ERR "add_uevent_var: too many keys\n");
+		WARN_ON(1);
 		return -ENOMEM;
-
-	envp[*cur_index] = buffer + *cur_len;
+	}
 
 	va_start(args, format);
-	*cur_len += vsnprintf(envp[*cur_index],
-			      max(buffer_size - *cur_len, 0),
-			      format, args) + 1;
+	len = vsnprintf(&env->buf[env->buflen],
+			sizeof(env->buf) - env->buflen,
+			format, args);
 	va_end(args);
 
-	if (*cur_len > buffer_size)
+	if (len >= (sizeof(env->buf) - env->buflen)) {
+		printk(KERN_ERR "add_uevent_var: buffer size too small\n");
+		WARN_ON(1);
 		return -ENOMEM;
+	}
 
-	(*cur_index)++;
+	env->envp[env->envp_idx++] = &env->buf[env->buflen];
+	env->buflen += len + 1;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(add_uevent_var);
diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c
index f094a08..9ef07ed 100644
--- a/net/atm/atm_sysfs.c
+++ b/net/atm/atm_sysfs.c
@@ -105,10 +105,9 @@
 	NULL
 };
 
-static int atm_uevent(struct class_device *cdev, char **envp, int num_envp, char *buf, int size)
+static int atm_uevent(struct class_device *cdev, struct kobj_uevent_env *env)
 {
 	struct atm_dev *adev;
-	int i = 0, len = 0;
 
 	if (!cdev)
 		return -ENODEV;
@@ -117,11 +116,9 @@
 	if (!adev)
 		return -ENODEV;
 
-	if (add_uevent_var(envp, num_envp, &i, buf, size, &len,
-			   "NAME=%s%d", adev->type, adev->number))
+	if (add_uevent_var(env, "NAME=%s%d", adev->type, adev->number))
 		return -ENOMEM;
 
-	envp[i] = NULL;
 	return 0;
 }
 
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 909a03d..6628e45 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -396,28 +396,22 @@
 #endif /* CONFIG_SYSFS */
 
 #ifdef CONFIG_HOTPLUG
-static int netdev_uevent(struct device *d, char **envp,
-			 int num_envp, char *buf, int size)
+static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
 {
 	struct net_device *dev = to_net_dev(d);
-	int retval, len = 0, i = 0;
+	int retval;
 
 	/* pass interface to uevent. */
-	retval = add_uevent_var(envp, num_envp, &i,
-				buf, size, &len,
-				"INTERFACE=%s", dev->name);
+	retval = add_uevent_var(env, "INTERFACE=%s", dev->name);
 	if (retval)
 		goto exit;
 
 	/* pass ifindex to uevent.
 	 * ifindex is useful as it won't change (interface name may change)
 	 * and is what RtNetlink uses natively. */
-	retval = add_uevent_var(envp, num_envp, &i,
-				buf, size, &len,
-				"IFINDEX=%d", dev->ifindex);
+	retval = add_uevent_var(env, "IFINDEX=%d", dev->ifindex);
 
 exit:
-	envp[i] = NULL;
 	return retval;
 }
 #endif
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index 2d5d2255..29f820e 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -53,8 +53,7 @@
 }
 
 #ifdef CONFIG_HOTPLUG
-static int wiphy_uevent(struct device *dev, char **envp,
-			int num_envp, char *buf, int size)
+static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	/* TODO, we probably need stuff here */
 	return 0;
diff --git a/sound/aoa/soundbus/core.c b/sound/aoa/soundbus/core.c
index 64d1639..f84f3e5 100644
--- a/sound/aoa/soundbus/core.c
+++ b/sound/aoa/soundbus/core.c
@@ -56,13 +56,12 @@
 }
 
 
-static int soundbus_uevent(struct device *dev, char **envp, int num_envp,
-			   char *buffer, int buffer_size)
+static int soundbus_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	struct soundbus_dev * soundbus_dev;
 	struct of_device * of;
 	const char *compat;
-	int retval = 0, i = 0, length = 0;
+	int retval = 0;
 	int cplen, seen = 0;
 
 	if (!dev)
@@ -75,15 +74,11 @@
 	of = &soundbus_dev->ofdev;
 
 	/* stuff we want to pass to /sbin/hotplug */
-	retval = add_uevent_var(envp, num_envp, &i,
-				buffer, buffer_size, &length,
-				"OF_NAME=%s", of->node->name);
+	retval = add_uevent_var(env, "OF_NAME=%s", of->node->name);
 	if (retval)
 		return retval;
 
-	retval = add_uevent_var(envp, num_envp, &i,
-				buffer, buffer_size, &length,
-				"OF_TYPE=%s", of->node->type);
+	retval = add_uevent_var(env, "OF_TYPE=%s", of->node->type);
 	if (retval)
 		return retval;
 
@@ -93,27 +88,19 @@
 
 	compat = of_get_property(of->node, "compatible", &cplen);
 	while (compat && cplen > 0) {
-		int tmp = length;
-		retval = add_uevent_var(envp, num_envp, &i,
-					buffer, buffer_size, &length,
-					"OF_COMPATIBLE_%d=%s", seen, compat);
+		int tmp = env->buflen;
+		retval = add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat);
 		if (retval)
 			return retval;
-		compat += length - tmp;
-		cplen -= length - tmp;
+		compat += env->buflen - tmp;
+		cplen -= env->buflen - tmp;
 		seen += 1;
 	}
 
-	retval = add_uevent_var(envp, num_envp, &i,
-				buffer, buffer_size, &length,
-				"OF_COMPATIBLE_N=%d", seen);
+	retval = add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen);
 	if (retval)
 		return retval;
-	retval = add_uevent_var(envp, num_envp, &i,
-				buffer, buffer_size, &length,
-				"MODALIAS=%s", soundbus_dev->modalias);
-
-	envp[i] = NULL;
+	retval = add_uevent_var(env, "MODALIAS=%s", soundbus_dev->modalias);
 
 	return retval;
 }