aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/printk
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2015-06-25 18:01:30 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-25 20:00:38 -0400
commit6fe29354befe4c46eb308b662155d4d8017358e1 (patch)
tree8bdee457952ed38e3a3495721e7c1cf37b8b7f65 /kernel/printk
parent0a295e67ec19d59bdb146e0b60ac9659104f2215 (diff)
printk: implement support for extended console drivers
printk log_buf keeps various metadata for each message including its sequence number and timestamp. The metadata is currently available only through /dev/kmsg and stripped out before passed onto console drivers. We want this metadata to be available to console drivers too so that console consumers can get full information including the metadata and dictionary, which among other things can be used to detect whether messages got lost in transit. This patch implements support for extended console drivers. Consoles can indicate that they want extended messages by setting the new CON_EXTENDED flag and they'll be fed messages formatted the same way as /dev/kmsg. "<level>,<sequnum>,<timestamp>,<contflag>;<message text>\n" If extended consoles exist, in-kernel fragment assembly is disabled. This ensures that all messages emitted to consoles have full metadata including sequence number. The contflag carries enough information to reassemble the fragments from the reader side trivially. Note that this only affects /dev/kmsg. Regular console and /proc/kmsg outputs are not affected by this change. * Extended message formatting for console drivers is enabled iff there are registered extended consoles. * Comment describing /dev/kmsg message format updated to add missing contflag field and help distinguishing variable from verbatim terms. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: David Miller <davem@davemloft.net> Cc: Kay Sievers <kay@vrfy.org> Reviewed-by: Petr Mladek <pmladek@suse.cz> Cc: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/printk')
-rw-r--r--kernel/printk/printk.c66
1 files changed, 58 insertions, 8 deletions
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 51ce4f1838b3..ae980dc3ac1e 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -85,6 +85,18 @@ static struct lockdep_map console_lock_dep_map = {
85#endif 85#endif
86 86
87/* 87/*
88 * Number of registered extended console drivers.
89 *
90 * If extended consoles are present, in-kernel cont reassembly is disabled
91 * and each fragment is stored as a separate log entry with proper
92 * continuation flag so that every emitted message has full metadata. This
93 * doesn't change the result for regular consoles or /proc/kmsg. For
94 * /dev/kmsg, as long as the reader concatenates messages according to
95 * consecutive continuation flags, the end result should be the same too.
96 */
97static int nr_ext_console_drivers;
98
99/*
88 * Helper macros to handle lockdep when locking/unlocking console_sem. We use 100 * Helper macros to handle lockdep when locking/unlocking console_sem. We use
89 * macros instead of functions so that _RET_IP_ contains useful information. 101 * macros instead of functions so that _RET_IP_ contains useful information.
90 */ 102 */
@@ -195,7 +207,7 @@ static int console_may_schedule;
195 * need to be changed in the future, when the requirements change. 207 * need to be changed in the future, when the requirements change.
196 * 208 *
197 * /dev/kmsg exports the structured data in the following line format: 209 * /dev/kmsg exports the structured data in the following line format:
198 * "level,sequnum,timestamp;<message text>\n" 210 * "<level>,<sequnum>,<timestamp>,<contflag>;<message text>\n"
199 * 211 *
200 * The optional key/value pairs are attached as continuation lines starting 212 * The optional key/value pairs are attached as continuation lines starting
201 * with a space character and terminated by a newline. All possible 213 * with a space character and terminated by a newline. All possible
@@ -1417,7 +1429,9 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
1417 * log_buf[start] to log_buf[end - 1]. 1429 * log_buf[start] to log_buf[end - 1].
1418 * The console_lock must be held. 1430 * The console_lock must be held.
1419 */ 1431 */
1420static void call_console_drivers(int level, const char *text, size_t len) 1432static void call_console_drivers(int level,
1433 const char *ext_text, size_t ext_len,
1434 const char *text, size_t len)
1421{ 1435{
1422 struct console *con; 1436 struct console *con;
1423 1437
@@ -1438,7 +1452,10 @@ static void call_console_drivers(int level, const char *text, size_t len)
1438 if (!cpu_online(smp_processor_id()) && 1452 if (!cpu_online(smp_processor_id()) &&
1439 !(con->flags & CON_ANYTIME)) 1453 !(con->flags & CON_ANYTIME))
1440 continue; 1454 continue;
1441 con->write(con, text, len); 1455 if (con->flags & CON_EXTENDED)
1456 con->write(con, ext_text, ext_len);
1457 else
1458 con->write(con, text, len);
1442 } 1459 }
1443} 1460}
1444 1461
@@ -1581,8 +1598,12 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
1581 if (cont.len && cont.flushed) 1598 if (cont.len && cont.flushed)
1582 return false; 1599 return false;
1583 1600
1584 if (cont.len + len > sizeof(cont.buf)) { 1601 /*
1585 /* the line gets too long, split it up in separate records */ 1602 * If ext consoles are present, flush and skip in-kernel
1603 * continuation. See nr_ext_console_drivers definition. Also, if
1604 * the line gets too long, split it up in separate records.
1605 */
1606 if (nr_ext_console_drivers || cont.len + len > sizeof(cont.buf)) {
1586 cont_flush(LOG_CONT); 1607 cont_flush(LOG_CONT);
1587 return false; 1608 return false;
1588 } 1609 }
@@ -1917,9 +1938,19 @@ static struct cont {
1917 u8 level; 1938 u8 level;
1918 bool flushed:1; 1939 bool flushed:1;
1919} cont; 1940} cont;
1941static char *log_text(const struct printk_log *msg) { return NULL; }
1942static char *log_dict(const struct printk_log *msg) { return NULL; }
1920static struct printk_log *log_from_idx(u32 idx) { return NULL; } 1943static struct printk_log *log_from_idx(u32 idx) { return NULL; }
1921static u32 log_next(u32 idx) { return 0; } 1944static u32 log_next(u32 idx) { return 0; }
1922static void call_console_drivers(int level, const char *text, size_t len) {} 1945static ssize_t msg_print_ext_header(char *buf, size_t size,
1946 struct printk_log *msg, u64 seq,
1947 enum log_flags prev_flags) { return 0; }
1948static ssize_t msg_print_ext_body(char *buf, size_t size,
1949 char *dict, size_t dict_len,
1950 char *text, size_t text_len) { return 0; }
1951static void call_console_drivers(int level,
1952 const char *ext_text, size_t ext_len,
1953 const char *text, size_t len) {}
1923static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev, 1954static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev,
1924 bool syslog, char *buf, size_t size) { return 0; } 1955 bool syslog, char *buf, size_t size) { return 0; }
1925static size_t cont_print_text(char *text, size_t size) { return 0; } 1956static size_t cont_print_text(char *text, size_t size) { return 0; }
@@ -2172,7 +2203,7 @@ static void console_cont_flush(char *text, size_t size)
2172 len = cont_print_text(text, size); 2203 len = cont_print_text(text, size);
2173 raw_spin_unlock(&logbuf_lock); 2204 raw_spin_unlock(&logbuf_lock);
2174 stop_critical_timings(); 2205 stop_critical_timings();
2175 call_console_drivers(cont.level, text, len); 2206 call_console_drivers(cont.level, NULL, 0, text, len);
2176 start_critical_timings(); 2207 start_critical_timings();
2177 local_irq_restore(flags); 2208 local_irq_restore(flags);
2178 return; 2209 return;
@@ -2196,6 +2227,7 @@ out:
2196 */ 2227 */
2197void console_unlock(void) 2228void console_unlock(void)
2198{ 2229{
2230 static char ext_text[CONSOLE_EXT_LOG_MAX];
2199 static char text[LOG_LINE_MAX + PREFIX_MAX]; 2231 static char text[LOG_LINE_MAX + PREFIX_MAX];
2200 static u64 seen_seq; 2232 static u64 seen_seq;
2201 unsigned long flags; 2233 unsigned long flags;
@@ -2214,6 +2246,7 @@ void console_unlock(void)
2214again: 2246again:
2215 for (;;) { 2247 for (;;) {
2216 struct printk_log *msg; 2248 struct printk_log *msg;
2249 size_t ext_len = 0;
2217 size_t len; 2250 size_t len;
2218 int level; 2251 int level;
2219 2252
@@ -2259,13 +2292,22 @@ skip:
2259 level = msg->level; 2292 level = msg->level;
2260 len += msg_print_text(msg, console_prev, false, 2293 len += msg_print_text(msg, console_prev, false,
2261 text + len, sizeof(text) - len); 2294 text + len, sizeof(text) - len);
2295 if (nr_ext_console_drivers) {
2296 ext_len = msg_print_ext_header(ext_text,
2297 sizeof(ext_text),
2298 msg, console_seq, console_prev);
2299 ext_len += msg_print_ext_body(ext_text + ext_len,
2300 sizeof(ext_text) - ext_len,
2301 log_dict(msg), msg->dict_len,
2302 log_text(msg), msg->text_len);
2303 }
2262 console_idx = log_next(console_idx); 2304 console_idx = log_next(console_idx);
2263 console_seq++; 2305 console_seq++;
2264 console_prev = msg->flags; 2306 console_prev = msg->flags;
2265 raw_spin_unlock(&logbuf_lock); 2307 raw_spin_unlock(&logbuf_lock);
2266 2308
2267 stop_critical_timings(); /* don't trace print latency */ 2309 stop_critical_timings(); /* don't trace print latency */
2268 call_console_drivers(level, text, len); 2310 call_console_drivers(level, ext_text, ext_len, text, len);
2269 start_critical_timings(); 2311 start_critical_timings();
2270 local_irq_restore(flags); 2312 local_irq_restore(flags);
2271 } 2313 }
@@ -2521,6 +2563,11 @@ void register_console(struct console *newcon)
2521 newcon->next = console_drivers->next; 2563 newcon->next = console_drivers->next;
2522 console_drivers->next = newcon; 2564 console_drivers->next = newcon;
2523 } 2565 }
2566
2567 if (newcon->flags & CON_EXTENDED)
2568 if (!nr_ext_console_drivers++)
2569 pr_info("printk: continuation disabled due to ext consoles, expect more fragments in /dev/kmsg\n");
2570
2524 if (newcon->flags & CON_PRINTBUFFER) { 2571 if (newcon->flags & CON_PRINTBUFFER) {
2525 /* 2572 /*
2526 * console_unlock(); will print out the buffered messages 2573 * console_unlock(); will print out the buffered messages
@@ -2593,6 +2640,9 @@ int unregister_console(struct console *console)
2593 } 2640 }
2594 } 2641 }
2595 2642
2643 if (!res && (console->flags & CON_EXTENDED))
2644 nr_ext_console_drivers--;
2645
2596 /* 2646 /*
2597 * If this isn't the last console and it has CON_CONSDEV set, we 2647 * If this isn't the last console and it has CON_CONSDEV set, we
2598 * need to set it on the next preferred console. 2648 * need to set it on the next preferred console.