2 +++ b/include/linux/kmsg_dump.h
5 + * linux/include/kmsg_dump.h
7 + * Copyright (C) 2009 Net Insight AB
9 + * Author: Simon Kagstrom <simon.kagstrom@netinsight.net>
11 + * This file is subject to the terms and conditions of the GNU General Public
12 + * License. See the file COPYING in the main directory of this archive
15 +#ifndef _LINUX_KMSG_DUMP_H
16 +#define _LINUX_KMSG_DUMP_H
18 +#include <linux/list.h>
20 +enum kmsg_dump_reason {
26 + * struct kmsg_dumper - kernel crash message dumper structure
27 + * @dump: The callback which gets called on crashes. The buffer is passed
28 + * as two sections, where s1 (length l1) contains the older
29 + * messages and s2 (length l2) contains the newer.
30 + * @list: Entry in the dumper list (private)
31 + * @registered: Flag that specifies if this is already registered
34 + void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason,
35 + const char *s1, unsigned long l1,
36 + const char *s2, unsigned long l2);
37 + struct list_head list;
41 +void kmsg_dump(enum kmsg_dump_reason reason);
43 +int kmsg_dump_register(struct kmsg_dumper *dumper);
45 +int kmsg_dump_unregister(struct kmsg_dumper *dumper);
47 +#endif /* _LINUX_KMSG_DUMP_H */
52 #include <linux/debug_locks.h>
53 #include <linux/interrupt.h>
54 +#include <linux/kmsg_dump.h>
55 #include <linux/kallsyms.h>
56 #include <linux/notifier.h>
57 #include <linux/module.h>
58 @@ -74,6 +75,7 @@ NORET_TYPE void panic(const char * fmt,
62 + kmsg_dump(KMSG_DUMP_PANIC);
64 * If we have crashed and we have a crash kernel loaded let it handle
66 @@ -339,6 +341,7 @@ void oops_exit(void)
69 print_oops_end_marker();
70 + kmsg_dump(KMSG_DUMP_OOPS);
73 #ifdef WANT_WARN_ON_SLOWPATH
77 #include <linux/bootmem.h>
78 #include <linux/syscalls.h>
79 #include <linux/kexec.h>
80 +#include <linux/kmsg_dump.h>
82 #include <asm/uaccess.h>
84 @@ -1405,3 +1406,121 @@ bool printk_timed_ratelimit(unsigned lon
86 EXPORT_SYMBOL(printk_timed_ratelimit);
89 +static DEFINE_SPINLOCK(dump_list_lock);
90 +static LIST_HEAD(dump_list);
93 + * kmsg_dump_register - register a kernel log dumper.
94 + * @dump: pointer to the kmsg_dumper structure
96 + * Adds a kernel log dumper to the system. The dump callback in the
97 + * structure will be called when the kernel oopses or panics and must be
98 + * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise.
100 +int kmsg_dump_register(struct kmsg_dumper *dumper)
102 + unsigned long flags;
105 + /* The dump callback needs to be set */
109 + spin_lock_irqsave(&dump_list_lock, flags);
110 + /* Don't allow registering multiple times */
111 + if (!dumper->registered) {
112 + dumper->registered = 1;
113 + list_add_tail(&dumper->list, &dump_list);
116 + spin_unlock_irqrestore(&dump_list_lock, flags);
120 +EXPORT_SYMBOL_GPL(kmsg_dump_register);
123 + * kmsg_dump_unregister - unregister a kmsg dumper.
124 + * @dump: pointer to the kmsg_dumper structure
126 + * Removes a dump device from the system. Returns zero on success and
127 + * %-EINVAL otherwise.
129 +int kmsg_dump_unregister(struct kmsg_dumper *dumper)
131 + unsigned long flags;
134 + spin_lock_irqsave(&dump_list_lock, flags);
135 + if (dumper->registered) {
136 + dumper->registered = 0;
137 + list_del(&dumper->list);
140 + spin_unlock_irqrestore(&dump_list_lock, flags);
144 +EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
146 +static const char const *kmsg_reasons[] = {
147 + [KMSG_DUMP_OOPS] = "oops",
148 + [KMSG_DUMP_PANIC] = "panic",
151 +static const char *kmsg_to_str(enum kmsg_dump_reason reason)
153 + if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
156 + return kmsg_reasons[reason];
160 + * kmsg_dump - dump kernel log to kernel message dumpers.
161 + * @reason: the reason (oops, panic etc) for dumping
163 + * Iterate through each of the dump devices and call the oops/panic
164 + * callbacks with the log buffer.
166 +void kmsg_dump(enum kmsg_dump_reason reason)
170 + struct kmsg_dumper *dumper;
171 + const char *s1, *s2;
172 + unsigned long l1, l2;
173 + unsigned long flags;
175 + /* Theoretically, the log could move on after we do this, but
176 + there's not a lot we can do about that. The new messages
177 + will overwrite the start of what we dump. */
178 + spin_lock_irqsave(&logbuf_lock, flags);
179 + end = log_end & LOG_BUF_MASK;
180 + chars = logged_chars;
181 + spin_unlock_irqrestore(&logbuf_lock, flags);
183 + if (logged_chars > end) {
184 + s1 = log_buf + log_buf_len - logged_chars + end;
185 + l1 = logged_chars - end;
193 + s2 = log_buf + end - logged_chars;
197 + if (!spin_trylock_irqsave(&dump_list_lock, flags)) {
198 + printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n",
199 + kmsg_to_str(reason));
202 + list_for_each_entry(dumper, &dump_list, list)
203 + dumper->dump(dumper, reason, s1, l1, s2, l2);
204 + spin_unlock_irqrestore(&dump_list_lock, flags);