aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/printk
diff options
context:
space:
mode:
authorSergey Senozhatsky <sergey.senozhatsky@gmail.com>2016-08-02 17:03:56 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-08-02 19:35:04 -0400
commitcf7754441c563230ed74096fcd4b8cca49910550 (patch)
tree14975cad530696dcda95be2f34e71d03c9494341 /kernel/printk
parent874f9c7da9a4acbc1b9e12ca722579fb50e4d142 (diff)
printk: introduce suppress_message_printing()
Messages' levels and console log level are inspected when the actual printing occurs, which may provoke console_unlock() and console_cont_flush() to waste CPU cycles on every message that has loglevel above the current console_loglevel. Schematically, console_unlock() does the following: console_unlock() { ... for (;;) { ... raw_spin_lock_irqsave(&logbuf_lock, flags); skip: msg = log_from_idx(console_idx); if (msg->flags & LOG_NOCONS) { ... goto skip; } level = msg->level; len += msg_print_text(); >> sprintfs memcpy, etc. if (nr_ext_console_drivers) { ext_len = msg_print_ext_header(); >> scnprintf ext_len += msg_print_ext_body(); >> scnprintfs etc. } ... raw_spin_unlock(&logbuf_lock); call_console_drivers(level, ext_text, ext_len, text, len) { if (level >= console_loglevel && >> drop the message !ignore_loglevel) return; console->write(...); } local_irq_restore(flags); } ... } The thing here is this deferred `level >= console_loglevel' check. We are wasting CPU cycles on sprintfs/memcpy/etc. preparing the messages that we will eventually drop. This can be huge when we register a new CON_PRINTBUFFER console, for instance. For every such a console register_console() resets the console_seq, console_idx, console_prev and sets a `exclusive console' pointer to replay the log buffer to that just-registered console. And there can be a lot of messages to replay, in the worst case most of which can be dropped after console_loglevel test. We know messages' levels long before we call msg_print_text() and friends, so we can just move console_loglevel check out of call_console_drivers() and format a new message only if we are sure that it won't be dropped. The patch factors out loglevel check into suppress_message_printing() function and tests message->level and console_loglevel before formatting functions in console_unlock() and console_cont_flush() are getting executed. This improves things not only for exclusive CON_PRINTBUFFER consoles, but for every console_unlock() that attempts to print a message of level above the console_loglevel. Link: http://lkml.kernel.org/r/20160627135012.8229-1-sergey.senozhatsky@gmail.com Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> Reviewed-by: Petr Mladek <pmladek@suse.com> Cc: Tejun Heo <tj@kernel.org> Cc: Jan Kara <jack@suse.cz> Cc: Calvin Owens <calvinowens@fb.com> 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.c25
1 files changed, 19 insertions, 6 deletions
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index d2accf2f4448..8bdce14254f4 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -985,6 +985,11 @@ module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
985MODULE_PARM_DESC(ignore_loglevel, 985MODULE_PARM_DESC(ignore_loglevel,
986 "ignore loglevel setting (prints all kernel messages to the console)"); 986 "ignore loglevel setting (prints all kernel messages to the console)");
987 987
988static bool suppress_message_printing(int level)
989{
990 return (level >= console_loglevel && !ignore_loglevel);
991}
992
988#ifdef CONFIG_BOOT_PRINTK_DELAY 993#ifdef CONFIG_BOOT_PRINTK_DELAY
989 994
990static int boot_delay; /* msecs delay after each printk during bootup */ 995static int boot_delay; /* msecs delay after each printk during bootup */
@@ -1014,7 +1019,7 @@ static void boot_delay_msec(int level)
1014 unsigned long timeout; 1019 unsigned long timeout;
1015 1020
1016 if ((boot_delay == 0 || system_state != SYSTEM_BOOTING) 1021 if ((boot_delay == 0 || system_state != SYSTEM_BOOTING)
1017 || (level >= console_loglevel && !ignore_loglevel)) { 1022 || suppress_message_printing(level)) {
1018 return; 1023 return;
1019 } 1024 }
1020 1025
@@ -1438,8 +1443,6 @@ static void call_console_drivers(int level,
1438 1443
1439 trace_console(text, len); 1444 trace_console(text, len);
1440 1445
1441 if (level >= console_loglevel && !ignore_loglevel)
1442 return;
1443 if (!console_drivers) 1446 if (!console_drivers)
1444 return; 1447 return;
1445 1448
@@ -1908,6 +1911,7 @@ static void call_console_drivers(int level,
1908static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev, 1911static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev,
1909 bool syslog, char *buf, size_t size) { return 0; } 1912 bool syslog, char *buf, size_t size) { return 0; }
1910static size_t cont_print_text(char *text, size_t size) { return 0; } 1913static size_t cont_print_text(char *text, size_t size) { return 0; }
1914static bool suppress_message_printing(int level) { return false; }
1911 1915
1912/* Still needs to be defined for users */ 1916/* Still needs to be defined for users */
1913DEFINE_PER_CPU(printk_func_t, printk_func); 1917DEFINE_PER_CPU(printk_func_t, printk_func);
@@ -2187,6 +2191,13 @@ static void console_cont_flush(char *text, size_t size)
2187 if (!cont.len) 2191 if (!cont.len)
2188 goto out; 2192 goto out;
2189 2193
2194 if (suppress_message_printing(cont.level)) {
2195 cont.cons = cont.len;
2196 if (cont.flushed)
2197 cont.len = 0;
2198 goto out;
2199 }
2200
2190 /* 2201 /*
2191 * We still queue earlier records, likely because the console was 2202 * We still queue earlier records, likely because the console was
2192 * busy. The earlier ones need to be printed before this one, we 2203 * busy. The earlier ones need to be printed before this one, we
@@ -2290,10 +2301,13 @@ skip:
2290 break; 2301 break;
2291 2302
2292 msg = log_from_idx(console_idx); 2303 msg = log_from_idx(console_idx);
2293 if (msg->flags & LOG_NOCONS) { 2304 level = msg->level;
2305 if ((msg->flags & LOG_NOCONS) ||
2306 suppress_message_printing(level)) {
2294 /* 2307 /*
2295 * Skip record we have buffered and already printed 2308 * Skip record we have buffered and already printed
2296 * directly to the console when we received it. 2309 * directly to the console when we received it, and
2310 * record that has level above the console loglevel.
2297 */ 2311 */
2298 console_idx = log_next(console_idx); 2312 console_idx = log_next(console_idx);
2299 console_seq++; 2313 console_seq++;
@@ -2307,7 +2321,6 @@ skip:
2307 goto skip; 2321 goto skip;
2308 } 2322 }
2309 2323
2310 level = msg->level;
2311 len += msg_print_text(msg, console_prev, false, 2324 len += msg_print_text(msg, console_prev, false,
2312 text + len, sizeof(text) - len); 2325 text + len, sizeof(text) - len);
2313 if (nr_ext_console_drivers) { 2326 if (nr_ext_console_drivers) {