Here's a patch against 2.5.66 that adds /sbin/hotplug support for
kobjects that are registered with a subsystem.  This allows partitions
in /sys/block to get hotplug calls.  The /sys/device/ hotplug calls were
also moved to use this logic, but the /sys/class/ hotplug call can not,
as it does not create new kobjects.
This work is based on work done by Kevin Fleming, who originally got the
hotplug support working for /sys/block.
Any comments or objections to me sending this on to Linus?
thanks,
greg k-h
diff -Nru a/arch/i386/kernel/edd.c b/arch/i386/kernel/edd.c
--- a/arch/i386/kernel/edd.c	Fri Apr  4 11:52:35 2003
+++ b/arch/i386/kernel/edd.c	Fri Apr  4 11:52:35 2003
@@ -598,7 +598,7 @@
 	.default_attrs	= def_attrs,
 };
 
-static decl_subsys(edd,&ktype_edd);
+static decl_subsys(edd,&ktype_edd,NULL);
 
 
 /**
diff -Nru a/drivers/acpi/bus.c b/drivers/acpi/bus.c
--- a/drivers/acpi/bus.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/acpi/bus.c	Fri Apr  4 11:52:35 2003
@@ -676,7 +676,7 @@
 	return_VALUE(-ENODEV);
 }
 
-decl_subsys(acpi,NULL);
+decl_subsys(acpi,NULL,NULL);
 
 static int __init acpi_init (void)
 {
diff -Nru a/drivers/base/base.h b/drivers/base/base.h
--- a/drivers/base/base.h	Fri Apr  4 11:52:35 2003
+++ b/drivers/base/base.h	Fri Apr  4 11:52:35 2003
@@ -20,13 +20,8 @@
 
 
 #ifdef CONFIG_HOTPLUG
-extern int dev_hotplug(struct device *dev, const char *action);
 extern int class_hotplug(struct device *dev, const char *action);
 #else
-static inline int dev_hotplug(struct device *dev, const char *action)
-{
-	return 0;
-}
 static inline int class_hotplug(struct device *dev, const char *action)
 {
 	return 0;
diff -Nru a/drivers/base/bus.c b/drivers/base/bus.c
--- a/drivers/base/bus.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/base/bus.c	Fri Apr  4 11:52:35 2003
@@ -132,7 +132,7 @@
 
 };
 
-decl_subsys(bus,&ktype_bus);
+decl_subsys(bus,&ktype_bus,NULL);
 
 /**
  *	bus_for_each_dev - device iterator.
diff -Nru a/drivers/base/class.c b/drivers/base/class.c
--- a/drivers/base/class.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/base/class.c	Fri Apr  4 11:52:35 2003
@@ -49,7 +49,9 @@
 	.sysfs_ops	= &class_sysfs_ops,
 };
 
-static decl_subsys(class,&ktype_devclass);
+/* Classes can't use the kobject hotplug logic, as
+ * they do not add new kobjects to the system */
+static decl_subsys(class,&ktype_devclass,NULL);
 
 
 static int devclass_dev_link(struct device_class * cls, struct device * dev)
diff -Nru a/drivers/base/core.c b/drivers/base/core.c
--- a/drivers/base/core.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/base/core.c	Fri Apr  4 11:52:35 2003
@@ -23,13 +23,12 @@
 
 DECLARE_MUTEX(device_sem);
 
-#define to_dev(obj) container_of(obj,struct device,kobj)
-
 
 /*
  * sysfs bindings for devices.
  */
 
+#define to_dev(obj) container_of(obj,struct device,kobj)
 #define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr)
 
 extern struct attribute * dev_default_attrs[];
@@ -86,11 +85,55 @@
 	.default_attrs	= dev_default_attrs,
 };
 
