[patch] kernel sysfs events layer

Robert Love
2004-08-31T21:48:41Z

Here is the Kernel Events Layer rewritten as more of an asynchronous
sysfs change notifier. The concept of object and payload have been
removed. Instead, events are modeled as signals emitting from kobjects.
It is pretty simple.

The interface is now:

int send_kevent(enum kevent type, struct kset *kset,
struct kobject *kobj, const char *signal)

Say your processor (with kobject "kobj") is overheating. You might do

send_kevent(KEVENT_POWER, NULL, kobj, "overheating");

We could get rid of signal and just require passing a specific attribute
file in sysfs, which would presumably explain the reason for the event,
but I think having a single signal value is acceptable. The rest of the
payload has been ditched.

The basic idea here is to represent to user-space events as changes to
sysfs. Media was changed? Then that block device in sysfs emits a
"media_change" event.

This patch includes two example events: file system mount and unmount.

Kay has some utilities and examples at
http://vrfy.org/projects/kevents/
and
http://vrfy.org/projects/kdbusd/

The intention of this work is to hook the kernel into D-BUS, although
the implementation is agnostic and should work with any user-space
setup.

Best,

Robert Love

Kernel Events Layer. A simple sysfs change notifier over netlink.
Signed-Off-By: Robert Love <rml@novell.com>

fs/super.c | 11 ++++-
include/linux/kevent.h | 42 +++++++++++++++++++
include/linux/netlink.h | 1
init/Kconfig | 14 ++++++
kernel/Makefile | 1
kernel/kevent.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 173 insertions(+), 1 deletion(-)

diff -urN linux-2.6.9-rc1-mm2/fs/super.c linux/fs/super.c
--- linux-2.6.9-rc1-mm2/fs/super.c 2004-08-31 16:46:15.912777856 -0400
+++ linux/fs/super.c 2004-08-31 16:44:41.023203264 -0400
@@ -35,6 +35,7 @@
#include <linux/vfs.h>
#include <linux/writeback.h> /* for the emergency remount stuff */
#include <linux/idr.h>
+#include <linux/kevent.h>
#include <asm/uaccess.h>

@@ -875,8 +876,12 @@
up_write(&s->s_umount);
deactivate_super(s);
s = ERR_PTR(error);
- } else
+ } else {
s->s_flags |= MS_ACTIVE;
+ if (bdev->bd_disk)
+ send_kevent(KEVENT_FS, NULL,
+ &bdev->bd_disk->kobj, "mount");
+ }
}

return s;
@@ -891,6 +896,10 @@
void kill_block_super(struct super_block *sb)
{
struct block_device *bdev = sb->s_bdev;
+
+ if (bdev->bd_disk)
+ send_kevent(KEVENT_FS, NULL, &bdev->bd_disk->kobj, "umount");
+
generic_shutdown_super(sb);
set_blocksize(bdev, sb->s_old_blocksize);
close_bdev_excl(bdev);
diff -urN linux-2.6.9-rc1-mm2/include/linux/kevent.h linux/include/linux/kevent.h
--- linux-2.6.9-rc1-mm2/include/linux/kevent.h 1969-12-31 19:00:00.000000000 -0500
+++ linux/include/linux/kevent.h 2004-08-31 16:21:05.706364128 -0400
@@ -0,0 +1,42 @@
+#ifndef _LINUX_KEVENT_H
+#define _LINUX_KEVENT_H
+
+#include <linux/config.h>
+#include <linux/kobject.h>
+
+/* kevent types - these are used as the multicast group */
+enum kevent {
+ KEVENT_GENERAL = 0,
+ KEVENT_STORAGE = 1,
+ KEVENT_POWER = 2,
+ KEVENT_FS = 3,
+ KEVENT_HOTPLUG = 4,
+};
+
+#ifdef __KERNEL__
+#ifdef CONFIG_KERNEL_EVENTS
+
+int send_kevent(enum kevent type, struct kset *kset,
+ struct kobject *kobj, const char *signal);
+
+int send_kevent_atomic(enum kevent type, struct kset *kset,
+ struct kobject *kobj, const char *signal);
+
+#else
+
+static inline int send_kevent(enum kevent type, struct kset *kset,
+ struct kobject *kobj, const char *signal)
+{
+ return 0;
+}
+
+static inline int send_kevent_atomic(enum kevent type, struct kset *kset,
+ struct kobject *kobj, const char *signal)
+{
+ return 0;
+}
+
+#endif /* CONFIG_KERNEL_EVENTS */
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_KEVENT_H */
diff -urN linux-2.6.9-rc1-mm2/include/linux/netlink.h linux/include/linux/netlink.h
--- linux-2.6.9-rc1-mm2/include/linux/netlink.h 2004-08-31 16:46:16.316716448 -0400
+++ linux/include/linux/netlink.h 2004-08-31 16:44:41.372150216 -0400
@@ -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_KEVENT 15 /* Kernel messages to userspace */
#define NETLINK_TAPBASE 16 /* 16 to 31 are ethertap */

#define MAX_LINKS 32
diff -urN linux-2.6.9-rc1-mm2/init/Kconfig linux/init/Kconfig
--- linux-2.6.9-rc1-mm2/init/Kconfig 2004-08-31 16:46:16.454695472 -0400
+++ linux/init/Kconfig 2004-08-31 16:44:41.445139120 -0400
@@ -149,6 +149,20 @@
logging of avc messages output). Does not do system-call
auditing without CONFIG_AUDITSYSCALL.

