[PATCH/RFC v5 4/5]: core: Add dump device to call on oopses and panics

Simon Kagstrom
2009-10-13T13:22:44Z

The core functionality is implemented as per Linus suggestion from

http://lists.infradead.org/pipermail/linux-mtd/2009-October/027620.html

a struct dump_device type has been added which contains callbacks to
dump the kernel log buffers on crashes. The dump_kmsg function gets
called from oops_exit() and panic() and invokes these callbacks.

Signed-off-by: Simon Kagstrom <simon.kagstrom@netinsight.net>
---
include/linux/dump_device.h | 33 +++++++++++++++++
kernel/panic.c | 3 ++
kernel/printk.c | 83 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 119 insertions(+), 0 deletions(-)
create mode 100644 include/linux/dump_device.h

diff --git a/include/linux/dump_device.h b/include/linux/dump_device.h
new file mode 100644
index 0000000..8c3f378
--- /dev/null
+++ b/include/linux/dump_device.h
@@ -0,0 +1,33 @@
+/*
+ * linux/include/dump_device.h
+ *
+ * Copyright (C) 2009 Net Insight
+ *
+ * Author: Simon Kagstrom <simon.kagstrom@netinsight.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+#ifndef _LINUX_DUMP_DEVICE_H
+#define _LINUX_DUMP_DEVICE_H
+
+#include <linux/list.h>
+
+struct dump_device {
+ void (*oops)(struct dump_device *, const char *, unsigned long,
+ const char *, unsigned long);
+ void (*panic)(struct dump_device *, const char *, unsigned long,
+ const char *, unsigned long);
+ int (*setup)(struct dump_device *);
+ void *priv;
+ struct list_head list;
+};
+
+void dump_kmsg(int panic);
+
+int register_dumpdevice(struct dump_device *dump, void *priv);
+
+void unregister_dumpdevice(struct dump_device *dump);
+
+#endif /* _LINUX_DUMP_DEVICE_H */
diff --git a/kernel/panic.c b/kernel/panic.c
index c0b33b8..e7dbf2b 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -8,6 +8,7 @@
* This function is used through-out the kernel (including mm and fs)
* to indicate a major problem.
*/
+#include <linux/dump_device.h>
#include <linux/debug_locks.h>
#include <linux/interrupt.h>
#include <linux/kallsyms.h>
@@ -76,6 +77,7 @@ NORET_TYPE void panic(const char * fmt, ...)
dump_stack();
#endif

+ dump_kmsg(1);
/*
* If we have crashed and we have a crash kernel loaded let it handle
* everything else.
@@ -341,6 +343,7 @@ void oops_exit(void)
{
do_oops_enter_exit();
print_oops_end_marker();
+ dump_kmsg(0);
}

#ifdef WANT_WARN_ON_SLOWPATH
diff --git a/kernel/printk.c b/kernel/printk.c
index f38b07f..5e1fe73 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -33,6 +33,7 @@
#include <linux/bootmem.h>
#include <linux/syscalls.h>
#include <linux/kexec.h>
+#include <linux/dump_device.h>

#include <asm/uaccess.h>

@@ -1405,3 +1406,85 @@ bool printk_timed_ratelimit(unsigned long *caller_jiffies,
}
EXPORT_SYMBOL(printk_timed_ratelimit);
#endif
+
+static LIST_HEAD(dump_list);
+static DEFINE_RWLOCK(dump_list_lock);
+
+/**
+ * register_dump_device - register a dumpdevice.
+ * @dump: pointer to the dump structure
+ * @priv: private data for the structure
+ *
+ * Adds a dump device to the system. The oops and panic callbacks
+ * in the structure will be called when the kernel oopses and panics
+ * respectively. At least one of these must be set. Returns zero on
+ * success and -EINVAL otherwise.
+ */
+int register_dumpdevice(struct dump_device *dump, void *priv)
+{
+ /* We need at least one of these set */
+ if (!dump->oops && !dump->panic)
+ return -EINVAL;
+ if (dump->setup && dump->setup(dump) != 0)
+ return -EINVAL;
+ dump->priv = priv;
+
+ INIT_LIST_HEAD(&dump->list);
+ write_lock(&dump_list_lock);
+ list_add(&dump->list, &dump_list);
+ write_unlock(&dump_list_lock);
+ return 0;
+}
+EXPORT_SYMBOL(register_dumpdevice);
+
+/**
+ * unregister_dump_device - unregister a dumpdevice.
+ * @dump: pointer to the dump structure
+ *
+ * Removes a dump device from the system.
+ */
+void unregister_dumpdevice(struct dump_device *dump)
+{
+ write_lock(&dump_list_lock);
+ list_del(&dump->list);
+ write_unlock(&dump_list_lock);
+}
+EXPORT_SYMBOL(unregister_dumpdevice);
+
+/**
+ * dump_kmsg - dump kernel log to dump devices.
+ * @panic: if this is a panic (1) or an oops (0)
+ *
+ * Iterate through each of the dump devices and call the oops/panic
+ * callbacks with the log buffer.
+ */
+void dump_kmsg(int panic)
+{
+ unsigned long len = ACCESS_ONCE(log_end);
+ struct dump_device *dump;
+ const char *s1, *s2;
+ unsigned long l1, l2;
+
+ s1 = "";
+ l1 = 0;
+ s2 = log_buf;
+ l2 = len;
+
+ /* Have we rotated around the circular buffer? */
+ if (len > log_buf_len) {
+ unsigned long pos = (len & LOG_BUF_MASK);
+
+ s1 = log_buf + pos;
+ l1 = log_buf_len - pos;
+
+ s2 = log_buf;
+ l2 = pos;
+ }
+
+ list_for_each_entry(dump, &dump_list, list) {
+ if (panic && dump->panic)
+ dump->panic(dump, s1, l1, s2, l2);
+ else if (!panic && dump->oops)
+ dump->oops(dump, s1, l1, s2, l2);
+ }
+}
--
1.6.0.4