+
+static int dev_hotplug_filter(struct kset *kset, struct kobject *kobj)
+{
+	struct kobj_type *ktype = get_ktype(kobj);
+
+	if (ktype == &ktype_device) {
+		struct device *dev = to_dev(kobj);
+		if (dev->bus)
+			return 1;
+	}
+	return 0;
+}
+
+static char *dev_hotplug_name(struct kset *kset, struct kobject *kobj)
+{
+	struct device *dev = to_dev(kobj);
+
+	return dev->bus->name;
+}
+
+static int dev_hotplug(struct kset *kset, struct kobject *kobj, char **envp,
+			int num_envp, char *buffer, int buffer_size)
+{
+	struct device *dev = to_dev(kobj);
+	int retval = 0;
+
+	if (dev->bus->hotplug) {
+		/* have the bus specific function add its stuff */
+		retval = dev->bus->hotplug (dev, envp, num_envp, buffer, buffer_size);
+			if (retval) {
+			pr_debug ("%s - hotplug() returned %d\n",
+				  __FUNCTION__, retval);
+		}
+	}
+
+	return retval;
+}
+
+static struct kset_hotplug_ops device_hotplug_ops = {
+	.filter =	dev_hotplug_filter,
+	.name =		dev_hotplug_name,
+	.hotplug =	dev_hotplug,
+};
+
 /**
  *	device_subsys - structure to be registered with kobject core.
  */
 
-decl_subsys(devices,&ktype_device);
+decl_subsys(devices, &ktype_device, &device_hotplug_ops);
 
 
 /**
@@ -192,9 +235,6 @@
 	if (platform_notify)
 		platform_notify(dev);
 
-	/* notify userspace of device entry */
-	dev_hotplug(dev, "add");
-
 	devclass_add_device(dev);
  register_done:
 	if (error && parent)
@@ -277,9 +317,6 @@
 	 */
 	if (platform_notify_remove)
 		platform_notify_remove(dev);
-
-	/* notify userspace that this device is about to disappear */
-	dev_hotplug (dev, "remove");
 
 	bus_remove_device(dev);
 
diff -Nru a/drivers/base/firmware.c b/drivers/base/firmware.c
--- a/drivers/base/firmware.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/base/firmware.c	Fri Apr  4 11:52:35 2003
@@ -6,7 +6,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 
-static decl_subsys(firmware,NULL);
+static decl_subsys(firmware,NULL,NULL);
 
 int firmware_register(struct subsystem * s)
 {
diff -Nru a/drivers/base/hotplug.c b/drivers/base/hotplug.c
--- a/drivers/base/hotplug.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/base/hotplug.c	Fri Apr  4 11:52:35 2003
@@ -2,8 +2,8 @@
  * drivers/base/hotplug.c - hotplug call code
  * 
  * Copyright (c) 2000-2001 David Brownell
- * Copyright (c) 2002 Greg Kroah-Hartman
- * Copyright (c) 2002 IBM Corp.
+ * Copyright (c) 2002-2003 Greg Kroah-Hartman
+ * Copyright (c) 2002-2003 IBM Corp.
  *
  * Based off of drivers/usb/core/usb.c:call_agent(), which was 
  * written by David Brownell.
@@ -53,17 +53,6 @@
 	if (!hotplug_path [0])
 		return -ENODEV;
 
-	if (in_interrupt ()) {
-		pr_debug ("%s - in_interrupt, not allowed!", __FUNCTION__);
-		return -EIO;
-	}
-
-	if (!current->fs->root) {
-		/* don't try to do anything unless we have a root partition */
-		pr_debug ("%s - %s -- no FS yet\n", __FUNCTION__, action);
-		return -EIO;
-	}
-
 	envp = (char **) kmalloc (NUM_ENVP * sizeof (char *), GFP_KERNEL);
 	if (!envp)
 		return -ENOMEM;
@@ -127,23 +116,6 @@
 	kfree (envp);
 	return retval;
 }
