There are several problems with replying the log buffer when a new console is added.
Only the end of partially flushed line is printed on the new console. But this end of the line is not longer printed on the other consoles. In fact, any new line are printed only on the new console until the replay is finished. This patch tries to fix all the above mentioned problems. The replay finishes when the original sequence number is reached. Only the already printed piece of the continuous line is printed. The rest will be printed when all consoles are enabled again. Signed-off-by: Petr Mladek <pmla...@suse.com> --- kernel/printk/printk.c | 68 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index a920cc36c24e..d117e7b1244d 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -134,9 +134,11 @@ static int __down_trylock_console_sem(unsigned long ip) static int console_locked, console_suspended; /* - * If exclusive_console is non-NULL then only this console is to be printed to. + * If exclusive_console is non-NULL then only this console is to be printed to + * and only until the given sequence number. */ static struct console *exclusive_console; +static u64 exclusive_console_seq_stop; /* * Array of consoles built from command line options (console=) @@ -2177,14 +2179,15 @@ static inline int can_use_console(void) return cpu_online(raw_smp_processor_id()) || have_callable_console(); } -static void console_cont_flush(char *text, size_t size) +static void console_cont_flush(struct cont *cont,char *text, size_t size) { unsigned long flags; + u8 level; size_t len; raw_spin_lock_irqsave(&logbuf_lock, flags); - if (!cont.msg->text_len) + if (!cont->msg->text_len) goto out; /* @@ -2194,10 +2197,11 @@ static void console_cont_flush(char *text, size_t size) if (console_seq < log_next_seq) goto out; - len = cont_print_text(&cont, text, size); + len = cont_print_text(cont, text, size); + level = cont->msg->level; raw_spin_unlock(&logbuf_lock); stop_critical_timings(); - call_console_drivers(cont.msg->level, NULL, 0, text, len); + call_console_drivers(level, NULL, 0, text, len); start_critical_timings(); local_irq_restore(flags); return; @@ -2282,18 +2286,19 @@ again: len = 0; } + if (exclusive_console && + console_seq == exclusive_console_seq_stop) + break; + if (console_seq == log_next_seq) break; msg = log_from_idx(console_idx); level = msg->level; - /* - * The line might be already partially printed from the cont - * buffer. In this case, flush the rest using a fake - * cont buffer. - */ - if (cont_console_len && cont_console_seq == console_seq) { + if (cont_console_len && + cont_console_seq == console_seq && + !exclusive_console) { struct cont fake_cont; fake_cont.owner = NULL; @@ -2317,7 +2322,6 @@ again: log_text(msg), msg->text_len); } } - console_idx = log_next(console_idx); console_seq++; console_prev = msg->flags; @@ -2334,12 +2338,43 @@ again: raw_spin_unlock(&logbuf_lock); - /* flush buffered message fragment immediately to console */ - console_cont_flush(text, sizeof(text)); + if (unlikely(exclusive_console)) { + /* Replay also the partially flushed cont buffer */ + if (cont_console_seq) { + struct printk_log fake_msg; + struct printk_log *msg; + struct cont fake_cont; + size_t len; + int level; + + raw_spin_lock(&logbuf_lock); + if (console_seq == log_next_seq) { + memcpy(&fake_msg, &cont_msg, sizeof(fake_msg)); + fake_cont.buf = cont_buf; + } else { + msg = log_from_idx(console_idx); + memcpy(&fake_msg, msg, sizeof(fake_msg)); + fake_cont.buf = log_text(msg); + } + fake_cont.msg = &fake_msg; + fake_msg.text_len = cont_console_len; + cont_console_len = 0; + + len = cont_print_text(&fake_cont, text, sizeof(text)); + level = fake_msg.level; + + raw_spin_unlock(&logbuf_lock); + + stop_critical_timings(); /* don't trace print latency */ + call_console_drivers(level, NULL, 0, text, len); + start_critical_timings(); + } - /* Release the exclusive_console once it is used */ - if (unlikely(exclusive_console)) exclusive_console = NULL; + } else { + /* Flush buffered message fragment immediately to console */ + console_cont_flush(&cont, text, sizeof(text)); + } console_locked = 0; up_console_sem(); @@ -2626,6 +2661,7 @@ void register_console(struct console *newcon) * the already-registered consoles. */ exclusive_console = newcon; + exclusive_console_seq_stop = console_seq; } console_unlock(); console_sysfs_notify(); -- 1.8.5.6