diff options
author | Sergey Senozhatsky <sergey.senozhatsky@gmail.com> | 2016-08-02 17:03:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-08-02 19:35:04 -0400 |
commit | cf7754441c563230ed74096fcd4b8cca49910550 (patch) | |
tree | 14975cad530696dcda95be2f34e71d03c9494341 /kernel/printk | |
parent | 874f9c7da9a4acbc1b9e12ca722579fb50e4d142 (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.c | 25 |
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); | |||
985 | MODULE_PARM_DESC(ignore_loglevel, | 985 | MODULE_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 | ||
988 | static 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 | ||
990 | static int boot_delay; /* msecs delay after each printk during bootup */ | 995 | static 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, | |||
1908 | static size_t msg_print_text(const struct printk_log *msg, enum log_flags prev, | 1911 | static 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; } |
1910 | static size_t cont_print_text(char *text, size_t size) { return 0; } | 1913 | static size_t cont_print_text(char *text, size_t size) { return 0; } |
1914 | static 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 */ |
1913 | DEFINE_PER_CPU(printk_func_t, printk_func); | 1917 | DEFINE_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) { |