diff options
| author | Greg Kroah-Hartman <greg@kroah.com> | 2004-09-24 10:07:32 -0700 |
|---|---|---|
| committer | Greg Kroah-Hartman <greg@kroah.com> | 2004-09-24 10:07:32 -0700 |
| commit | f413349679a606cd3f0d0e6b87c4a518173ef8af (patch) | |
| tree | 6062da2db1a2e3d716c39c0abc7fa2d16a2d861e | |
| parent | 2d952a582ef77577dc903a756ab365520bdbd1f7 (diff) | |
| parent | a1e66cc0011a9f9fae58a5609e329357d747635d (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.c | 2 | ||||
| -rw-r--r-- | drivers/base/firmware_class.c | 2 | ||||
| -rw-r--r-- | drivers/pci/pci-driver.c | 1 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 2 | ||||
| -rw-r--r-- | drivers/usb/serial/bus.c | 1 | ||||
| -rw-r--r-- | fs/super.c | 17 | ||||
| -rw-r--r-- | include/linux/device.h | 2 | ||||
| -rw-r--r-- | include/linux/kobject.h | 14 | ||||
| -rw-r--r-- | include/linux/kobject_uevent.h | 51 | ||||
| -rw-r--r-- | include/linux/module.h | 14 | ||||
| -rw-r--r-- | include/linux/netlink.h | 1 | ||||
| -rw-r--r-- | include/linux/pci.h | 2 | ||||
| -rw-r--r-- | init/Kconfig | 19 | ||||
| -rw-r--r-- | kernel/Makefile | 3 | ||||
| -rw-r--r-- | kernel/ksysfs.c | 56 | ||||
| -rw-r--r-- | kernel/module.c | 21 | ||||
| -rw-r--r-- | lib/Makefile | 2 | ||||
| -rw-r--r-- | lib/kobject.c | 137 | ||||
| -rw-r--r-- | lib/kobject_uevent.c | 295 |
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 */ + + |