-
-
-/*
- * dev_hotplug - called when any device is added or removed from a bus
- */
-int dev_hotplug (struct device *dev, const char *action)
-{
-	pr_debug ("%s\n", __FUNCTION__);
-	if (!dev)
-		return -ENODEV;
-
-	if (!dev->bus)
-		return -ENODEV;
-
-	return do_hotplug (dev, dev->bus->name, action, dev->bus->hotplug);
-}
-
 
 /*
  * class_hotplug - called when a class is added or removed from a device
diff -Nru a/drivers/block/genhd.c b/drivers/block/genhd.c
--- a/drivers/block/genhd.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/block/genhd.c	Fri Apr  4 11:52:35 2003
@@ -525,9 +525,21 @@
 	.default_attrs	= default_attrs,
 };
 
+extern struct kobj_type ktype_part;
+
+static int block_hotplug_filter(struct kset *kset, struct kobject *kobj)
+{
+	struct kobj_type *ktype = get_ktype(kobj);
+
+	return ((ktype == &ktype_block) || (ktype == &ktype_part));
+}
+
+static struct kset_hotplug_ops block_hotplug_ops = {
+	.filter	= block_hotplug_filter,
+};
 
 /* declare block_subsys. */
-static decl_subsys(block,&ktype_block);
+static decl_subsys(block, &ktype_block, &block_hotplug_ops);
 
 
 struct gendisk *alloc_disk(int minors)
diff -Nru a/drivers/hotplug/pci_hotplug_core.c b/drivers/hotplug/pci_hotplug_core.c
--- a/drivers/hotplug/pci_hotplug_core.c	Fri Apr  4 11:52:35 2003
+++ b/drivers/hotplug/pci_hotplug_core.c	Fri Apr  4 11:52:35 2003
@@ -100,7 +100,7 @@
 	.sysfs_ops = &hotplug_slot_sysfs_ops
 };
 
-static decl_subsys(hotplug_slots, &hotplug_slot_ktype);
+static decl_subsys(hotplug_slots, &hotplug_slot_ktype, NULL);
 
 
 /* these strings match up with the values in pci_bus_speed */
diff -Nru a/fs/filesystems.c b/fs/filesystems.c
--- a/fs/filesystems.c	Fri Apr  4 11:52:35 2003
+++ b/fs/filesystems.c	Fri Apr  4 11:52:35 2003
@@ -61,7 +61,7 @@
 
 
 /* define fs_subsys */
-static decl_subsys(fs, NULL);
+static decl_subsys(fs, NULL, NULL);
 
 static int register_fs_subsys(struct file_system_type * fs)
 {
diff -Nru a/fs/partitions/check.c b/fs/partitions/check.c
--- a/fs/partitions/check.c	Fri Apr  4 11:52:35 2003
+++ b/fs/partitions/check.c	Fri Apr  4 11:52:35 2003
@@ -248,7 +248,7 @@
 
 extern struct subsystem block_subsys;
 
-static struct kobj_type ktype_part = {
+struct kobj_type ktype_part = {
 	.default_attrs	= default_attrs,
 	.sysfs_ops	= &part_sysfs_ops,
 };
diff -Nru a/include/linux/kobject.h b/include/linux/kobject.h
--- a/include/linux/kobject.h	Fri Apr  4 11:52:35 2003
+++ b/include/linux/kobject.h	Fri Apr  4 11:52:35 2003
@@ -57,12 +57,24 @@
  *	of object; multiple ksets can belong to one subsystem. All 
  *	ksets of a subsystem share the subsystem's lock.
  *
+ *      Each kset can support hotplugging; if it does, it will be given
+ *      the opportunity to filter out specific kobjects from being
+ *      reported, as well as to add its own "data" elements to the
+ *      environment being passed to the hotplug helper.
  */
+struct kset_hotplug_ops {
+	int (*filter)(struct kset *kset, struct kobject *kobj);
+	char *(*name)(struct kset *kset, struct kobject *kobj);
+	int (*hotplug)(struct kset *kset, struct kobject *kobj, char **envp,
+			int num_envp, char *buffer, int buffer_size);
+};
+
 struct kset {
 	struct subsystem	* subsys;
 	struct kobj_type	* ktype;
 	struct list_head	list;
 	struct kobject		kobj;
+	struct kset_hotplug_ops	* hotplug_ops;
 };
 
 
@@ -86,6 +98,13 @@
 	kobject_put(&k->kobj);
 }
 
+static inline struct kobj_type * get_ktype(struct kobject * k)
+{
+	if (k->kset && k->kset->ktype)
+		return k->kset->ktype;
+	else 
+		return k->ktype;
+}
 
 extern struct kobject * kset_find_obj(struct kset *, const char *);
 
@@ -95,11 +114,12 @@
 	struct rw_semaphore	rwsem;
 };
 
