diff options
Diffstat (limited to 'kernel/printk.c')
| -rw-r--r-- | kernel/printk.c | 126 |
1 files changed, 123 insertions, 3 deletions
diff --git a/kernel/printk.c b/kernel/printk.c index f38b07f78a4e..17463ca2e229 100644 --- a/kernel/printk.c +++ b/kernel/printk.c | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | #include <linux/bootmem.h> | 33 | #include <linux/bootmem.h> |
| 34 | #include <linux/syscalls.h> | 34 | #include <linux/syscalls.h> |
| 35 | #include <linux/kexec.h> | 35 | #include <linux/kexec.h> |
| 36 | #include <linux/ratelimit.h> | ||
| 37 | #include <linux/kmsg_dump.h> | ||
| 36 | 38 | ||
| 37 | #include <asm/uaccess.h> | 39 | #include <asm/uaccess.h> |
| 38 | 40 | ||
| @@ -1376,11 +1378,11 @@ late_initcall(disable_boot_consoles); | |||
| 1376 | */ | 1378 | */ |
| 1377 | DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10); | 1379 | DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10); |
| 1378 | 1380 | ||
| 1379 | int printk_ratelimit(void) | 1381 | int __printk_ratelimit(const char *func) |
| 1380 | { | 1382 | { |
| 1381 | return __ratelimit(&printk_ratelimit_state); | 1383 | return ___ratelimit(&printk_ratelimit_state, func); |
| 1382 | } | 1384 | } |
| 1383 | EXPORT_SYMBOL(printk_ratelimit); | 1385 | EXPORT_SYMBOL(__printk_ratelimit); |
| 1384 | 1386 | ||
| 1385 | /** | 1387 | /** |
| 1386 | * printk_timed_ratelimit - caller-controlled printk ratelimiting | 1388 | * printk_timed_ratelimit - caller-controlled printk ratelimiting |
| @@ -1404,4 +1406,122 @@ bool printk_timed_ratelimit(unsigned long *caller_jiffies, | |||
| 1404 | return false; | 1406 | return false; |
| 1405 | } | 1407 | } |
| 1406 | EXPORT_SYMBOL(printk_timed_ratelimit); | 1408 | EXPORT_SYMBOL(printk_timed_ratelimit); |
| 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 | * @dumper: 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 | * @dumper: 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 | } | ||
| 1407 | #endif | 1527 | #endif |
