target/linux: refresh kernel patches
[oweals/openwrt.git] / target / linux / generic / 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 +}