Commit 456b565cc52fbcdaa2e19ffdf40d9dd3b726d603
Committed by
David Woodhouse
1 parent
7cb777a3d7
Exists in
master
and in
7 other branches
core: Add kernel message dumper to call on oopses and panics
The core functionality is implemented as per Linus suggestion from http://lists.infradead.org/pipermail/linux-mtd/2009-October/027620.html (with the kmsg_dump implementation by Linus). A struct kmsg_dumper has been added which contains a callback to dump the kernel log buffers on crashes. The kmsg_dump function gets called from oops_exit() and panic() and invokes this callbacks with the crash reason. [dwmw2: Fix log_end handling] Signed-off-by: Simon Kagstrom <simon.kagstrom@netinsight.net> Reviewed-by: Anders Grafstrom <anders.grafstrom@netinsight.net> Reviewed-by: Linus Torvalds <torvalds@linux-foundation.org> Acked-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Showing 3 changed files with 166 additions and 0 deletions Side-by-side Diff
include/linux/kmsg_dump.h
1 | +/* | |
2 | + * linux/include/kmsg_dump.h | |
3 | + * | |
4 | + * Copyright (C) 2009 Net Insight AB | |
5 | + * | |
6 | + * Author: Simon Kagstrom <simon.kagstrom@netinsight.net> | |
7 | + * | |
8 | + * This file is subject to the terms and conditions of the GNU General Public | |
9 | + * License. See the file COPYING in the main directory of this archive | |
10 | + * for more details. | |
11 | + */ | |
12 | +#ifndef _LINUX_KMSG_DUMP_H | |
13 | +#define _LINUX_KMSG_DUMP_H | |
14 | + | |
15 | +#include <linux/list.h> | |
16 | + | |
17 | +enum kmsg_dump_reason { | |
18 | + KMSG_DUMP_OOPS, | |
19 | + KMSG_DUMP_PANIC, | |
20 | +}; | |
21 | + | |
22 | +/** | |
23 | + * struct kmsg_dumper - kernel crash message dumper structure | |
24 | + * @dump: The callback which gets called on crashes. The buffer is passed | |
25 | + * as two sections, where s1 (length l1) contains the older | |
26 | + * messages and s2 (length l2) contains the newer. | |
27 | + * @list: Entry in the dumper list (private) | |
28 | + * @registered: Flag that specifies if this is already registered | |
29 | + */ | |
30 | +struct kmsg_dumper { | |
31 | + void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason, | |
32 | + const char *s1, unsigned long l1, | |
33 | + const char *s2, unsigned long l2); | |
34 | + struct list_head list; | |
35 | + int registered; | |
36 | +}; | |
37 | + | |
38 | +void kmsg_dump(enum kmsg_dump_reason reason); | |
39 | + | |
40 | +int kmsg_dump_register(struct kmsg_dumper *dumper); | |
41 | + | |
42 | +int kmsg_dump_unregister(struct kmsg_dumper *dumper); | |
43 | + | |
44 | +#endif /* _LINUX_KMSG_DUMP_H */ |
kernel/panic.c
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 | */ |
11 | 11 | #include <linux/debug_locks.h> |
12 | 12 | #include <linux/interrupt.h> |
13 | +#include <linux/kmsg_dump.h> | |
13 | 14 | #include <linux/kallsyms.h> |
14 | 15 | #include <linux/notifier.h> |
15 | 16 | #include <linux/module.h> |
... | ... | @@ -74,6 +75,7 @@ |
74 | 75 | dump_stack(); |
75 | 76 | #endif |
76 | 77 | |
78 | + kmsg_dump(KMSG_DUMP_PANIC); | |
77 | 79 | /* |
78 | 80 | * If we have crashed and we have a crash kernel loaded let it handle |
79 | 81 | * everything else. |
... | ... | @@ -338,6 +340,7 @@ |
338 | 340 | { |
339 | 341 | do_oops_enter_exit(); |
340 | 342 | print_oops_end_marker(); |
343 | + kmsg_dump(KMSG_DUMP_OOPS); | |
341 | 344 | } |
342 | 345 | |
343 | 346 | #ifdef WANT_WARN_ON_SLOWPATH |
kernel/printk.c
... | ... | @@ -33,6 +33,7 @@ |
33 | 33 | #include <linux/bootmem.h> |
34 | 34 | #include <linux/syscalls.h> |
35 | 35 | #include <linux/kexec.h> |
36 | +#include <linux/kmsg_dump.h> | |
36 | 37 | |
37 | 38 | #include <asm/uaccess.h> |
38 | 39 | |
... | ... | @@ -1405,4 +1406,122 @@ |
1405 | 1406 | } |
1406 | 1407 | EXPORT_SYMBOL(printk_timed_ratelimit); |
1407 | 1408 | #endif |
1409 | + | |
1410 | +static DEFINE_SPINLOCK(dump_list_lock); | |
1411 | +static LIST_HEAD(dump_list); | |
1412 | + | |
1413 | +/** | |
1414 | + * kmsg_dump_register - register a kernel log dumper. | |
1415 | + * @dump: pointer to the kmsg_dumper structure | |
1416 | + * | |
1417 | + * Adds a kernel log dumper to the system. The dump callback in the | |
1418 | + * structure will be called when the kernel oopses or panics and must be | |
1419 | + * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise. | |
1420 | + */ | |
1421 | +int kmsg_dump_register(struct kmsg_dumper *dumper) | |
1422 | +{ | |
1423 | + unsigned long flags; | |
1424 | + int err = -EBUSY; | |
1425 | + | |
1426 | + /* The dump callback needs to be set */ | |
1427 | + if (!dumper->dump) | |
1428 | + return -EINVAL; | |
1429 | + | |
1430 | + spin_lock_irqsave(&dump_list_lock, flags); | |
1431 | + /* Don't allow registering multiple times */ | |
1432 | + if (!dumper->registered) { | |
1433 | + dumper->registered = 1; | |
1434 | + list_add_tail(&dumper->list, &dump_list); | |
1435 | + err = 0; | |
1436 | + } | |
1437 | + spin_unlock_irqrestore(&dump_list_lock, flags); | |
1438 | + | |
1439 | + return err; | |
1440 | +} | |
1441 | +EXPORT_SYMBOL_GPL(kmsg_dump_register); | |
1442 | + | |
1443 | +/** | |
1444 | + * kmsg_dump_unregister - unregister a kmsg dumper. | |
1445 | + * @dump: pointer to the kmsg_dumper structure | |
1446 | + * | |
1447 | + * Removes a dump device from the system. Returns zero on success and | |
1448 | + * %-EINVAL otherwise. | |
1449 | + */ | |
1450 | +int kmsg_dump_unregister(struct kmsg_dumper *dumper) | |
1451 | +{ | |
1452 | + unsigned long flags; | |
1453 | + int err = -EINVAL; | |
1454 | + | |
1455 | + spin_lock_irqsave(&dump_list_lock, flags); | |
1456 | + if (dumper->registered) { | |
1457 | + dumper->registered = 0; | |
1458 | + list_del(&dumper->list); | |
1459 | + err = 0; | |
1460 | + } | |
1461 | + spin_unlock_irqrestore(&dump_list_lock, flags); | |
1462 | + | |
1463 | + return err; | |
1464 | +} | |
1465 | +EXPORT_SYMBOL_GPL(kmsg_dump_unregister); | |
1466 | + | |
1467 | +static const char const *kmsg_reasons[] = { | |
1468 | + [KMSG_DUMP_OOPS] = "oops", | |
1469 | + [KMSG_DUMP_PANIC] = "panic", | |
1470 | +}; | |
1471 | + | |
1472 | +static const char *kmsg_to_str(enum kmsg_dump_reason reason) | |
1473 | +{ | |
1474 | + if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0) | |
1475 | + return "unknown"; | |
1476 | + | |
1477 | + return kmsg_reasons[reason]; | |
1478 | +} | |
1479 | + | |
1480 | +/** | |
1481 | + * kmsg_dump - dump kernel log to kernel message dumpers. | |
1482 | + * @reason: the reason (oops, panic etc) for dumping | |
1483 | + * | |
1484 | + * Iterate through each of the dump devices and call the oops/panic | |
1485 | + * callbacks with the log buffer. | |
1486 | + */ | |
1487 | +void kmsg_dump(enum kmsg_dump_reason reason) | |
1488 | +{ | |
1489 | + unsigned long end; | |
1490 | + unsigned chars; | |
1491 | + struct kmsg_dumper *dumper; | |
1492 | + const char *s1, *s2; | |
1493 | + unsigned long l1, l2; | |
1494 | + unsigned long flags; | |
1495 | + | |
1496 | + /* Theoretically, the log could move on after we do this, but | |
1497 | + there's not a lot we can do about that. The new messages | |
1498 | + will overwrite the start of what we dump. */ | |
1499 | + spin_lock_irqsave(&logbuf_lock, flags); | |
1500 | + end = log_end & LOG_BUF_MASK; | |
1501 | + chars = logged_chars; | |
1502 | + spin_unlock_irqrestore(&logbuf_lock, flags); | |
1503 | + | |
1504 | + if (logged_chars > end) { | |
1505 | + s1 = log_buf + log_buf_len - logged_chars + end; | |
1506 | + l1 = logged_chars - end; | |
1507 | + | |
1508 | + s2 = log_buf; | |
1509 | + l2 = end; | |
1510 | + } else { | |
1511 | + s1 = ""; | |
1512 | + l1 = 0; | |
1513 | + | |
1514 | + s2 = log_buf + end - logged_chars; | |
1515 | + l2 = logged_chars; | |
1516 | + } | |
1517 | + | |
1518 | + if (!spin_trylock_irqsave(&dump_list_lock, flags)) { | |
1519 | + printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n", | |
1520 | + kmsg_to_str(reason)); | |
1521 | + return; | |
1522 | + } | |
1523 | + list_for_each_entry(dumper, &dump_list, list) | |
1524 | + dumper->dump(dumper, reason, s1, l1, s2, l2); | |
1525 | + spin_unlock_irqrestore(&dump_list_lock, flags); | |
1526 | +} |