-#define decl_subsys(_name,_type) \
+#define decl_subsys(_name,_type,_hotplug_ops) \
 struct subsystem _name##_subsys = { \
 	.kset = { \
 		.kobj = { .name = __stringify(_name) }, \
 		.ktype = _type, \
+		.hotplug_ops =_hotplug_ops, \
 	} \
 }
 
diff -Nru a/lib/kobject.c b/lib/kobject.c
--- a/lib/kobject.c	Fri Apr  4 11:52:35 2003
+++ b/lib/kobject.c	Fri Apr  4 11:52:35 2003
@@ -11,14 +11,6 @@
 
 static spinlock_t kobj_lock = SPIN_LOCK_UNLOCKED;
 
-static inline struct kobj_type * get_ktype(struct kobject * k)
-{
-	if (k->kset && k->kset->ktype)
-		return k->kset->ktype;
-	else 
-		return k->ktype;
-}
-
 /**
  *	populate_dir - populate directory with attributes.
  *	@kobj:	object we're working on.
@@ -67,6 +59,140 @@
 }
 
 
+#ifdef CONFIG_HOTPLUG
+static int get_kobj_path_length(struct kset *kset, struct kobject *kobj)
+{
+	int length = 1;
+	struct kobject * parent = kobj;
+
+	/* walk up the ancestors until we hit the one pointing to the 
+	 * root.
+	 * Add 1 to strlen for leading '/' of each level.
+	 */
+	do {
+		length += strlen (parent->name) + 1;
+		parent = parent->parent;
+	} while (parent);
+	return length;
+}
+
+static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, int length)
+{
+	struct kobject * parent;
+
+	--length;
+	for (parent = kobj; parent; parent = parent->parent) {
+		int cur = strlen (parent->name);
+		/* back up enough to print this name with '/' */
+		length -= cur;
+		strncpy (path + length, parent->name, cur);
+		*(path + --length) = '/';
+	}
+
+	pr_debug("%s: path = '%s'\n",__FUNCTION__,path);
+}
+
+#define BUFFER_SIZE	1024	/* should be enough memory for the env */
+#define NUM_ENVP	32	/* number of env pointers */
+static void kset_hotplug(const char *action, struct kset *kset,
+			 struct kobject *kobj)
+{
+	char *argv [3];
+	char **envp;
+	char *buffer;
+	char *scratch;
+	int i = 0;
+	int retval;
+	int kobj_path_length;
+	char *kobj_path;
+	char *name = NULL;
+
+	/* If the kset has a filter operation, call it. If it returns
+	   failure, no hotplug event is required. */
+	if (kset->hotplug_ops->filter) {
+		if (!kset->hotplug_ops->filter(kset, kobj))
+			return;
+	}
+
+	pr_debug ("%s\n", __FUNCTION__);
+
+	if (!hotplug_path[0])
+		return;
+
+	envp = (char **)kmalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);
+	if (!envp)
+		return;
+	memset (envp, 0x00, NUM_ENVP * sizeof (char *));
+
+	buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
+	if (!buffer) {
+		kfree(envp);
+		return;
+	}
+
+	if (kset->hotplug_ops->name)
+		name = kset->hotplug_ops->name(kset, kobj);
+	if (name == NULL)
+		name = kset->kobj.name;
+
+	argv [0] = hotplug_path;
+	argv [1] = name;
+	argv [2] = 0;
+
+	/* minimal command environment */
+	envp [i++] = "HOME=/";
+	envp [i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+	scratch = buffer;
+
+	envp [i++] = scratch;
+	scratch += sprintf(scratch, "ACTION=%s", action) + 1;
+
+	kobj_path_length = get_kobj_path_length (kset, kobj);
+	kobj_path = kmalloc (kobj_path_length, GFP_KERNEL);
+	if (!kobj_path) {
+		kfree (buffer);
+		kfree (envp);
+		return;
+	}
+	memset (kobj_path, 0x00, kobj_path_length);
+	fill_kobj_path (kset, kobj, kobj_path, kobj_path_length);
+
+	envp [i++] = scratch;
+	scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
+
+	if (kset->hotplug_ops->hotplug) {
+		/* have the kset specific function add its stuff */
+		retval = kset->hotplug_ops->hotplug (kset, kobj,
+				  &envp[i], NUM_ENVP - i, scratch,
+				  BUFFER_SIZE - (scratch - buffer));
+		if (retval) {
+			pr_debug ("%s - hotplug() returned %d\n",
+				  __FUNCTION__, retval);
+			goto exit;
+		}
+	}
+
+	pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
+		  envp[0], envp[1], envp[2], envp[3]);
+	retval = call_usermodehelper (argv[0], argv, envp, 0);
+	if (retval)
+		pr_debug ("%s - call_usermodehelper returned %d\n",
+			  __FUNCTION__, retval);
+
+exit:
+	kfree (kobj_path);
+	kfree (buffer);
+	return;
+}
+#else
+static void kset_hotplug(const char *action, struct kset *kset,
+			 struct kobject *kobj)
+{
+	return 0;
+}
+#endif	/* CONFIG_HOTPLUG */
+
 /**
  *	kobject_init - initialize object.
  *	@kobj:	object in question.
@@ -111,6 +237,7 @@
 {
 	int error = 0;
 	struct kobject * parent;
+	struct kobject * top_kobj;
 
 	if (!(kobj = kobject_get(kobj)))
 		return -ENOENT;
@@ -134,6 +261,19 @@
 	error = create_dir(kobj);
 	if (error)
 		unlink(kobj);
+	else {
+		/* If this kobj does not belong to a kset,
+		   try to find a parent that does. */
+		top_kobj = kobj;
+		if (!top_kobj->kset && top_kobj->parent) {
+			do {
+				top_kobj = top_kobj->parent;
+			} while (!top_kobj->kset && top_kobj->parent);
+		}
+	
+		if (top_kobj->kset && top_kobj->kset->hotplug_ops)
+			kset_hotplug("add", top_kobj->kset, kobj);
+	}
 	return error;
 }
 
@@ -162,6 +302,20 @@
 
 void kobject_del(struct kobject * kobj)
 {
+	struct kobject * top_kobj;
+
+	/* If this kobj does not belong to a kset,
+	   try to find a parent that does. */
+	top_kobj = kobj;
+	if (!top_kobj->kset && top_kobj->parent) {
+		do {
+			top_kobj = top_kobj->parent;
+		} while (!top_kobj->kset && top_kobj->parent);
+	}
+
+	if (top_kobj->kset && top_kobj->kset->hotplug_ops)
+		kset_hotplug("remove", top_kobj->kset, kobj);
+
 	sysfs_remove_dir(kobj);
 	unlink(kobj);
 }
diff -Nru a/net/core/dev.c b/net/core/dev.c
--- a/net/core/dev.c	Fri Apr  4 11:52:35 2003
+++ b/net/core/dev.c	Fri Apr  4 11:52:35 2003
@@ -2811,7 +2811,7 @@
 extern void dv_init(void);
 #endif /* CONFIG_NET_DIVERT */
 
-static decl_subsys(net,NULL);
+static decl_subsys(net,NULL,NULL);
 
 
 /*
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/