Recent changes to allow using DEVNAME:0.0 style console names caused a regression to the kernel command line handling for the console options. The last preferred console added gets used for init. This is documented in the comments for add_preferred_console(). Now the kernel command line options for console=ttyS0,115200 console=tty0 are wrongly handled and cause the /dev/console to be associated with ttyS0 instead of tty0. This happens because we are calling __add_preferred_console() later on from serial8250_isa_init_ports() after console_setup() and the console gets treated as the last added preferred console. As the DEVNAME:0.0 style console device is not known at console_setup() time, and we need to call __add_preferred_console() later. Let's fix the issue by reserving a position in console_cmdline for a deferred console, and then populate the reserved entry before calling __add_preferred_console(). Note that we now need to check for valid preferred console too in __add_preferred_console(). Reported-by: Petr Mladek <pmladek@xxxxxxxx> Link: https://lore.kernel.org/linux-serial/ZlC6_Um4P4b-_WQE@xxxxxxxxxxxxxxx/ Fixes: f03e8c1060f8 ("printk: Save console options for add_preferred_console_match()") Signed-off-by: Tony Lindgren <tony.lindgren@xxxxxxxxxxxxxxx> --- kernel/printk/conopt.c | 2 +- kernel/printk/console_cmdline.h | 6 ++- kernel/printk/printk.c | 68 ++++++++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/kernel/printk/conopt.c b/kernel/printk/conopt.c index 9d507bac3657..08dded8fbb3b 100644 --- a/kernel/printk/conopt.c +++ b/kernel/printk/conopt.c @@ -140,7 +140,7 @@ int add_preferred_console_match(const char *match, const char *name, if (con->has_brl_opt) brl_opt = con->brl_opt; - console_opt_add_preferred_console(name, idx, con->opt, brl_opt); + console_opt_add_preferred_console(match, name, idx, con->opt, brl_opt); return 0; } diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h index a125e0235589..e22021e3e28b 100644 --- a/kernel/printk/console_cmdline.h +++ b/kernel/printk/console_cmdline.h @@ -5,13 +5,15 @@ #define MAX_CMDLINECONSOLES 8 int console_opt_save(const char *str, const char *brl_opt); -int console_opt_add_preferred_console(const char *name, const short idx, - char *options, char *brl_options); +int console_opt_add_preferred_console(const char *devname, const char *name, + const short idx, char *options, + char *brl_options); struct console_cmdline { char name[16]; /* Name of the driver */ int index; /* Minor dev. to use */ + char devname[32]; /* DEVNAME:0.0 style device name */ bool user_specified; /* Specified by command line vs. platform */ char *options; /* Options for the driver */ #ifdef CONFIG_A11Y_BRAILLE_CONSOLE diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 420fd310129d..52bdd7dcdb6f 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2426,6 +2426,24 @@ static void set_user_specified(struct console_cmdline *c, bool user_specified) console_set_on_cmdline = 1; } +/* Checks if a console is the last user specified preferred console */ +static bool is_last_user_prefcon(int position) +{ + struct console_cmdline *c = console_cmdline; + int last_user_specified = -1; + int i; + + for (i = 0; i < MAX_CMDLINECONSOLES; i++, c++) { + if (!c->name[0] && !c->devname[0]) + break; + + if (c->user_specified || c->devname[0]) + last_user_specified = i; + } + + return position == last_user_specified; +} + static int __add_preferred_console(const char *name, const short idx, char *options, char *brl_options, bool user_specified) { @@ -2445,10 +2463,10 @@ static int __add_preferred_console(const char *name, const short idx, char *opti * if we have a slot free. */ for (i = 0, c = console_cmdline; - i < MAX_CMDLINECONSOLES && c->name[0]; + i < MAX_CMDLINECONSOLES && (c->name[0] || c->devname[0]); i++, c++) { if (strcmp(c->name, name) == 0 && c->index == idx) { - if (!brl_options) + if (!brl_options && (!user_specified || is_last_user_prefcon(i))) preferred_console = i; set_user_specified(c, user_specified); return 0; @@ -2467,6 +2485,25 @@ static int __add_preferred_console(const char *name, const short idx, char *opti return 0; } +/* Reserves a console_cmdline position for a deferred devname console */ +static void reserve_deferred_console(const char *str) +{ + struct console_cmdline *c = console_cmdline; + size_t namelen; + int i; + + namelen = strcspn(str, ","); + if (namelen == 0 || namelen >= sizeof(c->devname)) + return; + + for (i = 0; i < MAX_CMDLINECONSOLES; i++, c++) { + if (c->name[0] || c->devname[0]) + continue; + strscpy(c->devname, str, namelen + 1); + return; + } +} + static int __init console_msg_format_setup(char *str) { if (!strcmp(str, "syslog")) @@ -2508,8 +2545,10 @@ static int __init console_setup(char *str) console_set_on_cmdline = 1; /* Don't attempt to parse a DEVNAME:0.0 style console */ - if (strchr(str, ':')) + if (strchr(str, ':')) { + reserve_deferred_console(str); return 1; + } /* * Decode str into name, index, options. @@ -2542,9 +2581,26 @@ static int __init console_setup(char *str) __setup("console=", console_setup); /* Only called from add_preferred_console_match() */ -int console_opt_add_preferred_console(const char *name, const short idx, - char *options, char *brl_options) +int console_opt_add_preferred_console(const char *devname, const char *name, + const short idx, char *options, + char *brl_options) { + struct console_cmdline *c = console_cmdline; + int i; + + /* Populate a reserved console based on devname */ + for (i = 0; i < MAX_CMDLINECONSOLES; i++, c++) { + if (!c->name[0] && !strcmp(c->devname, devname)) { + strscpy(c->name, name); + c->index = idx; + c->options = options; +#ifdef CONFIG_A11Y_BRAILLE_CONSOLE + c->brl_options = brl_options; +#endif + break; + } + } + return __add_preferred_console(name, idx, options, brl_options, true); } @@ -3333,7 +3389,7 @@ static int try_enable_preferred_console(struct console *newcon, int i, err; for (i = 0, c = console_cmdline; - i < MAX_CMDLINECONSOLES && c->name[0]; + i < MAX_CMDLINECONSOLES && (c->name[0] || c->devname[0]); i++, c++) { if (c->user_specified != user_specified) continue; -- 2.45.2