Skip to content

Commit e369d82

Browse files
ozbenhpmladek
authored andcommitted
printk: Fix preferred console selection with multiple matches
In the following circumstances, the rule of selecting the console corresponding to the last "console=" entry on the command line as the preferred console (CON_CONSDEV, ie, /dev/console) fails. This is a specific example, but it could happen with different consoles that have a similar name aliasing mechanism. - The kernel command line has both console=tty0 and console=ttyS0 in that order (the latter with speed etc... arguments). This is common with some cloud setups such as Amazon Linux. - add_preferred_console is called early to register "uart0". In our case that happens from acpi_parse_spcr() on arm64 since the "enable_console" argument is true on that architecture. This causes "uart0" to become entry 0 of the console_cmdline array. Now, because of the above, what happens is: - add_preferred_console is called by the cmdline parsing for tty0 and ttyS0 respectively, thus occupying entries 1 and 2 of the console_cmdline array (since this happens after ACPI SPCR parsing). At that point preferred_console is set to 2 as expected. - When the tty layer kicks in, it will call register_console for tty0. This will match entry 1 in console_cmdline array. It isn't our preferred console but because it's our only console at this point, it will end up "first" in the consoles list. - When 8250 probes the actual serial port later on, it calls register_console for ttyS0. At that point the loop in register_console tries to match it with the entries in the console_cmdline array. Ideally this should match ttyS0 in entry 2, which is preferred, causing it to be inserted first and to replace tty0 as CONSDEV. However, 8250 provides a "match" hook in its struct console, and that hook will match "uart" as an alias to "ttyS". So we match uart0 at entry 0 in the array which is not the preferred console and will not match entry 2 which is since we break out of the loop on the first match. As a result, we don't set CONSDEV and don't insert it first, but second in the console list. As a result, we end up with tty0 remaining first in the array, and thus /dev/console going there instead of the last user specified one which is ttyS0. This tentative fix register_console() to scan first for consoles specified on the command line, and only if none is found, to then scan for consoles specified by the architecture. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Benjamin Herrenschmidt <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Reviewed-by: Sergey Senozhatsky <[email protected]> Signed-off-by: Petr Mladek <[email protected]>
1 parent ad8cd1d commit e369d82

File tree

2 files changed

+19
-11
lines changed

2 files changed

+19
-11
lines changed

kernel/printk/console_cmdline.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ struct console_cmdline
66
{
77
char name[16]; /* Name of the driver */
88
int index; /* Minor dev. to use */
9+
bool user_specified; /* Specified by command line vs. platform */
910
char *options; /* Options for the driver */
1011
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
1112
char *brl_options; /* Options for braille driver */

kernel/printk/printk.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,7 +2116,7 @@ asmlinkage __visible void early_printk(const char *fmt, ...)
21162116
#endif
21172117

21182118
static int __add_preferred_console(char *name, int idx, char *options,
2119-
char *brl_options)
2119+
char *brl_options, bool user_specified)
21202120
{
21212121
struct console_cmdline *c;
21222122
int i;
@@ -2131,6 +2131,8 @@ static int __add_preferred_console(char *name, int idx, char *options,
21312131
if (strcmp(c->name, name) == 0 && c->index == idx) {
21322132
if (!brl_options)
21332133
preferred_console = i;
2134+
if (user_specified)
2135+
c->user_specified = true;
21342136
return 0;
21352137
}
21362138
}
@@ -2140,6 +2142,7 @@ static int __add_preferred_console(char *name, int idx, char *options,
21402142
preferred_console = i;
21412143
strlcpy(c->name, name, sizeof(c->name));
21422144
c->options = options;
2145+
c->user_specified = user_specified;
21432146
braille_set_options(c, brl_options);
21442147

21452148
c->index = idx;
@@ -2194,7 +2197,7 @@ static int __init console_setup(char *str)
21942197
idx = simple_strtoul(s, NULL, 10);
21952198
*s = 0;
21962199

2197-
__add_preferred_console(buf, idx, options, brl_options);
2200+
__add_preferred_console(buf, idx, options, brl_options, true);
21982201
console_set_on_cmdline = 1;
21992202
return 1;
22002203
}
@@ -2215,7 +2218,7 @@ __setup("console=", console_setup);
22152218
*/
22162219
int add_preferred_console(char *name, int idx, char *options)
22172220
{
2218-
return __add_preferred_console(name, idx, options, NULL);
2221+
return __add_preferred_console(name, idx, options, NULL, false);
22192222
}
22202223

22212224
bool console_suspend_enabled = true;
@@ -2636,14 +2639,16 @@ early_param("keep_bootcon", keep_bootcon_setup);
26362639
* Care need to be taken with consoles that are statically
26372640
* enabled such as netconsole
26382641
*/
2639-
static int try_enable_new_console(struct console *newcon)
2642+
static int try_enable_new_console(struct console *newcon, bool user_specified)
26402643
{
26412644
struct console_cmdline *c;
26422645
int i;
26432646

26442647
for (i = 0, c = console_cmdline;
26452648
i < MAX_CMDLINECONSOLES && c->name[0];
26462649
i++, c++) {
2650+
if (c->user_specified != user_specified)
2651+
continue;
26472652
if (!newcon->match ||
26482653
newcon->match(newcon, c->name, c->index, c->options) != 0) {
26492654
/* default matching */
@@ -2673,9 +2678,10 @@ static int try_enable_new_console(struct console *newcon)
26732678

26742679
/*
26752680
* Some consoles, such as pstore and netconsole, can be enabled even
2676-
* without matching.
2681+
* without matching. Accept the pre-enabled consoles only when match()
2682+
* and setup() had a change to be called.
26772683
*/
2678-
if (newcon->flags & CON_ENABLED)
2684+
if (newcon->flags & CON_ENABLED && c->user_specified == user_specified)
26792685
return 0;
26802686

26812687
return -ENOENT;
@@ -2752,11 +2758,12 @@ void register_console(struct console *newcon)
27522758
}
27532759
}
27542760

2755-
/*
2756-
* See if this console matches one we selected on
2757-
* the command line or if it was statically enabled
2758-
*/
2759-
err = try_enable_new_console(newcon);
2761+
/* See if this console matches one we selected on the command line */
2762+
err = try_enable_new_console(newcon, true);
2763+
2764+
/* If not, try to match against the platform default(s) */
2765+
if (err == -ENOENT)
2766+
err = try_enable_new_console(newcon, false);
27602767

27612768
/* printk() messages are not printed to the Braille console. */
27622769
if (err || newcon->flags & CON_BRL)

0 commit comments

Comments
 (0)