diff options
author | Felix Fietkau <nbd@openwrt.org> | 2010-07-20 01:54:21 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2010-07-20 01:54:21 +0000 |
commit | 9f6c22b4974a89a24c0d8758eb9b9b57a93867a4 (patch) | |
tree | 3f188757412396d4625f6d648c9604f3f1ce949b /target/linux/generic/patches-2.6.32/930-kmsg_dump_backport.patch | |
parent | ae2c9dcb2b682a06e6e4d18246fa36b9c5214df1 (diff) | |
download | master-31e0f0ae-9f6c22b4974a89a24c0d8758eb9b9b57a93867a4.tar.gz master-31e0f0ae-9f6c22b4974a89a24c0d8758eb9b9b57a93867a4.tar.bz2 master-31e0f0ae-9f6c22b4974a89a24c0d8758eb9b9b57a93867a4.zip |
kernel: add the new 'crashlog' feature, which tries to store kernel oops/panic logs in a fixed location in RAM to recover them after the reboot and make them available to user space using debugfs
SVN-Revision: 22305
Diffstat (limited to 'target/linux/generic/patches-2.6.32/930-kmsg_dump_backport.patch')
-rw-r--r-- | target/linux/generic/patches-2.6.32/930-kmsg_dump_backport.patch | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/target/linux/generic/patches-2.6.32/930-kmsg_dump_backport.patch b/target/linux/generic/patches-2.6.32/930-kmsg_dump_backport.patch new file mode 100644 index 0000000000..4b0369059e --- /dev/null +++ b/target/linux/generic/patches-2.6.32/930-kmsg_dump_backport.patch @@ -0,0 +1,205 @@ +--- /dev/null ++++ b/include/linux/kmsg_dump.h +@@ -0,0 +1,44 @@ ++/* ++ * linux/include/kmsg_dump.h ++ * ++ * Copyright (C) 2009 Net Insight AB ++ * ++ * 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_KMSG_DUMP_H ++#define _LINUX_KMSG_DUMP_H ++ ++#include <linux/list.h> ++ ++enum kmsg_dump_reason { ++ KMSG_DUMP_OOPS, ++ KMSG_DUMP_PANIC, ++}; ++ ++/** ++ * struct kmsg_dumper - kernel crash message dumper structure ++ * @dump: The callback which gets called on crashes. The buffer is passed ++ * as two sections, where s1 (length l1) contains the older ++ * messages and s2 (length l2) contains the newer. ++ * @list: Entry in the dumper list (private) ++ * @registered: Flag that specifies if this is already registered ++ */ ++struct kmsg_dumper { ++ void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, ++ const char *s1, unsigned long l1, ++ const char *s2, unsigned long l2); ++ struct list_head list; ++ int registered; ++}; ++ ++void kmsg_dump(enum kmsg_dump_reason reason); ++ ++int kmsg_dump_register(struct kmsg_dumper *dumper); ++ ++int kmsg_dump_unregister(struct kmsg_dumper *dumper); ++ ++#endif /* _LINUX_KMSG_DUMP_H */ +--- a/kernel/panic.c ++++ b/kernel/panic.c +@@ -10,6 +10,7 @@ + */ + #include <linux/debug_locks.h> + #include <linux/interrupt.h> ++#include <linux/kmsg_dump.h> + #include <linux/kallsyms.h> + #include <linux/notifier.h> + #include <linux/module.h> +@@ -74,6 +75,7 @@ NORET_TYPE void panic(const char * fmt, + dump_stack(); + #endif + ++ kmsg_dump(KMSG_DUMP_PANIC); + /* + * If we have crashed and we have a crash kernel loaded let it handle + * everything else. +@@ -339,6 +341,7 @@ void oops_exit(void) + { + do_oops_enter_exit(); + print_oops_end_marker(); ++ kmsg_dump(KMSG_DUMP_OOPS); + } + + #ifdef WANT_WARN_ON_SLOWPATH +--- 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/kmsg_dump.h> + + #include <asm/uaccess.h> + +@@ -1405,3 +1406,121 @@ bool printk_timed_ratelimit(unsigned lon + } + EXPORT_SYMBOL(printk_timed_ratelimit); + #endif ++ ++static DEFINE_SPINLOCK(dump_list_lock); ++static LIST_HEAD(dump_list); ++ ++/** ++ * kmsg_dump_register - register a kernel log dumper. ++ * @dump: pointer to the kmsg_dumper structure ++ * ++ * Adds a kernel log dumper to the system. The dump callback in the ++ * structure will be called when the kernel oopses or panics and must be ++ * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise. ++ */ ++int kmsg_dump_register(struct kmsg_dumper *dumper) ++{ ++ unsigned long flags; ++ int err = -EBUSY; ++ ++ /* The dump callback needs to be set */ ++ if (!dumper->dump) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&dump_list_lock, flags); ++ /* Don't allow registering multiple times */ ++ if (!dumper->registered) { ++ dumper->registered = 1; ++ list_add_tail(&dumper->list, &dump_list); ++ err = 0; ++ } ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(kmsg_dump_register); ++ ++/** ++ * kmsg_dump_unregister - unregister a kmsg dumper. ++ * @dump: pointer to the kmsg_dumper structure ++ * ++ * Removes a dump device from the system. Returns zero on success and ++ * %-EINVAL otherwise. ++ */ ++int kmsg_dump_unregister(struct kmsg_dumper *dumper) ++{ ++ unsigned long flags; ++ int err = -EINVAL; ++ ++ spin_lock_irqsave(&dump_list_lock, flags); ++ if (dumper->registered) { ++ dumper->registered = 0; ++ list_del(&dumper->list); ++ err = 0; ++ } ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++ ++ return err; ++} ++EXPORT_SYMBOL_GPL(kmsg_dump_unregister); ++ ++static const char const *kmsg_reasons[] = { ++ [KMSG_DUMP_OOPS] = "oops", ++ [KMSG_DUMP_PANIC] = "panic", ++}; ++ ++static const char *kmsg_to_str(enum kmsg_dump_reason reason) ++{ ++ if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0) ++ return "unknown"; ++ ++ return kmsg_reasons[reason]; ++} ++ ++/** ++ * kmsg_dump - dump kernel log to kernel message dumpers. ++ * @reason: the reason (oops, panic etc) for dumping ++ * ++ * Iterate through each of the dump devices and call the oops/panic ++ * callbacks with the log buffer. ++ */ ++void kmsg_dump(enum kmsg_dump_reason reason) ++{ ++ unsigned long end; ++ unsigned chars; ++ struct kmsg_dumper *dumper; ++ const char *s1, *s2; ++ unsigned long l1, l2; ++ unsigned long flags; ++ ++ /* Theoretically, the log could move on after we do this, but ++ there's not a lot we can do about that. The new messages ++ will overwrite the start of what we dump. */ ++ spin_lock_irqsave(&logbuf_lock, flags); ++ end = log_end & LOG_BUF_MASK; ++ chars = logged_chars; ++ spin_unlock_irqrestore(&logbuf_lock, flags); ++ ++ if (logged_chars > end) { ++ s1 = log_buf + log_buf_len - logged_chars + end; ++ l1 = logged_chars - end; ++ ++ s2 = log_buf; ++ l2 = end; ++ } else { ++ s1 = ""; ++ l1 = 0; ++ ++ s2 = log_buf + end - logged_chars; ++ l2 = logged_chars; ++ } ++ ++ if (!spin_trylock_irqsave(&dump_list_lock, flags)) { ++ printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", ++ kmsg_to_str(reason)); ++ return; ++ } ++ list_for_each_entry(dumper, &dump_list, list) ++ dumper->dump(dumper, reason, s1, l1, s2, l2); ++ spin_unlock_irqrestore(&dump_list_lock, flags); ++} |