summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <greg@kroah.com>2004-09-24 10:07:32 -0700
committerGreg Kroah-Hartman <greg@kroah.com>2004-09-24 10:07:32 -0700
commitf413349679a606cd3f0d0e6b87c4a518173ef8af (patch)
tree6062da2db1a2e3d716c39c0abc7fa2d16a2d861e
parent2d952a582ef77577dc903a756ab365520bdbd1f7 (diff)
parenta1e66cc0011a9f9fae58a5609e329357d747635d (diff)
Merge bk://kernel.bkbits.net//home/mochel/linux-2.6-core
into kroah.com:/home/greg/linux/BK/driver-2.6
-rw-r--r--drivers/base/bus.c2
-rw-r--r--drivers/base/firmware_class.c2
-rw-r--r--drivers/pci/pci-driver.c1
-rw-r--r--drivers/usb/core/usb.c2
-rw-r--r--drivers/usb/serial/bus.c1
-rw-r--r--fs/super.c17
-rw-r--r--include/linux/device.h2
-rw-r--r--include/linux/kobject.h14
-rw-r--r--include/linux/kobject_uevent.h51
-rw-r--r--include/linux/module.h14
-rw-r--r--include/linux/netlink.h1
-rw-r--r--include/linux/pci.h2
-rw-r--r--init/Kconfig19
-rw-r--r--kernel/Makefile3
-rw-r--r--kernel/ksysfs.c56
-rw-r--r--kernel/module.c21
-rw-r--r--lib/Makefile2
-rw-r--r--lib/kobject.c137
-rw-r--r--lib/kobject_uevent.c295
19 files changed, 505 insertions, 137 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 825ad5a33227..b5a7d337a87f 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -529,6 +529,7 @@ int bus_add_driver(struct device_driver * drv)
down_write(&bus->subsys.rwsem);
driver_attach(drv);
up_write(&bus->subsys.rwsem);
+ module_add_driver(drv->owner, drv);
driver_add_attrs(bus, drv);
}
@@ -553,6 +554,7 @@ void bus_remove_driver(struct device_driver * drv)
pr_debug("bus %s: remove driver %s\n", drv->bus->name, drv->name);
driver_detach(drv);
up_write(&drv->bus->subsys.rwsem);
+ module_remove_driver(drv);
kobject_unregister(&drv->kobj);
put_bus(drv->bus);
}
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 9317f3f3191c..96e78b60f540 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -420,7 +420,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,
add_timer(&fw_priv->timeout);
}
- kobject_hotplug("add", &class_dev->kobj);
+ kobject_hotplug(&class_dev->kobj, KOBJ_ADD);
wait_for_completion(&fw_priv->completion);
set_bit(FW_STATUS_DONE, &fw_priv->status);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 9f8f56a2d1b5..57fcdb9e55a4 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -404,6 +404,7 @@ pci_register_driver(struct pci_driver *drv)
drv->driver.bus = &pci_bus_type;
drv->driver.probe = pci_device_probe;
drv->driver.remove = pci_device_remove;
+ drv->driver.owner = drv->owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
pci_init_dynids(&drv->dynids);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index de2edfb08582..df461b120612 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -73,6 +73,7 @@ static int generic_remove (struct device *dev)
}
static struct device_driver usb_generic_driver = {
+ .owner = THIS_MODULE,
.name = "usb",
.bus = &usb_bus_type,
.probe = generic_probe,
@@ -150,6 +151,7 @@ int usb_register(struct usb_driver *new_driver)
new_driver->driver.bus = &usb_bus_type;
new_driver->driver.probe = usb_probe_interface;
new_driver->driver.remove = usb_unbind_interface;
+ new_driver->driver.owner = new_driver->owner;
retval = driver_register(&new_driver->driver);
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index 3cabbbb10f00..2f612c2d894b 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -120,6 +120,7 @@ int usb_serial_bus_register(struct usb_serial_device_type *device)
device->driver.bus = &usb_serial_bus_type;
device->driver.probe = usb_serial_device_probe;
device->driver.remove = usb_serial_device_remove;
+ device->driver.owner = device->owner;
retval = driver_register(&device->driver);
diff --git a/fs/super.c b/fs/super.c
index 6f8960dbef68..d00587a742a9 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -35,6 +35,7 @@
#include <linux/vfs.h>
#include <linux/writeback.h> /* for the emergency remount stuff */
#include <linux/idr.h>
+#include <linux/kobject.h>
#include <asm/uaccess.h>
@@ -655,6 +656,16 @@ static int test_bdev_super(struct super_block *s, void *data)
return (void *)s->s_bdev == data;
}
+static void bdev_uevent(struct block_device *bdev, enum kobject_action action)
+{
+ if (bdev->bd_disk) {
+ if (bdev->bd_part)
+ kobject_uevent(&bdev->bd_part->kobj, action, NULL);
+ else
+ kobject_uevent(&bdev->bd_disk->kobj, action, NULL);
+ }
+}
+
struct super_block *get_sb_bdev(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
int (*fill_super)(struct super_block *, void *, int))
@@ -697,8 +708,10 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
up_write(&s->s_umount);
deactivate_super(s);
s = ERR_PTR(error);
- } else
+ } else {
s->s_flags |= MS_ACTIVE;
+ bdev_uevent(bdev, KOBJ_MOUNT);
+ }
}
return s;
@@ -713,6 +726,8 @@ EXPORT_SYMBOL(get_sb_bdev);
void kill_block_super(struct super_block *sb)
{
struct block_device *bdev = sb->s_bdev;
+
+ bdev_uevent(bdev, KOBJ_UMOUNT);
generic_shutdown_super(sb);
set_blocksize(bdev, sb->s_old_blocksize);
close_bdev_excl(bdev);
diff --git a/include/linux/device.h b/include/linux/device.h
index 4bb694c6737a..0b6148232700 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -106,6 +106,8 @@ struct device_driver {
struct kobject kobj;
struct list_head devices;
+ struct module * owner;
+
int (*probe) (struct device * dev);
int (*remove) (struct device * dev);
void (*shutdown) (struct device * dev);
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 331d25b9cc41..157ce2cc1642 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -22,10 +22,14 @@
#include <linux/sysfs.h>
#include <linux/rwsem.h>
#include <linux/kref.h>
+#include <linux/kobject_uevent.h>
#include <asm/atomic.h>
#define KOBJ_NAME_LEN 20
+/* counter to tag the hotplug event, read only except for the kobject core */
+extern u64 hotplug_seqnum;
+
struct kobject {
char * k_name;
char name[KOBJ_NAME_LEN];
@@ -59,9 +63,7 @@ extern void kobject_unregister(struct kobject *);
extern struct kobject * kobject_get(struct kobject *);
extern void kobject_put(struct kobject *);
-extern void kobject_hotplug(const char *action, struct kobject *);
-
-extern char * kobject_get_path(struct kset *, struct kobject *, int);
+extern char * kobject_get_path(struct kobject *, int);
struct kobj_type {
void (*release)(struct kobject *);
@@ -234,5 +236,11 @@ struct subsys_attribute {
extern int subsys_create_file(struct subsystem * , struct subsys_attribute *);
extern void subsys_remove_file(struct subsystem * , struct subsys_attribute *);
+#ifdef CONFIG_HOTPLUG
+extern void kobject_hotplug(struct kobject *kobj, enum kobject_action action);
+#else
+static inline void kobject_hotplug(struct kobject *kobj, enum kobject_action action) { }
+#endif
+
#endif /* __KERNEL__ */
#endif /* _KOBJECT_H_ */
diff --git a/include/linux/kobject_uevent.h b/include/linux/kobject_uevent.h
new file mode 100644
index 000000000000..ad0f04424522
--- /dev/null
+++ b/include/linux/kobject_uevent.h
@@ -0,0 +1,51 @@
+/*
+ * kobject_uevent.h - list of kobject user events that can be generated
+ *
+ * Copyright (C) 2004 IBM Corp.
+ * Copyright (C) 2004 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+#ifndef _KOBJECT_EVENT_H_
+#define _KOBJECT_EVENT_H_
+
+/*
+ * If you add an action here, you must also add the proper string to the
+ * lib/kobject_uevent.c file.
+ */
+
+enum kobject_action {
+ KOBJ_ADD = 0x00, /* add event, for hotplug */
+ KOBJ_REMOVE = 0x01, /* remove event, for hotplug */
+ KOBJ_CHANGE = 0x02, /* a sysfs attribute file has changed */
+ KOBJ_MOUNT = 0x03, /* mount event for block devices */
+ KOBJ_UMOUNT = 0x04, /* umount event for block devices */
+ KOBJ_MAX_ACTION, /* must be last action listed */
+};
+
+
+#ifdef CONFIG_KOBJECT_UEVENT
+int kobject_uevent(struct kobject *kobj,
+ enum kobject_action action,
+ struct attribute *attr);
+int kobject_uevent_atomic(struct kobject *kobj,
+ enum kobject_action action,
+ struct attribute *attr);
+#else
+static inline int kobject_uevent(struct kobject *kobj,
+ enum kobject_action action,
+ struct attribute *attr)
+{
+ return 0;
+}
+static inline int kobject_uevent_atomic(struct kobject *kobj,
+ enum kobject_action action,
+ struct attribute *attr)
+{
+ return 0;
+}
+#endif
+
+#endif
diff --git a/include/linux/module.h b/include/linux/module.h
index fbf2dfc90015..27512af4e394 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -445,6 +445,11 @@ int register_module_notifier(struct notifier_block * nb);
int unregister_module_notifier(struct notifier_block * nb);
extern void print_modules(void);
+
+struct device_driver;
+void module_add_driver(struct module *, struct device_driver *);
+void module_remove_driver(struct device_driver *);
+
#else /* !CONFIG_MODULES... */
#define EXPORT_SYMBOL(sym)
#define EXPORT_SYMBOL_GPL(sym)
@@ -534,6 +539,15 @@ static inline int unregister_module_notifier(struct notifier_block * nb)
static inline void print_modules(void)
{
}
+
+static inline void module_add_driver(struct module *, struct device_driver *)
+{
+}
+
+static inline void module_remove_driver(struct device_driver *)
+{
+}
+
#endif /* CONFIG_MODULES */
#define symbol_request(x) try_then_request_module(symbol_get(x), "symbol:" #x)
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 01864a8fd17f..e969de36edaa 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -17,6 +17,7 @@
#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
+#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */
#define MAX_LINKS 32
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 3f76ca047d2f..61ba98e2a3d8 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -631,9 +631,11 @@ struct pci_dynids {
unsigned int use_driver_data:1; /* pci_driver->driver_data is used */
};
+struct module;
struct pci_driver {
struct list_head node;
char *name;
+ struct module *owner;
const struct pci_device_id *id_table; /* must be non-NULL for probe to be called */
int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
diff --git a/init/Kconfig b/init/Kconfig
index 3f83eb242049..0d8b1bfc2e8c 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -205,6 +205,25 @@ config HOTPLUG
agent" (/sbin/hotplug) to load modules and set up software needed
to use devices as you hotplug them.
+config KOBJECT_UEVENT
+ bool "Kernel Userspace Events"
+ depends on NET
+ default y
+ help
+ This option enables the kernel userspace event layer, which is a
+ simple mechanism for kernel-to-user communication over a netlink
+ socket.
+ The goal of the kernel userspace events layer is to provide a simple
+ and efficient events system, that notifies userspace about kobject
+ state changes. This will enable applications to just listen for
+ events instead of polling system devices and files.
+ Hotplug events (kobject addition and removal) are also available on
+ the netlink socket in addition to the execution of /sbin/hotplug if
+ CONFIG_HOTPLUG is enabled.
+
+ Say Y, unless you are building a system requiring minimal memory
+ consumption.
+
config IKCONFIG
bool "Kernel .config support"
---help---
diff --git a/kernel/Makefile b/kernel/Makefile
index 5e65d330ca29..93155e52bb54 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -7,7 +7,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
sysctl.o capability.o ptrace.o timer.o user.o \
signal.o sys.o kmod.o workqueue.o pid.o \
rcupdate.o intermodule.o extable.o params.o posix-timers.o \
- kthread.o
+ kthread.o
obj-$(CONFIG_FUTEX) += futex.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
@@ -24,6 +24,7 @@ obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_SYSFS) += ksysfs.o
ifneq ($(CONFIG_IA64),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c
new file mode 100644
index 000000000000..31f1a60df733
--- /dev/null
+++ b/kernel/ksysfs.c
@@ -0,0 +1,56 @@
+/*
+ * kernel/ksysfs.c - sysfs attributes in /sys/kernel, which
+ * are not related to any other subsystem
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file is release under the GPLv2
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kobject.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#define KERNEL_ATTR_RO(_name) \
+static struct subsys_attribute _name##_attr = __ATTR_RO(_name)
+
+#define KERNEL_ATTR_RW(_name) \
+static struct subsys_attribute _name##_attr = \
+ __ATTR(_name, 0644, _name##_show, _name##_store)
+
+#ifdef CONFIG_HOTPLUG
+static ssize_t hotplug_seqnum_show(struct subsystem *subsys, char *page)
+{
+ return sprintf(page, "%llu\n", (unsigned long long)hotplug_seqnum);
+}
+KERNEL_ATTR_RO(hotplug_seqnum);
+#endif
+
+static decl_subsys(kernel, NULL, NULL);
+
+static struct attribute * kernel_attrs[] = {
+#ifdef CONFIG_HOTPLUG
+ &hotplug_seqnum_attr.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group kernel_attr_group = {
+ .attrs = kernel_attrs,
+};
+
+static int __init ksysfs_init(void)
+{
+ int error = subsystem_register(&kernel_subsys);
+ if (!error)
+ error = sysfs_create_group(&kernel_subsys.kset.kobj,
+ &kernel_attr_group);
+
+ return error;
+}
+
+core_initcall(ksysfs_init);
diff --git a/kernel/module.c b/kernel/module.c
index 8b3726655a87..bb08e9779607 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -34,6 +34,7 @@
#include <linux/vermagic.h>
#include <linux/notifier.h>
#include <linux/stop_machine.h>
+#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/semaphore.h>
#include <asm/cacheflush.h>
@@ -2140,6 +2141,26 @@ void print_modules(void)
printk("\n");
}
+void module_add_driver(struct module *mod, struct device_driver *drv)
+{
+ if (!mod || !drv)
+ return;
+ if (!mod->mkobj)
+ return;
+
+ /* Don't check return code; this call is idempotent */
+ sysfs_create_link(&drv->kobj, &mod->mkobj->kobj, "module");
+}
+EXPORT_SYMBOL(module_add_driver);
+
+void module_remove_driver(struct device_driver *drv)
+{
+ if (!drv)
+ return;
+ sysfs_remove_link(&drv->kobj, "module");
+}
+EXPORT_SYMBOL(module_remove_driver);
+
#ifdef CONFIG_MODVERSIONS
/* Generate the signature for struct module here, too, for modversions. */
void struct_module(struct module *mod) { return; }
diff --git a/lib/Makefile b/lib/Makefile
index da5dca19c2c8..24bbe1cac5bd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -6,7 +6,7 @@
lib-y := errno.o ctype.o string.o vsprintf.o cmdline.o \
bust_spinlocks.o rbtree.o radix-tree.o dump_stack.o \
kobject.o kref.o idr.o div64.o parser.o int_sqrt.o \
- bitmap.o extable.o
+ bitmap.o extable.o kobject_uevent.o
lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
diff --git a/lib/kobject.c b/lib/kobject.c
index a971b8e55e6b..d623f0575f23 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -63,7 +63,7 @@ static inline struct kobject * to_kobj(struct list_head * entry)
return container_of(entry,struct kobject,entry);
}
-static int get_kobj_path_length(struct kset *kset, struct kobject *kobj)
+static int get_kobj_path_length(struct kobject *kobj)
{
int length = 1;
struct kobject * parent = kobj;
@@ -79,7 +79,7 @@ static int get_kobj_path_length(struct kset *kset, struct kobject *kobj)
return length;
}
-static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path, int length)
+static void fill_kobj_path(struct kobject *kobj, char *path, int length)
{
struct kobject * parent;
@@ -99,146 +99,24 @@ static void fill_kobj_path(struct kset *kset, struct kobject *kobj, char *path,
* kobject_get_path - generate and return the path associated with a given kobj
* and kset pair. The result must be freed by the caller with kfree().
*
- * @kset: kset in question, with which to build the path
* @kobj: kobject in question, with which to build the path
* @gfp_mask: the allocation type used to allocate the path
*/
-char * kobject_get_path(struct kset *kset, struct kobject *kobj, int gfp_mask)
+char *kobject_get_path(struct kobject *kobj, int gfp_mask)
{
char *path;
int len;
- len = get_kobj_path_length(kset, kobj);
+ len = get_kobj_path_length(kobj);
path = kmalloc(len, gfp_mask);
if (!path)
return NULL;
memset(path, 0x00, len);
- fill_kobj_path(kset, kobj, path, len);
+ fill_kobj_path(kobj, path, len);
return path;
}
-#ifdef CONFIG_HOTPLUG
-
-#define BUFFER_SIZE 1024 /* should be enough memory for the env */
-#define NUM_ENVP 32 /* number of env pointers */
-static unsigned long sequence_num;
-static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
-
-static void kset_hotplug(const char *action, struct kset *kset,
- struct kobject *kobj)
-{
- char *argv [3];
- char **envp = NULL;
- char *buffer = NULL;
- char *scratch;
- int i = 0;
- int retval;
- char *kobj_path = NULL;
- char *name = NULL;
- unsigned long seq;
-
- /* 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 = 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)
- goto exit;
-
- 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] = NULL;
-
- /* 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;
-
- spin_lock(&sequence_lock);
- seq = sequence_num++;
- spin_unlock(&sequence_lock);
-
- envp [i++] = scratch;
- scratch += sprintf(scratch, "SEQNUM=%ld", seq) + 1;
-
- kobj_path = kobject_get_path(kset, kobj, GFP_KERNEL);
- if (!kobj_path)
- goto exit;
-
- 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 %s\n", __FUNCTION__, argv[0], argv[1],
- envp[0], envp[1], envp[2], envp[3], envp[4]);
- 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);
- kfree(envp);
- return;
-}
-
-void kobject_hotplug(const char *action, struct kobject *kobj)
-{
- struct kobject * top_kobj = kobj;
-
- /* If this kobj does not belong to a kset,
- try to find a parent that does. */
- 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(action, top_kobj->kset, kobj);
-}
-#else
-void kobject_hotplug(const char *action, struct kobject *kobj)
-{
- return;
-}
-#endif /* CONFIG_HOTPLUG */
-
/**
* kobject_init - initialize object.
* @kobj: object in question.
@@ -308,7 +186,7 @@ int kobject_add(struct kobject * kobj)
if (parent)
kobject_put(parent);
} else {
- kobject_hotplug("add", kobj);
+ kobject_hotplug(kobj, KOBJ_ADD);
}
return error;
@@ -422,7 +300,7 @@ int kobject_rename(struct kobject * kobj, char *new_name)
void kobject_del(struct kobject * kobj)
{
- kobject_hotplug("remove", kobj);
+ kobject_hotplug(kobj, KOBJ_REMOVE);
sysfs_remove_dir(kobj);
unlink(kobj);
}
@@ -654,7 +532,6 @@ EXPORT_SYMBOL(kobject_put);
EXPORT_SYMBOL(kobject_add);
EXPORT_SYMBOL(kobject_del);
EXPORT_SYMBOL(kobject_rename);
-EXPORT_SYMBOL(kobject_hotplug);
EXPORT_SYMBOL(kset_register);
EXPORT_SYMBOL(kset_unregister);
diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c
new file mode 100644
index 000000000000..33e0cef31ba8
--- /dev/null
+++ b/lib/kobject_uevent.c
@@ -0,0 +1,295 @@
+/*
+ * kernel userspace event delivery
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004 Novell, Inc. All rights reserved.
+ * Copyright (C) 2004 IBM, Inc. All rights reserved.
+ *
+ * Licensed under the GNU GPL v2.
+ *
+ * Authors:
+ * Robert Love <rml@novell.com>
+ * Kay Sievers <kay.sievers@vrfy.org>
+ * Arjan van de Ven <arjanv@redhat.com>
+ * Greg Kroah-Hartman <greg@kroah.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/string.h>
+#include <linux/kobject_uevent.h>
+#include <linux/kobject.h>
+#include <net/sock.h>
+
+/*
+ * These must match up with the values for enum kobject_action
+ * as found in include/linux/kobject_uevent.h
+ */
+static char *actions[] = {
+ "add", /* 0x00 */
+ "remove", /* 0x01 */
+ "change", /* 0x02 */
+ "mount", /* 0x03 */
+ "umount", /* 0x04 */
+};
+
+static char *action_to_string(enum kobject_action action)
+{
+ if (action >= KOBJ_MAX_ACTION)
+ return NULL;
+ else
+ return actions[action];
+}
+
+#ifdef CONFIG_KOBJECT_UEVENT
+static struct sock *uevent_sock;
+
+/**
+ * send_uevent - notify userspace by sending event trough netlink socket
+ *
+ * @signal: signal name
+ * @obj: object path (kobject)
+ * @buf: buffer used to pass auxiliary data like the hotplug environment
+ * @buflen:
+ * gfp_mask:
+ */
+static int send_uevent(const char *signal, const char *obj, const void *buf,
+ int buflen, int gfp_mask)
+{
+ struct sk_buff *skb;
+ char *pos;
+ int len;
+
+ if (!uevent_sock)
+ return -EIO;
+
+ len = strlen(signal) + 1;
+ len += strlen(obj) + 1;
+ len += buflen;
+
+ skb = alloc_skb(len, gfp_mask);
+ if (!skb)
+ return -ENOMEM;
+
+ pos = skb_put(skb, len);
+
+ pos += sprintf(pos, "%s@%s", signal, obj) + 1;
+ memcpy(pos, buf, buflen);
+
+ return netlink_broadcast(uevent_sock, skb, 0, 1, gfp_mask);
+}
+
+static int do_kobject_uevent(struct kobject *kobj, enum kobject_action action,
+ struct attribute *attr, int gfp_mask)
+{
+ char *path;
+ char *attrpath;
+ char *signal;
+ int len;
+ int rc = -ENOMEM;
+
+ path = kobject_get_path(kobj, gfp_mask);
+ if (!path)
+ return -ENOMEM;
+
+ signal = action_to_string(action);
+ if (!signal)
+ return -EINVAL;
+
+ if (attr) {
+ len = strlen(path);
+ len += strlen(attr->name) + 2;
+ attrpath = kmalloc(len, gfp_mask);
+ if (!attrpath)
+ goto exit;
+ sprintf(attrpath, "%s/%s", path, attr->name);
+ rc = send_uevent(signal, attrpath, NULL, 0, gfp_mask);
+ kfree(attrpath);
+ } else {
+ rc = send_uevent(signal, path, NULL, 0, gfp_mask);
+ }
+
+exit:
+ kfree(path);
+ return rc;
+}
+
+/**
+ * kobject_uevent - notify userspace by sending event through netlink socket
+ *
+ * @signal: signal name
+ * @kobj: struct kobject that the event is happening to
+ * @attr: optional struct attribute the event belongs to
+ */
+int kobject_uevent(struct kobject *kobj, enum kobject_action action,
+ struct attribute *attr)
+{
+ return do_kobject_uevent(kobj, action, attr, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(kobject_uevent);
+
+int kobject_uevent_atomic(struct kobject *kobj, enum kobject_action action,
+ struct attribute *attr)
+{
+ return do_kobject_uevent(kobj, action, attr, GFP_ATOMIC);
+}
+
+EXPORT_SYMBOL_GPL(kobject_uevent_atomic);
+
+static int __init kobject_uevent_init(void)
+{
+ uevent_sock = netlink_kernel_create(NETLINK_KOBJECT_UEVENT, NULL);
+
+ if (!uevent_sock) {
+ printk(KERN_ERR
+ "kobject_uevent: unable to create netlink socket!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+core_initcall(kobject_uevent_init);
+
+#else
+static inline int send_uevent(const char *signal, const char *obj,
+ const void *buf, int buflen, int gfp_mask)
+{
+ return 0;
+}
+
+#endif /* CONFIG_KOBJECT_UEVENT */
+
+
+#ifdef CONFIG_HOTPLUG
+u64 hotplug_seqnum;
+static spinlock_t sequence_lock = SPIN_LOCK_UNLOCKED;
+
+#define BUFFER_SIZE 1024 /* should be enough memory for the env */
+#define NUM_ENVP 32 /* number of env pointers */
+/**
+ * kobject_hotplug - notify userspace by executing /sbin/hotplug
+ *
+ * @action: action that is happening (usually "ADD" or "REMOVE")
+ * @kobj: struct kobject that the action is happening to
+ */
+void kobject_hotplug(struct kobject *kobj, enum kobject_action action)
+{
+ char *argv [3];
+ char **envp = NULL;
+ char *buffer = NULL;
+ char *scratch;
+ int i = 0;
+ int retval;
+ char *kobj_path = NULL;
+ char *name = NULL;
+ char *action_string;
+ u64 seq;
+ struct kobject *top_kobj = kobj;
+ struct kset *kset;
+
+ 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 = top_kobj->kset;
+ else
+ return;
+
+ /* If the kset has a filter operation, call it.
+ Skip the event, if the filter returns zero. */
+ if (kset->hotplug_ops->filter) {
+ if (!kset->hotplug_ops->filter(kset, kobj))
+ return;
+ }
+
+ pr_debug ("%s\n", __FUNCTION__);
+
+ action_string = action_to_string(action);
+ if (!action_string)
+ return;
+
+ envp = 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)
+ goto exit;
+
+ 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] = NULL;
+
+ /* 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_string) + 1;
+
+ kobj_path = kobject_get_path(kobj, GFP_KERNEL);
+ if (!kobj_path)
+ goto exit;
+
+ envp [i++] = scratch;
+ scratch += sprintf (scratch, "DEVPATH=%s", kobj_path) + 1;
+
+ spin_lock(&sequence_lock);
+ seq = ++hotplug_seqnum;
+ spin_unlock(&sequence_lock);
+
+ envp [i++] = scratch;
+ scratch += sprintf(scratch, "SEQNUM=%lld", (long long)seq) + 1;
+
+ envp [i++] = scratch;
+ scratch += sprintf(scratch, "SUBSYSTEM=%s", name) + 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 %s\n", __FUNCTION__, argv[0], argv[1],
+ envp[0], envp[1], envp[2], envp[3], envp[4]);
+
+ send_uevent(action_string, kobj_path, buffer, scratch - buffer, GFP_KERNEL);
+
+ if (!hotplug_path[0])
+ goto exit;
+
+ 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);
+ kfree(envp);
+ return;
+}
+EXPORT_SYMBOL(kobject_hotplug);
+#endif /* CONFIG_HOTPLUG */
+
+