Commit 19feeff1 authored by Sergey Senozhatsky's avatar Sergey Senozhatsky Committed by Linus Torvalds

printk/nmi: avoid direct printk()-s from __printk_nmi_flush()

__printk_nmi_flush() can be called from nmi_panic(), therefore it has to
test whether it's executed in NMI context and thus must route the
messages through deferred printk() or via direct printk().

This is to avoid potential deadlocks, as described in commit
cf9b1106 ("printk/nmi: flush NMI messages on the system panic").

However there remain two places where __printk_nmi_flush() does
unconditional direct printk() calls:

 - pr_err("printk_nmi_flush: internal error ...")
 - pr_cont("\n")

Factor out print_nmi_seq_line() parts into a new printk_nmi_flush_line()
function, which takes care of in_nmi(), and use it in
__printk_nmi_flush() for printing and error-reporting.

Link: http://lkml.kernel.org/r/20160830161354.581-1-sergey.senozhatsky@gmail.comSigned-off-by: default avatarSergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Jan Kara <jack@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 7e932159
...@@ -99,26 +99,32 @@ static int vprintk_nmi(const char *fmt, va_list args) ...@@ -99,26 +99,32 @@ static int vprintk_nmi(const char *fmt, va_list args)
return add; return add;
} }
/* static void printk_nmi_flush_line(const char *text, int len)
* printk one line from the temporary buffer from @start index until
* and including the @end index.
*/
static void print_nmi_seq_line(struct nmi_seq_buf *s, int start, int end)
{ {
const char *buf = s->buffer + start;
/* /*
* The buffers are flushed in NMI only on panic. The messages must * The buffers are flushed in NMI only on panic. The messages must
* go only into the ring buffer at this stage. Consoles will get * go only into the ring buffer at this stage. Consoles will get
* explicitly called later when a crashdump is not generated. * explicitly called later when a crashdump is not generated.
*/ */
if (in_nmi()) if (in_nmi())
printk_deferred("%.*s", (end - start) + 1, buf); printk_deferred("%.*s", len, text);
else else
printk("%.*s", (end - start) + 1, buf); printk("%.*s", len, text);
} }
/*
* printk one line from the temporary buffer from @start index until
* and including the @end index.
*/
static void printk_nmi_flush_seq_line(struct nmi_seq_buf *s,
int start, int end)
{
const char *buf = s->buffer + start;
printk_nmi_flush_line(buf, (end - start) + 1);
}
/* /*
* Flush data from the associated per_CPU buffer. The function * Flush data from the associated per_CPU buffer. The function
* can be called either via IRQ work or independently. * can be called either via IRQ work or independently.
...@@ -150,9 +156,11 @@ static void __printk_nmi_flush(struct irq_work *work) ...@@ -150,9 +156,11 @@ static void __printk_nmi_flush(struct irq_work *work)
* the buffer an unexpected way. If we printed something then * the buffer an unexpected way. If we printed something then
* @len must only increase. * @len must only increase.
*/ */
if (i && i >= len) if (i && i >= len) {
pr_err("printk_nmi_flush: internal error: i=%d >= len=%zu\n", const char *msg = "printk_nmi_flush: internal error\n";
i, len);
printk_nmi_flush_line(msg, strlen(msg));
}
if (!len) if (!len)
goto out; /* Someone else has already flushed the buffer. */ goto out; /* Someone else has already flushed the buffer. */
...@@ -166,14 +174,14 @@ static void __printk_nmi_flush(struct irq_work *work) ...@@ -166,14 +174,14 @@ static void __printk_nmi_flush(struct irq_work *work)
/* Print line by line. */ /* Print line by line. */
for (; i < size; i++) { for (; i < size; i++) {
if (s->buffer[i] == '\n') { if (s->buffer[i] == '\n') {
print_nmi_seq_line(s, last_i, i); printk_nmi_flush_seq_line(s, last_i, i);
last_i = i + 1; last_i = i + 1;
} }
} }
/* Check if there was a partial line. */ /* Check if there was a partial line. */
if (last_i < size) { if (last_i < size) {
print_nmi_seq_line(s, last_i, size - 1); printk_nmi_flush_seq_line(s, last_i, size - 1);
pr_cont("\n"); printk_nmi_flush_line("\n", strlen("\n"));
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment