diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/printk.c | 159 |
1 files changed, 121 insertions, 38 deletions
diff --git a/kernel/printk.c b/kernel/printk.c index ac4bc9e79465..50c96b5651b6 100644 --- a/kernel/printk.c +++ b/kernel/printk.c | |||
| @@ -216,6 +216,7 @@ struct log { | |||
| 216 | */ | 216 | */ |
| 217 | static DEFINE_RAW_SPINLOCK(logbuf_lock); | 217 | static DEFINE_RAW_SPINLOCK(logbuf_lock); |
| 218 | 218 | ||
| 219 | #ifdef CONFIG_PRINTK | ||
| 219 | /* the next printk record to read by syslog(READ) or /proc/kmsg */ | 220 | /* the next printk record to read by syslog(READ) or /proc/kmsg */ |
| 220 | static u64 syslog_seq; | 221 | static u64 syslog_seq; |
| 221 | static u32 syslog_idx; | 222 | static u32 syslog_idx; |
| @@ -228,14 +229,19 @@ static u32 log_first_idx; | |||
| 228 | 229 | ||
| 229 | /* index and sequence number of the next record to store in the buffer */ | 230 | /* index and sequence number of the next record to store in the buffer */ |
| 230 | static u64 log_next_seq; | 231 | static u64 log_next_seq; |
| 231 | #ifdef CONFIG_PRINTK | ||
| 232 | static u32 log_next_idx; | 232 | static u32 log_next_idx; |
| 233 | 233 | ||
| 234 | /* the next printk record to write to the console */ | ||
| 235 | static u64 console_seq; | ||
| 236 | static u32 console_idx; | ||
| 237 | static enum log_flags console_prev; | ||
| 238 | |||
| 234 | /* the next printk record to read after the last 'clear' command */ | 239 | /* the next printk record to read after the last 'clear' command */ |
| 235 | static u64 clear_seq; | 240 | static u64 clear_seq; |
| 236 | static u32 clear_idx; | 241 | static u32 clear_idx; |
| 237 | 242 | ||
| 238 | #define LOG_LINE_MAX 1024 | 243 | #define PREFIX_MAX 32 |
| 244 | #define LOG_LINE_MAX 1024 - PREFIX_MAX | ||
| 239 | 245 | ||
| 240 | /* record buffer */ | 246 | /* record buffer */ |
| 241 | #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) | 247 | #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) |
| @@ -360,6 +366,7 @@ static void log_store(int facility, int level, | |||
| 360 | struct devkmsg_user { | 366 | struct devkmsg_user { |
| 361 | u64 seq; | 367 | u64 seq; |
| 362 | u32 idx; | 368 | u32 idx; |
| 369 | enum log_flags prev; | ||
| 363 | struct mutex lock; | 370 | struct mutex lock; |
| 364 | char buf[8192]; | 371 | char buf[8192]; |
| 365 | }; | 372 | }; |
| @@ -425,6 +432,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, | |||
| 425 | struct log *msg; | 432 | struct log *msg; |
| 426 | u64 ts_usec; | 433 | u64 ts_usec; |
| 427 | size_t i; | 434 | size_t i; |
| 435 | char cont = '-'; | ||
| 428 | size_t len; | 436 | size_t len; |
| 429 | ssize_t ret; | 437 | ssize_t ret; |
| 430 | 438 | ||
| @@ -462,8 +470,25 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf, | |||
| 462 | msg = log_from_idx(user->idx); | 470 | msg = log_from_idx(user->idx); |
| 463 | ts_usec = msg->ts_nsec; | 471 | ts_usec = msg->ts_nsec; |
| 464 | do_div(ts_usec, 1000); | 472 | do_div(ts_usec, 1000); |
| 465 | len = sprintf(user->buf, "%u,%llu,%llu;", | 473 | |
| 466 | (msg->facility << 3) | msg->level, user->seq, ts_usec); | 474 | /* |
| 475 | * If we couldn't merge continuation line fragments during the print, | ||
| 476 | * export the stored flags to allow an optional external merge of the | ||
| 477 | * records. Merging the records isn't always neccessarily correct, like | ||
| 478 | * when we hit a race during printing. In most cases though, it produces | ||
| 479 | * better readable output. 'c' in the record flags mark the first | ||
| 480 | * fragment of a line, '+' the following. | ||
| 481 | */ | ||
| 482 | if (msg->flags & LOG_CONT && !(user->prev & LOG_CONT)) | ||
| 483 | cont = 'c'; | ||
| 484 | else if ((msg->flags & LOG_CONT) || | ||
| 485 | ((user->prev & LOG_CONT) && !(msg->flags & LOG_PREFIX))) | ||
| 486 | cont = '+'; | ||
| 487 | |||
| 488 | len = sprintf(user->buf, "%u,%llu,%llu,%c;", | ||
| 489 | (msg->facility << 3) | msg->level, | ||
| 490 | user->seq, ts_usec, cont); | ||
| 491 | user->prev = msg->flags; | ||
| 467 | 492 | ||
| 468 | /* escape non-printable characters */ | 493 | /* escape non-printable characters */ |
| 469 | for (i = 0; i < msg->text_len; i++) { | 494 | for (i = 0; i < msg->text_len; i++) { |
| @@ -646,6 +671,15 @@ void log_buf_kexec_setup(void) | |||
| 646 | VMCOREINFO_SYMBOL(log_buf_len); | 671 | VMCOREINFO_SYMBOL(log_buf_len); |
| 647 | VMCOREINFO_SYMBOL(log_first_idx); | 672 | VMCOREINFO_SYMBOL(log_first_idx); |
| 648 | VMCOREINFO_SYMBOL(log_next_idx); | 673 | VMCOREINFO_SYMBOL(log_next_idx); |
| 674 | /* | ||
| 675 | * Export struct log size and field offsets. User space tools can | ||
| 676 | * parse it and detect any changes to structure down the line. | ||
| 677 | */ | ||
| 678 | VMCOREINFO_STRUCT_SIZE(log); | ||
| 679 | VMCOREINFO_OFFSET(log, ts_nsec); | ||
| 680 | VMCOREINFO_OFFSET(log, len); | ||
| 681 | VMCOREINFO_OFFSET(log, text_len); | ||
| 682 | VMCOREINFO_OFFSET(log, dict_len); | ||
| 649 | } | 683 | } |
| 650 | #endif | 684 | #endif |
| 651 | 685 | ||
| @@ -876,7 +910,7 @@ static size_t msg_print_text(const struct log *msg, enum log_flags prev, | |||
| 876 | 910 | ||
| 877 | if (buf) { | 911 | if (buf) { |
| 878 | if (print_prefix(msg, syslog, NULL) + | 912 | if (print_prefix(msg, syslog, NULL) + |
| 879 | text_len + 1>= size - len) | 913 | text_len + 1 >= size - len) |
| 880 | break; | 914 | break; |
| 881 | 915 | ||
| 882 | if (prefix) | 916 | if (prefix) |
| @@ -907,7 +941,7 @@ static int syslog_print(char __user *buf, int size) | |||
| 907 | struct log *msg; | 941 | struct log *msg; |
| 908 | int len = 0; | 942 | int len = 0; |
| 909 | 943 | ||
| 910 | text = kmalloc(LOG_LINE_MAX, GFP_KERNEL); | 944 | text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL); |
| 911 | if (!text) | 945 | if (!text) |
| 912 | return -ENOMEM; | 946 | return -ENOMEM; |
| 913 | 947 | ||
| @@ -930,7 +964,8 @@ static int syslog_print(char __user *buf, int size) | |||
| 930 | 964 | ||
| 931 | skip = syslog_partial; | 965 | skip = syslog_partial; |
| 932 | msg = log_from_idx(syslog_idx); | 966 | msg = log_from_idx(syslog_idx); |
| 933 | n = msg_print_text(msg, syslog_prev, true, text, LOG_LINE_MAX); | 967 | n = msg_print_text(msg, syslog_prev, true, text, |
| 968 | LOG_LINE_MAX + PREFIX_MAX); | ||
| 934 | if (n - syslog_partial <= size) { | 969 | if (n - syslog_partial <= size) { |
| 935 | /* message fits into buffer, move forward */ | 970 | /* message fits into buffer, move forward */ |
| 936 | syslog_idx = log_next(syslog_idx); | 971 | syslog_idx = log_next(syslog_idx); |
| @@ -969,7 +1004,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) | |||
| 969 | char *text; | 1004 | char *text; |
| 970 | int len = 0; | 1005 | int len = 0; |
| 971 | 1006 | ||
| 972 | text = kmalloc(LOG_LINE_MAX, GFP_KERNEL); | 1007 | text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL); |
| 973 | if (!text) | 1008 | if (!text) |
| 974 | return -ENOMEM; | 1009 | return -ENOMEM; |
| 975 | 1010 | ||
| @@ -1022,7 +1057,8 @@ static int syslog_print_all(char __user *buf, int size, bool clear) | |||
| 1022 | struct log *msg = log_from_idx(idx); | 1057 | struct log *msg = log_from_idx(idx); |
| 1023 | int textlen; | 1058 | int textlen; |
| 1024 | 1059 | ||
| 1025 | textlen = msg_print_text(msg, prev, true, text, LOG_LINE_MAX); | 1060 | textlen = msg_print_text(msg, prev, true, text, |
| 1061 | LOG_LINE_MAX + PREFIX_MAX); | ||
| 1026 | if (textlen < 0) { | 1062 | if (textlen < 0) { |
| 1027 | len = textlen; | 1063 | len = textlen; |
| 1028 | break; | 1064 | break; |
| @@ -1349,20 +1385,36 @@ static struct cont { | |||
| 1349 | u64 ts_nsec; /* time of first print */ | 1385 | u64 ts_nsec; /* time of first print */ |
| 1350 | u8 level; /* log level of first message */ | 1386 | u8 level; /* log level of first message */ |
| 1351 | u8 facility; /* log level of first message */ | 1387 | u8 facility; /* log level of first message */ |
| 1388 | enum log_flags flags; /* prefix, newline flags */ | ||
| 1352 | bool flushed:1; /* buffer sealed and committed */ | 1389 | bool flushed:1; /* buffer sealed and committed */ |
| 1353 | } cont; | 1390 | } cont; |
| 1354 | 1391 | ||
| 1355 | static void cont_flush(void) | 1392 | static void cont_flush(enum log_flags flags) |
| 1356 | { | 1393 | { |
| 1357 | if (cont.flushed) | 1394 | if (cont.flushed) |
| 1358 | return; | 1395 | return; |
| 1359 | if (cont.len == 0) | 1396 | if (cont.len == 0) |
| 1360 | return; | 1397 | return; |
| 1361 | 1398 | ||
| 1362 | log_store(cont.facility, cont.level, LOG_NOCONS, cont.ts_nsec, | 1399 | if (cont.cons) { |
| 1363 | NULL, 0, cont.buf, cont.len); | 1400 | /* |
| 1364 | 1401 | * If a fragment of this line was directly flushed to the | |
| 1365 | cont.flushed = true; | 1402 | * console; wait for the console to pick up the rest of the |
| 1403 | * line. LOG_NOCONS suppresses a duplicated output. | ||
| 1404 | */ | ||
| 1405 | log_store(cont.facility, cont.level, flags | LOG_NOCONS, | ||
| 1406 | cont.ts_nsec, NULL, 0, cont.buf, cont.len); | ||
| 1407 | cont.flags = flags; | ||
| 1408 | cont.flushed = true; | ||
| 1409 | } else { | ||
| 1410 | /* | ||
| 1411 | * If no fragment of this line ever reached the console, | ||
| 1412 | * just submit it to the store and free the buffer. | ||
| 1413 | */ | ||
| 1414 | log_store(cont.facility, cont.level, flags, 0, | ||
| 1415 | NULL, 0, cont.buf, cont.len); | ||
| 1416 | cont.len = 0; | ||
| 1417 | } | ||
| 1366 | } | 1418 | } |
| 1367 | 1419 | ||
| 1368 | static bool cont_add(int facility, int level, const char *text, size_t len) | 1420 | static bool cont_add(int facility, int level, const char *text, size_t len) |
| @@ -1371,7 +1423,8 @@ static bool cont_add(int facility, int level, const char *text, size_t len) | |||
| 1371 | return false; | 1423 | return false; |
| 1372 | 1424 | ||
| 1373 | if (cont.len + len > sizeof(cont.buf)) { | 1425 | if (cont.len + len > sizeof(cont.buf)) { |
| 1374 | cont_flush(); | 1426 | /* the line gets too long, split it up in separate records */ |
| 1427 | cont_flush(LOG_CONT); | ||
| 1375 | return false; | 1428 | return false; |
| 1376 | } | 1429 | } |
| 1377 | 1430 | ||
| @@ -1380,12 +1433,17 @@ static bool cont_add(int facility, int level, const char *text, size_t len) | |||
| 1380 | cont.level = level; | 1433 | cont.level = level; |
| 1381 | cont.owner = current; | 1434 | cont.owner = current; |
| 1382 | cont.ts_nsec = local_clock(); | 1435 | cont.ts_nsec = local_clock(); |
| 1436 | cont.flags = 0; | ||
| 1383 | cont.cons = 0; | 1437 | cont.cons = 0; |
| 1384 | cont.flushed = false; | 1438 | cont.flushed = false; |
| 1385 | } | 1439 | } |
| 1386 | 1440 | ||
| 1387 | memcpy(cont.buf + cont.len, text, len); | 1441 | memcpy(cont.buf + cont.len, text, len); |
| 1388 | cont.len += len; | 1442 | cont.len += len; |
| 1443 | |||
| 1444 | if (cont.len > (sizeof(cont.buf) * 80) / 100) | ||
| 1445 | cont_flush(LOG_CONT); | ||
| 1446 | |||
| 1389 | return true; | 1447 | return true; |
| 1390 | } | 1448 | } |
| 1391 | 1449 | ||
| @@ -1394,7 +1452,7 @@ static size_t cont_print_text(char *text, size_t size) | |||
| 1394 | size_t textlen = 0; | 1452 | size_t textlen = 0; |
| 1395 | size_t len; | 1453 | size_t len; |
| 1396 | 1454 | ||
| 1397 | if (cont.cons == 0) { | 1455 | if (cont.cons == 0 && (console_prev & LOG_NEWLINE)) { |
| 1398 | textlen += print_time(cont.ts_nsec, text); | 1456 | textlen += print_time(cont.ts_nsec, text); |
| 1399 | size -= textlen; | 1457 | size -= textlen; |
| 1400 | } | 1458 | } |
| @@ -1409,7 +1467,8 @@ static size_t cont_print_text(char *text, size_t size) | |||
| 1409 | } | 1467 | } |
| 1410 | 1468 | ||
| 1411 | if (cont.flushed) { | 1469 | if (cont.flushed) { |
| 1412 | text[textlen++] = '\n'; | 1470 | if (cont.flags & LOG_NEWLINE) |
| 1471 | text[textlen++] = '\n'; | ||
| 1413 | /* got everything, release buffer */ | 1472 | /* got everything, release buffer */ |
| 1414 | cont.len = 0; | 1473 | cont.len = 0; |
| 1415 | } | 1474 | } |
| @@ -1507,7 +1566,7 @@ asmlinkage int vprintk_emit(int facility, int level, | |||
| 1507 | * or another task also prints continuation lines. | 1566 | * or another task also prints continuation lines. |
| 1508 | */ | 1567 | */ |
| 1509 | if (cont.len && (lflags & LOG_PREFIX || cont.owner != current)) | 1568 | if (cont.len && (lflags & LOG_PREFIX || cont.owner != current)) |
| 1510 | cont_flush(); | 1569 | cont_flush(LOG_NEWLINE); |
| 1511 | 1570 | ||
| 1512 | /* buffer line if possible, otherwise store it right away */ | 1571 | /* buffer line if possible, otherwise store it right away */ |
| 1513 | if (!cont_add(facility, level, text, text_len)) | 1572 | if (!cont_add(facility, level, text, text_len)) |
| @@ -1525,7 +1584,7 @@ asmlinkage int vprintk_emit(int facility, int level, | |||
| 1525 | if (cont.len && cont.owner == current) { | 1584 | if (cont.len && cont.owner == current) { |
| 1526 | if (!(lflags & LOG_PREFIX)) | 1585 | if (!(lflags & LOG_PREFIX)) |
| 1527 | stored = cont_add(facility, level, text, text_len); | 1586 | stored = cont_add(facility, level, text, text_len); |
| 1528 | cont_flush(); | 1587 | cont_flush(LOG_NEWLINE); |
| 1529 | } | 1588 | } |
| 1530 | 1589 | ||
| 1531 | if (!stored) | 1590 | if (!stored) |
| @@ -1616,9 +1675,20 @@ asmlinkage int printk(const char *fmt, ...) | |||
| 1616 | } | 1675 | } |
| 1617 | EXPORT_SYMBOL(printk); | 1676 | EXPORT_SYMBOL(printk); |
| 1618 | 1677 | ||
| 1619 | #else | 1678 | #else /* CONFIG_PRINTK */ |
| 1620 | 1679 | ||
| 1680 | #define LOG_LINE_MAX 0 | ||
| 1681 | #define PREFIX_MAX 0 | ||
| 1621 | #define LOG_LINE_MAX 0 | 1682 | #define LOG_LINE_MAX 0 |
| 1683 | static u64 syslog_seq; | ||
| 1684 | static u32 syslog_idx; | ||
| 1685 | static u64 console_seq; | ||
| 1686 | static u32 console_idx; | ||
| 1687 | static enum log_flags syslog_prev; | ||
| 1688 | static u64 log_first_seq; | ||
| 1689 | static u32 log_first_idx; | ||
| 1690 | static u64 log_next_seq; | ||
| 1691 | static enum log_flags console_prev; | ||
| 1622 | static struct cont { | 1692 | static struct cont { |
| 1623 | size_t len; | 1693 | size_t len; |
| 1624 | size_t cons; | 1694 | size_t cons; |
| @@ -1902,10 +1972,34 @@ void wake_up_klogd(void) | |||
| 1902 | this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP); | 1972 | this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP); |
| 1903 | } | 1973 | } |
| 1904 | 1974 | ||
| 1905 | /* the next printk record to write to the console */ | 1975 | static void console_cont_flush(char *text, size_t size) |
| 1906 | static u64 console_seq; | 1976 | { |
| 1907 | static u32 console_idx; | 1977 | unsigned long flags; |
| 1908 | static enum log_flags console_prev; | 1978 | size_t len; |
| 1979 | |||
| 1980 | raw_spin_lock_irqsave(&logbuf_lock, flags); | ||
| 1981 | |||
| 1982 | if (!cont.len) | ||
| 1983 | goto out; | ||
| 1984 | |||
| 1985 | /* | ||
| 1986 | * We still queue earlier records, likely because the console was | ||
| 1987 | * busy. The earlier ones need to be printed before this one, we | ||
| 1988 | * did not flush any fragment so far, so just let it queue up. | ||
| 1989 | */ | ||
| 1990 | if (console_seq < log_next_seq && !cont.cons) | ||
| 1991 | goto out; | ||
| 1992 | |||
| 1993 | len = cont_print_text(text, size); | ||
| 1994 | raw_spin_unlock(&logbuf_lock); | ||
| 1995 | stop_critical_timings(); | ||
| 1996 | call_console_drivers(cont.level, text, len); | ||
| 1997 | start_critical_timings(); | ||
| 1998 | local_irq_restore(flags); | ||
| 1999 | return; | ||
| 2000 | out: | ||
| 2001 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); | ||
| 2002 | } | ||
| 1909 | 2003 | ||
| 1910 | /** | 2004 | /** |
| 1911 | * console_unlock - unlock the console system | 2005 | * console_unlock - unlock the console system |
| @@ -1923,7 +2017,7 @@ static enum log_flags console_prev; | |||
| 1923 | */ | 2017 | */ |
| 1924 | void console_unlock(void) | 2018 | void console_unlock(void) |
| 1925 | { | 2019 | { |
| 1926 | static char text[LOG_LINE_MAX]; | 2020 | static char text[LOG_LINE_MAX + PREFIX_MAX]; |
| 1927 | static u64 seen_seq; | 2021 | static u64 seen_seq; |
| 1928 | unsigned long flags; | 2022 | unsigned long flags; |
| 1929 | bool wake_klogd = false; | 2023 | bool wake_klogd = false; |
| @@ -1937,19 +2031,7 @@ void console_unlock(void) | |||
| 1937 | console_may_schedule = 0; | 2031 | console_may_schedule = 0; |
| 1938 | 2032 | ||
| 1939 | /* flush buffered message fragment immediately to console */ | 2033 | /* flush buffered message fragment immediately to console */ |
| 1940 | raw_spin_lock_irqsave(&logbuf_lock, flags); | 2034 | console_cont_flush(text, sizeof(text)); |
| 1941 | if (cont.len && (cont.cons < cont.len || cont.flushed)) { | ||
| 1942 | size_t len; | ||
| 1943 | |||
| 1944 | len = cont_print_text(text, sizeof(text)); | ||
| 1945 | raw_spin_unlock(&logbuf_lock); | ||
| 1946 | stop_critical_timings(); | ||
| 1947 | call_console_drivers(cont.level, text, len); | ||
| 1948 | start_critical_timings(); | ||
| 1949 | local_irq_restore(flags); | ||
| 1950 | } else | ||
| 1951 | raw_spin_unlock_irqrestore(&logbuf_lock, flags); | ||
| 1952 | |||
| 1953 | again: | 2035 | again: |
| 1954 | for (;;) { | 2036 | for (;;) { |
| 1955 | struct log *msg; | 2037 | struct log *msg; |
| @@ -1986,6 +2068,7 @@ skip: | |||
| 1986 | * will properly dump everything later. | 2068 | * will properly dump everything later. |
| 1987 | */ | 2069 | */ |
| 1988 | msg->flags &= ~LOG_NOCONS; | 2070 | msg->flags &= ~LOG_NOCONS; |
| 2071 | console_prev = msg->flags; | ||
| 1989 | goto skip; | 2072 | goto skip; |
| 1990 | } | 2073 | } |
| 1991 | 2074 | ||
