[backfire] backport r26906
[openwrt/svn-archive/archive.git] / target / linux / generic-2.6 / patches-2.6.32 / 930-kmsg_dump_backport.patch
1 --- /dev/null
2 +++ b/include/linux/kmsg_dump.h
3 @@ -0,0 +1,44 @@
4 +/*
5 + * linux/include/kmsg_dump.h
6 + *
7 + * Copyright (C) 2009 Net Insight AB
8 + *
9 + * Author: Simon Kagstrom <simon.kagstrom@netinsight.net>
10 + *
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
13 + * for more details.
14 + */
15 +#ifndef _LINUX_KMSG_DUMP_H
16 +#define _LINUX_KMSG_DUMP_H
17 +
18 +#include <linux/list.h>
19 +
20 +enum kmsg_dump_reason {
21 + KMSG_DUMP_OOPS,
22 + KMSG_DUMP_PANIC,
23 +};
24 +
25 +/**
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
32 + */
33 +struct kmsg_dumper {
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;
38 + int registered;
39 +};
40 +
41 +void kmsg_dump(enum kmsg_dump_reason reason);
42 +
43 +int kmsg_dump_register(struct kmsg_dumper *dumper);
44 +
45 +int kmsg_dump_unregister(struct kmsg_dumper *dumper);
46 +
47 +#endif /* _LINUX_KMSG_DUMP_H */
48 --- a/kernel/panic.c
49 +++ b/kernel/panic.c
50 @@ -10,6 +10,7 @@
51 */
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,
59 dump_stack();
60 #endif
61
62 + kmsg_dump(KMSG_DUMP_PANIC);
63 /*
64 * If we have crashed and we have a crash kernel loaded let it handle
65 * everything else.
66 @@ -339,6 +341,7 @@ void oops_exit(void)
67 {
68 do_oops_enter_exit();
69 print_oops_end_marker();
70 + kmsg_dump(KMSG_DUMP_OOPS);
71 }
72
73 #ifdef WANT_WARN_ON_SLOWPATH
74 --- a/kernel/printk.c
75 +++ b/kernel/printk.c
76 @@ -33,6 +33,7 @@
77 #include <linux/bootmem.h>
78 #include <linux/syscalls.h>
79 #include <linux/kexec.h>
80 +#include <linux/kmsg_dump.h>
81
82 #include <asm/uaccess.h>
83
84 @@ -1405,3 +1406,121 @@ bool printk_timed_ratelimit(unsigned lon
85 }
86 EXPORT_SYMBOL(printk_timed_ratelimit);
87 #endif
88 +
89 +static DEFINE_SPINLOCK(dump_list_lock);
90 +static LIST_HEAD(dump_list);
91 +
92 +/**
93 + * kmsg_dump_register - register a kernel log dumper.
94 + * @dump: pointer to the kmsg_dumper structure
95 + *
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.
99 + */
100 +int kmsg_dump_register(struct kmsg_dumper *dumper)
101 +{
102 + unsigned long flags;
103 + int err = -EBUSY;
104 +
105 + /* The dump callback needs to be set */
106 + if (!dumper->dump)
107 + return -EINVAL;
108 +
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);
114 + err = 0;
115 + }
116 + spin_unlock_irqrestore(&dump_list_lock, flags);
117 +
118 + return err;
119 +}
120 +EXPORT_SYMBOL_GPL(kmsg_dump_register);
121 +
122 +/**
123 + * kmsg_dump_unregister - unregister a kmsg dumper.
124 + * @dump: pointer to the kmsg_dumper structure
125 + *
126 + * Removes a dump device from the system. Returns zero on success and
127 + * %-EINVAL otherwise.
128 + */
129 +int kmsg_dump_unregister(struct kmsg_dumper *dumper)
130 +{
131 + unsigned long flags;
132 + int err = -EINVAL;
133 +
134 + spin_lock_irqsave(&dump_list_lock, flags);
135 + if (dumper->registered) {
136 + dumper->registered = 0;
137 + list_del(&dumper->list);
138 + err = 0;
139 + }
140 + spin_unlock_irqrestore(&dump_list_lock, flags);
141 +
142 + return err;
143 +}
144 +EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
145 +
146 +static const char const *kmsg_reasons[] = {
147 + [KMSG_DUMP_OOPS] = "oops",
148 + [KMSG_DUMP_PANIC] = "panic",
149 +};
150 +
151 +static const char *kmsg_to_str(enum kmsg_dump_reason reason)
152 +{
153 + if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
154 + return "unknown";
155 +
156 + return kmsg_reasons[reason];
157 +}
158 +
159 +/**
160 + * kmsg_dump - dump kernel log to kernel message dumpers.
161 + * @reason: the reason (oops, panic etc) for dumping
162 + *
163 + * Iterate through each of the dump devices and call the oops/panic
164 + * callbacks with the log buffer.
165 + */
166 +void kmsg_dump(enum kmsg_dump_reason reason)
167 +{
168 + unsigned long end;
169 + unsigned chars;
170 + struct kmsg_dumper *dumper;
171 + const char *s1, *s2;
172 + unsigned long l1, l2;
173 + unsigned long flags;
174 +
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);
182 +
183 + if (logged_chars > end) {
184 + s1 = log_buf + log_buf_len - logged_chars + end;
185 + l1 = logged_chars - end;
186 +
187 + s2 = log_buf;
188 + l2 = end;
189 + } else {
190 + s1 = "";
191 + l1 = 0;
192 +
193 + s2 = log_buf + end - logged_chars;
194 + l2 = logged_chars;
195 + }
196 +
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));
200 + return;
201 + }
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);
205 +}