Commit 456b565cc52fbcdaa2e19ffdf40d9dd3b726d603

Authored by Simon Kagstrom
Committed by David Woodhouse
1 parent 7cb777a3d7

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 */
... ... @@ -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
... ... @@ -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 +}