+config KERNEL_EVENTS
+ bool "Kernel Events Layer"
+ depends on NET
+ default y
+ help
+ This option enables the kernel events layer, which is a simple
+ mechanism for kernel-to-user communication over a netlink socket.
+ The goal of the kernel events layer is to provide a simple and
+ efficient logging, error, and events system. Specifically, code
+ is available to link the events into D-BUS. Say Y, unless you
+ are building a system requiring minimal memory consumption.
+
+ D-BUS is available at http://dbus.freedesktop.org/
+
config AUDITSYSCALL
bool "Enable system-call auditing support"
depends on AUDIT && (X86 || PPC64 || ARCH_S390 || IA64)
diff -urN linux-2.6.9-rc1-mm2/kernel/kevent.c linux/kernel/kevent.c
--- linux-2.6.9-rc1-mm2/kernel/kevent.c 1969-12-31 19:00:00.000000000 -0500
+++ linux/kernel/kevent.c 2004-08-31 16:20:00.280310400 -0400
@@ -0,0 +1,105 @@
+/*
+ * kernel/kevent.c - sysfs event delivery via netlink socket
+ *
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004 Novell, 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>
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/string.h>
+#include <linux/kobject.h>
+#include <linux/kevent.h>
+#include <net/sock.h>
+
+static struct sock *kevent_sock = NULL; /* kevent's global netlink socket */
+
+/**
+ * send_kevent - send a message to user-space via the kernel events layer
+ */
+static int do_send_kevent(enum kevent type, int gfp_mask,
+ const char *object, const char *signal)
+{
+ struct sk_buff *skb;
+ char *buffer;
+ int len;
+
+ if (!kevent_sock)
+ return -EIO;
+
+ if (!object || !signal)
+ return -EINVAL;
+
+ len = strlen(object) + 1 + strlen(signal);
+
+ skb = alloc_skb(len, gfp_mask);
+ if (!skb)
+ return -ENOMEM;
+
+ buffer = skb_put(skb, len);
+
+ sprintf(buffer, "%s\n%s", object, signal);
+
+ return netlink_broadcast(kevent_sock, skb, 0, (1 << type), gfp_mask);
+}
+
+int send_kevent(enum kevent type, struct kset *kset,
+ struct kobject *kobj, const char *signal)
+{
+ const char *path;
+ int ret;
+
+ path = kobject_get_path(kset, kobj, GFP_KERNEL);
+ if (!path)
+ return -ENOMEM;
+
+ ret = do_send_kevent(type, GFP_KERNEL, path, signal);
+ kfree(path);
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(send_kevent);
+
+int send_kevent_atomic(enum kevent type, struct kset *kset,
+ struct kobject *kobj, const char *signal)
+{
+ const char *path;
+ int ret;
+
+ path = kobject_get_path(kset, kobj, GFP_ATOMIC);
+ if (!path)
+ return -ENOMEM;
+
+ ret = do_send_kevent(type, GFP_ATOMIC, path, signal);
+ kfree(path);
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(send_kevent_atomic);
+
+static int kevent_init(void)
+{
+ kevent_sock = netlink_kernel_create(NETLINK_KEVENT, NULL);
+
+ if (!kevent_sock) {
+ printk(KERN_ERR
+ "kevent: unable to create netlink socket!\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+module_init(kevent_init);
diff -urN linux-2.6.9-rc1-mm2/kernel/Makefile linux/kernel/Makefile
--- linux-2.6.9-rc1-mm2/kernel/Makefile 2004-08-31 16:46:16.471692888 -0400
+++ linux/kernel/Makefile 2004-08-31 16:45:49.025865288 -0400
@@ -27,6 +27,7 @@
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_KERNEL_EVENTS) += kevent.o

ifneq ($(CONFIG_IA64),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is