diff -drupN a/kernel/printk/printk.c b/kernel/printk/printk.c --- a/kernel/printk/printk.c 2018-08-06 17:23:04.000000000 +0300 +++ b/kernel/printk/printk.c 2022-06-12 05:28:14.000000000 +0300 @@ -253,6 +253,17 @@ static int preferred_console = -1; int console_set_on_cmdline; EXPORT_SYMBOL(console_set_on_cmdline); +#ifdef CONFIG_OF +static bool of_specified_console; + +void console_set_by_of(void) +{ + of_specified_console = true; +} +#else +# define of_specified_console false +#endif + /* Flag: console code may call schedule() */ static int console_may_schedule; @@ -655,8 +666,11 @@ static ssize_t msg_print_ext_header(char * better readable output. 'c' in the record flags mark the first * fragment of a line, '+' the following. */ - if (msg->flags & LOG_CONT) - cont = (prev_flags & LOG_CONT) ? '+' : 'c'; + if (msg->flags & LOG_CONT && !(prev_flags & LOG_CONT)) + cont = 'c'; + else if ((msg->flags & LOG_CONT) || + ((prev_flags & LOG_CONT) && !(msg->flags & LOG_PREFIX))) + cont = '+'; return scnprintf(buf, size, "%u,%llu,%llu,%c;", (msg->facility << 3) | msg->level, seq, ts_usec, cont); @@ -1564,7 +1578,7 @@ static void call_console_drivers(int lev { struct console *con; - trace_console_rcuidle(text, len); + trace_console(text, len); if (!console_drivers) return; @@ -1640,33 +1654,35 @@ static struct cont { bool flushed:1; /* buffer sealed and committed */ } cont; -static void cont_flush(void) +static void cont_flush(enum log_flags flags) { if (cont.flushed) return; if (cont.len == 0) return; + 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, cont.flags | LOG_NOCONS, + log_store(cont.facility, cont.level, flags | LOG_NOCONS, cont.ts_nsec, NULL, 0, cont.buf, cont.len); + cont.flags = flags; 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, cont.flags, 0, + 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, enum log_flags flags, const char *text, size_t len) +static bool cont_add(int facility, int level, const char *text, size_t len) { if (cont.len && cont.flushed) return false; @@ -1677,7 +1693,7 @@ static bool cont_add(int facility, int l * the line gets too long, split it up in separate records. */ if (nr_ext_console_drivers || cont.len + len > sizeof(cont.buf)) { - cont_flush(); + cont_flush(LOG_CONT); return false; } @@ -1686,7 +1702,7 @@ static bool cont_add(int facility, int l cont.level = level; cont.owner = current; cont.ts_nsec = local_clock(); - cont.flags = flags; + cont.flags = 0; cont.cons = 0; cont.flushed = false; } @@ -1694,15 +1710,8 @@ static bool cont_add(int facility, int l memcpy(cont.buf + cont.len, text, len); cont.len += len; - // The original flags come from the first line, - // but later continuations can add a newline. - if (flags & LOG_NEWLINE) { - cont.flags |= LOG_NEWLINE; - cont_flush(); - } - if (cont.len > (sizeof(cont.buf) * 80) / 100) - cont_flush(); + cont_flush(LOG_CONT); return true; } @@ -1735,35 +1744,6 @@ static size_t cont_print_text(char *text return textlen; } -static size_t log_output(int facility, int level, enum log_flags lflags, const char *dict, size_t dictlen, char *text, size_t text_len) -{ - /* - * If an earlier line was buffered, and we're a continuation - * write from the same process, try to add it to the buffer. - */ - if (cont.len) { - if (cont.owner == current && (lflags & LOG_CONT)) { - if (cont_add(facility, level, lflags, text, text_len)) - return text_len; - } - /* Otherwise, make sure it's flushed */ - cont_flush(); - } - - /* Skip empty continuation lines that couldn't be added - they just flush */ - if (!text_len && (lflags & LOG_CONT)) - return 0; - - /* If it doesn't end in a newline, try to buffer the current line */ - if (!(lflags & LOG_NEWLINE)) { - if (cont_add(facility, level, lflags, text, text_len)) - return text_len; - } - - /* Store it in the record log */ - return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len); -} - asmlinkage int vprintk_emit(int facility, int level, const char *dict, size_t dictlen, const char *fmt, va_list args) @@ -1850,9 +1830,10 @@ asmlinkage int vprintk_emit(int facility /* strip kernel syslog prefix and extract log level or control flags */ if (facility == 0) { - int kern_level; + int kern_level = printk_get_level(text); - while ((kern_level = printk_get_level(text)) != 0) { + if (kern_level) { + const char *end_of_header = printk_skip_level(text); switch (kern_level) { case '0' ... '7': if (level == LOGLEVEL_DEFAULT) @@ -1860,13 +1841,14 @@ asmlinkage int vprintk_emit(int facility /* fallthrough */ case 'd': /* KERN_DEFAULT */ lflags |= LOG_PREFIX; - break; - case 'c': /* KERN_CONT */ - lflags |= LOG_CONT; } - - text_len -= 2; - text += 2; + /* + * No need to check length here because vscnprintf + * put '\0' at the end of the string. Only valid and + * newly printed level is detected. + */ + text_len -= end_of_header - text; + text = (char *)end_of_header; } } @@ -1876,7 +1858,45 @@ asmlinkage int vprintk_emit(int facility if (dict) lflags |= LOG_PREFIX|LOG_NEWLINE; - printed_len += log_output(facility, level, lflags, dict, dictlen, text, text_len); + if (!(lflags & LOG_NEWLINE)) { + /* + * Flush the conflicting buffer. An earlier newline was missing, + * or another task also prints continuation lines. + */ + if (cont.len && (lflags & LOG_PREFIX || cont.owner != current)) + cont_flush(LOG_NEWLINE); + + /* buffer line if possible, otherwise store it right away */ + if (cont_add(facility, level, text, text_len)) + printed_len += text_len; + else + printed_len += log_store(facility, level, + lflags | LOG_CONT, 0, + dict, dictlen, text, text_len); + } else { + bool stored = false; + + /* + * If an earlier newline was missing and it was the same task, + * either merge it with the current buffer and flush, or if + * there was a race with interrupts (prefix == true) then just + * flush it out and store this line separately. + * If the preceding printk was from a different task and missed + * a newline, flush and append the newline. + */ + if (cont.len) { + if (cont.owner == current && !(lflags & LOG_PREFIX)) + stored = cont_add(facility, level, text, + text_len); + cont_flush(LOG_NEWLINE); + } + + if (stored) + printed_len += text_len; + else + printed_len += log_store(facility, level, lflags, 0, + dict, dictlen, text, text_len); + } logbuf_cpu = UINT_MAX; raw_spin_unlock(&logbuf_lock); @@ -2642,7 +2662,7 @@ void register_console(struct console *ne * didn't select a console we take the first one * that registers here. */ - if (preferred_console < 0) { + if (preferred_console < 0 && !of_specified_console) { if (newcon->index < 0) newcon->index = 0; if (newcon->setup == NULL ||