Commit eab07260 authored by Kay Sievers's avatar Kay Sievers Committed by Greg Kroah-Hartman

kmsg - do not flush partial lines when the console is busy

Fragments of continuation lines are flushed to the console immediately. In
case the console is locked, the fragment must be queued up in the cont
buffer.

If the the console is busy and the continuation line is complete, but no part
of it was written to the console up to this point, we can just store the
entire line as a regular record and free the buffer earlier.

If the console is busy and earlier messages are already queued up, we
should not flush the fragments of continuation lines, but store them after
the queued up messages, to ensure the proper ordering.

This keeps the console output better readable in case printk()s race against
each other, or we receive over-long continuation lines we need to flush.
Signed-off-by: default avatarKay Sievers <kay@vrfy.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d39f3d77
...@@ -231,6 +231,11 @@ static u32 log_first_idx; ...@@ -231,6 +231,11 @@ static u32 log_first_idx;
static u64 log_next_seq; static u64 log_next_seq;
static u32 log_next_idx; static u32 log_next_idx;
/* the next printk record to write to the console */
static u64 console_seq;
static u32 console_idx;
static enum log_flags console_prev;
/* the next printk record to read after the last 'clear' command */ /* the next printk record to read after the last 'clear' command */
static u64 clear_seq; static u64 clear_seq;
static u32 clear_idx; static u32 clear_idx;
...@@ -1386,6 +1391,7 @@ static struct cont { ...@@ -1386,6 +1391,7 @@ static struct cont {
u64 ts_nsec; /* time of first print */ u64 ts_nsec; /* time of first print */
u8 level; /* log level of first message */ u8 level; /* log level of first message */
u8 facility; /* log level of first message */ u8 facility; /* log level of first message */
enum log_flags flags; /* prefix, newline flags */
bool flushed:1; /* buffer sealed and committed */ bool flushed:1; /* buffer sealed and committed */
} cont; } cont;
...@@ -1396,10 +1402,25 @@ static void cont_flush(enum log_flags flags) ...@@ -1396,10 +1402,25 @@ static void cont_flush(enum log_flags flags)
if (cont.len == 0) if (cont.len == 0)
return; return;
log_store(cont.facility, cont.level, LOG_NOCONS | flags, if (cont.cons) {
/*
* If a fragment of this line was directly flushed to the
* console; wait for the console to pick up the rest of the
* line. LOG_NOCONS suppresses a duplicated output.
*/
log_store(cont.facility, cont.level, flags | LOG_NOCONS,
cont.ts_nsec, NULL, 0, cont.buf, cont.len); cont.ts_nsec, NULL, 0, cont.buf, cont.len);
cont.flags = flags;
cont.flushed = true; cont.flushed = true;
} else {
/*
* If no fragment of this line ever reached the console,
* just submit it to the store and free the buffer.
*/
log_store(cont.facility, cont.level, flags, 0,
NULL, 0, cont.buf, cont.len);
cont.len = 0;
}
} }
static bool cont_add(int facility, int level, const char *text, size_t len) static bool cont_add(int facility, int level, const char *text, size_t len)
...@@ -1418,12 +1439,17 @@ static bool cont_add(int facility, int level, const char *text, size_t len) ...@@ -1418,12 +1439,17 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
cont.level = level; cont.level = level;
cont.owner = current; cont.owner = current;
cont.ts_nsec = local_clock(); cont.ts_nsec = local_clock();
cont.flags = 0;
cont.cons = 0; cont.cons = 0;
cont.flushed = false; cont.flushed = false;
} }
memcpy(cont.buf + cont.len, text, len); memcpy(cont.buf + cont.len, text, len);
cont.len += len; cont.len += len;
if (cont.len > (sizeof(cont.buf) * 80) / 100)
cont_flush(LOG_CONT);
return true; return true;
} }
...@@ -1432,7 +1458,7 @@ static size_t cont_print_text(char *text, size_t size) ...@@ -1432,7 +1458,7 @@ static size_t cont_print_text(char *text, size_t size)
size_t textlen = 0; size_t textlen = 0;
size_t len; size_t len;
if (cont.cons == 0) { if (cont.cons == 0 && (console_prev & LOG_NEWLINE)) {
textlen += print_time(cont.ts_nsec, text); textlen += print_time(cont.ts_nsec, text);
size -= textlen; size -= textlen;
} }
...@@ -1447,6 +1473,7 @@ static size_t cont_print_text(char *text, size_t size) ...@@ -1447,6 +1473,7 @@ static size_t cont_print_text(char *text, size_t size)
} }
if (cont.flushed) { if (cont.flushed) {
if (cont.flags & LOG_NEWLINE)
text[textlen++] = '\n'; text[textlen++] = '\n';
/* got everything, release buffer */ /* got everything, release buffer */
cont.len = 0; cont.len = 0;
...@@ -1545,7 +1572,7 @@ asmlinkage int vprintk_emit(int facility, int level, ...@@ -1545,7 +1572,7 @@ asmlinkage int vprintk_emit(int facility, int level,
* or another task also prints continuation lines. * or another task also prints continuation lines.
*/ */
if (cont.len && (lflags & LOG_PREFIX || cont.owner != current)) if (cont.len && (lflags & LOG_PREFIX || cont.owner != current))
cont_flush(0); cont_flush(LOG_NEWLINE);
/* buffer line if possible, otherwise store it right away */ /* buffer line if possible, otherwise store it right away */
if (!cont_add(facility, level, text, text_len)) if (!cont_add(facility, level, text, text_len))
...@@ -1563,7 +1590,7 @@ asmlinkage int vprintk_emit(int facility, int level, ...@@ -1563,7 +1590,7 @@ asmlinkage int vprintk_emit(int facility, int level,
if (cont.len && cont.owner == current) { if (cont.len && cont.owner == current) {
if (!(lflags & LOG_PREFIX)) if (!(lflags & LOG_PREFIX))
stored = cont_add(facility, level, text, text_len); stored = cont_add(facility, level, text, text_len);
cont_flush(0); cont_flush(LOG_NEWLINE);
} }
if (!stored) if (!stored)
...@@ -1661,10 +1688,13 @@ EXPORT_SYMBOL(printk); ...@@ -1661,10 +1688,13 @@ EXPORT_SYMBOL(printk);
#define LOG_LINE_MAX 0 #define LOG_LINE_MAX 0
static u64 syslog_seq; static u64 syslog_seq;
static u32 syslog_idx; static u32 syslog_idx;
static u64 console_seq;
static u32 console_idx;
static enum log_flags syslog_prev; static enum log_flags syslog_prev;
static u64 log_first_seq; static u64 log_first_seq;
static u32 log_first_idx; static u32 log_first_idx;
static u64 log_next_seq; static u64 log_next_seq;
static enum log_flags console_prev;
static struct cont { static struct cont {
size_t len; size_t len;
size_t cons; size_t cons;
...@@ -1948,10 +1978,34 @@ void wake_up_klogd(void) ...@@ -1948,10 +1978,34 @@ void wake_up_klogd(void)
this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP); this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP);
} }
/* the next printk record to write to the console */ static void console_cont_flush(char *text, size_t size)
static u64 console_seq; {
static u32 console_idx; unsigned long flags;
static enum log_flags console_prev; size_t len;
raw_spin_lock_irqsave(&logbuf_lock, flags);
if (!cont.len)
goto out;
/*
* We still queue earlier records, likely because the console was
* busy. The earlier ones need to be printed before this one, we
* did not flush any fragment so far, so just let it queue up.
*/
if (console_seq < log_next_seq && !cont.cons)
goto out;
len = cont_print_text(text, size);
raw_spin_unlock(&logbuf_lock);
stop_critical_timings();
call_console_drivers(cont.level, text, len);
start_critical_timings();
local_irq_restore(flags);
return;
out:
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
}
/** /**
* console_unlock - unlock the console system * console_unlock - unlock the console system
...@@ -1983,19 +2037,7 @@ void console_unlock(void) ...@@ -1983,19 +2037,7 @@ void console_unlock(void)
console_may_schedule = 0; console_may_schedule = 0;
/* flush buffered message fragment immediately to console */ /* flush buffered message fragment immediately to console */
raw_spin_lock_irqsave(&logbuf_lock, flags); console_cont_flush(text, sizeof(text));
if (cont.len && (cont.cons < cont.len || cont.flushed)) {
size_t len;
len = cont_print_text(text, sizeof(text));
raw_spin_unlock(&logbuf_lock);
stop_critical_timings();
call_console_drivers(cont.level, text, len);
start_critical_timings();
local_irq_restore(flags);
} else
raw_spin_unlock_irqrestore(&logbuf_lock, flags);
again: again:
for (;;) { for (;;) {
struct log *msg; struct log *msg;
...@@ -2032,6 +2074,7 @@ void console_unlock(void) ...@@ -2032,6 +2074,7 @@ void console_unlock(void)
* will properly dump everything later. * will properly dump everything later.
*/ */
msg->flags &= ~LOG_NOCONS; msg->flags &= ~LOG_NOCONS;
console_prev = msg->flags;
goto skip; goto skip;
} }
......
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