2024-06-13 12:52:00

by Tony Lindgren

[permalink] [raw]
Subject: [PATCH v2 0/2] Fixes for console command line ordering

Hi,

Recent changes to add support for DEVNAME:0.0 style consoles caused a
regression with the preferred console order where the last console on
the kernel command line is no longer the preferred console.

The following two changes fix the issue using Petr's suggestion that does
not involve calling __add_preferred_console() later on again, and adds
the deferred consoles to the console_cmdline[] directly to be updated
when the console is ready.

We revert the earlier printk related changes, and then add back the
DEVNAME:0.0 functionality based on Petr's code snippet. And we end up
reducing the code quite a bit too this way.

The reason we want DEVNAME:0.0 style consoles is it helps addressing the
console based on the connected serial port controller device rather than
using the hardcoded ttyS addressing. And that helps with issues related
to the console moving around after togging the HSUART option in the BIOS,
or when new ports are enabled in devicetree and aliases are not updated.

Regards,

Tony

Changes since v1:

- Revert the problem causing printk changes and switch to using the
solution based on Petr's suggestion and code snippet

Tony Lindgren (2):
printk: Revert add_preferred_console_match() related commits
printk: Add update_preferred_console()

drivers/tty/serial/serial_base_bus.c | 2 +-
include/linux/printk.h | 3 +-
kernel/printk/Makefile | 2 +-
kernel/printk/conopt.c | 146 ---------------------------
kernel/printk/console_cmdline.h | 7 +-
kernel/printk/printk.c | 96 ++++++++++++------
6 files changed, 69 insertions(+), 187 deletions(-)
delete mode 100644 kernel/printk/conopt.c


base-commit: 83a7eefedc9b56fe7bfeff13b6c7356688ffa670
--
2.45.2



2024-06-13 12:52:17

by Tony Lindgren

[permalink] [raw]
Subject: [PATCH v2 1/2] printk: Revert add_preferred_console_match() related commits

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, I added a call to
__add_preferred_console() later on when the console is ready.

To fix the issue, let's revert the printk related commits:

f03e8c1060f8 ("printk: Save console options for add_preferred_console_match()")
b73c9cbe4f1f ("printk: Flag register_console() if console is set on command line")
8a831c584e6e ("printk: Don't try to parse DEVNAME:0.0 console options")

We need to also drop the call for add_preferred_console_match() from
serial_base_add_one_prefcon() added by commit 787a1cabac01 ("serial: core:
Add support for DEVNAME:0.0 style naming for kernel console").

Petr has suggested a better way to handle the deferred consoles that does
not rely on calling __add_preferred_console() again.

Reported-by: Petr Mladek <[email protected]>
Link: https://lore.kernel.org/linux-serial/[email protected]/
Fixes: f03e8c1060f8 ("printk: Save console options for add_preferred_console_match()")
Signed-off-by: Tony Lindgren <[email protected]>
---
drivers/tty/serial/serial_base_bus.c | 8 +-
include/linux/printk.h | 3 -
kernel/printk/Makefile | 2 +-
kernel/printk/conopt.c | 146 ---------------------------
kernel/printk/console_cmdline.h | 6 --
kernel/printk/printk.c | 23 +----
6 files changed, 6 insertions(+), 182 deletions(-)
delete mode 100644 kernel/printk/conopt.c

diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
index 73c6ee540c83..5ebacb982f9e 100644
--- a/drivers/tty/serial/serial_base_bus.c
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -210,13 +210,7 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev)
static int serial_base_add_one_prefcon(const char *match, const char *dev_name,
int port_id)
{
- int ret;
-
- ret = add_preferred_console_match(match, dev_name, port_id);
- if (ret == -ENOENT)
- return 0;
-
- return ret;
+ return 0;
}

#ifdef __sparc__
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 40afab23881a..65c5184470f1 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -60,9 +60,6 @@ static inline const char *printk_skip_headers(const char *buffer)
#define CONSOLE_LOGLEVEL_DEFAULT CONFIG_CONSOLE_LOGLEVEL_DEFAULT
#define CONSOLE_LOGLEVEL_QUIET CONFIG_CONSOLE_LOGLEVEL_QUIET

-int add_preferred_console_match(const char *match, const char *name,
- const short idx);
-
extern int console_printk[];

#define console_loglevel (console_printk[0])
diff --git a/kernel/printk/Makefile b/kernel/printk/Makefile
index 040fe7d1eda2..39a2b61c7232 100644
--- a/kernel/printk/Makefile
+++ b/kernel/printk/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y = printk.o conopt.o
+obj-y = printk.o
obj-$(CONFIG_PRINTK) += printk_safe.o nbcon.o
obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o
obj-$(CONFIG_PRINTK_INDEX) += index.o
diff --git a/kernel/printk/conopt.c b/kernel/printk/conopt.c
deleted file mode 100644
index 9d507bac3657..000000000000
--- a/kernel/printk/conopt.c
+++ /dev/null
@@ -1,146 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Kernel command line console options for hardware based addressing
- *
- * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
- * Author: Tony Lindgren <[email protected]>
- */
-
-#include <linux/console.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/types.h>
-
-#include <asm/errno.h>
-
-#include "console_cmdline.h"
-
-/*
- * Allow longer DEVNAME:0.0 style console naming such as abcd0000.serial:0.0
- * in addition to the legacy ttyS0 style naming.
- */
-#define CONSOLE_NAME_MAX 32
-
-#define CONSOLE_OPT_MAX 16
-#define CONSOLE_BRL_OPT_MAX 16
-
-struct console_option {
- char name[CONSOLE_NAME_MAX];
- char opt[CONSOLE_OPT_MAX];
- char brl_opt[CONSOLE_BRL_OPT_MAX];
- u8 has_brl_opt:1;
-};
-
-/* Updated only at console_setup() time, no locking needed */
-static struct console_option conopt[MAX_CMDLINECONSOLES];
-
-/**
- * console_opt_save - Saves kernel command line console option for driver use
- * @str: Kernel command line console name and option
- * @brl_opt: Braille console options
- *
- * Saves a kernel command line console option for driver subsystems to use for
- * adding a preferred console during init. Called from console_setup() only.
- *
- * Return: 0 on success, negative error code on failure.
- */
-int __init console_opt_save(const char *str, const char *brl_opt)
-{
- struct console_option *con;
- size_t namelen, optlen;
- const char *opt;
- int i;
-
- namelen = strcspn(str, ",");
- if (namelen == 0 || namelen >= CONSOLE_NAME_MAX)
- return -EINVAL;
-
- opt = str + namelen;
- if (*opt == ',')
- opt++;
-
- optlen = strlen(opt);
- if (optlen >= CONSOLE_OPT_MAX)
- return -EINVAL;
-
- for (i = 0; i < MAX_CMDLINECONSOLES; i++) {
- con = &conopt[i];
-
- if (con->name[0]) {
- if (!strncmp(str, con->name, namelen))
- return 0;
- continue;
- }
-
- /*
- * The name isn't terminated, only opt is. Empty opt is fine,
- * but brl_opt can be either empty or NULL. For more info, see
- * _braille_console_setup().
- */
- strscpy(con->name, str, namelen + 1);
- strscpy(con->opt, opt, CONSOLE_OPT_MAX);
- if (brl_opt) {
- strscpy(con->brl_opt, brl_opt, CONSOLE_BRL_OPT_MAX);
- con->has_brl_opt = 1;
- }
-
- return 0;
- }
-
- return -ENOMEM;
-}
-
-static struct console_option *console_opt_find(const char *name)
-{
- struct console_option *con;
- int i;
-
- for (i = 0; i < MAX_CMDLINECONSOLES; i++) {
- con = &conopt[i];
- if (!strcmp(name, con->name))
- return con;
- }
-
- return NULL;
-}
-
-/**
- * add_preferred_console_match - Adds a preferred console if a match is found
- * @match: Expected console on kernel command line, such as console=DEVNAME:0.0
- * @name: Name of the console character device to add such as ttyS
- * @idx: Index for the console
- *
- * Allows driver subsystems to add a console after translating the command
- * line name to the character device name used for the console. Options are
- * added automatically based on the kernel command line. Duplicate preferred
- * consoles are ignored by __add_preferred_console().
- *
- * Return: 0 on success, negative error code on failure.
- */
-int add_preferred_console_match(const char *match, const char *name,
- const short idx)
-{
- struct console_option *con;
- char *brl_opt = NULL;
-
- if (!match || !strlen(match) || !name || !strlen(name) ||
- idx < 0)
- return -EINVAL;
-
- con = console_opt_find(match);
- if (!con)
- return -ENOENT;
-
- /*
- * See __add_preferred_console(). It checks for NULL brl_options to set
- * the preferred_console flag. Empty brl_opt instead of NULL leads into
- * the preferred_console flag not set, and CON_CONSDEV not being set,
- * and the boot console won't get disabled at the end of console_setup().
- */
- if (con->has_brl_opt)
- brl_opt = con->brl_opt;
-
- console_opt_add_preferred_console(name, idx, con->opt, brl_opt);
-
- return 0;
-}
diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h
index a125e0235589..3ca74ad391d6 100644
--- a/kernel/printk/console_cmdline.h
+++ b/kernel/printk/console_cmdline.h
@@ -2,12 +2,6 @@
#ifndef _CONSOLE_CMDLINE_H
#define _CONSOLE_CMDLINE_H

-#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);
-
struct console_cmdline
{
char name[16]; /* Name of the driver */
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 420fd310129d..dddb15f48d59 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -383,6 +383,9 @@ static int console_locked;
/*
* Array of consoles built from command line options (console=)
*/
+
+#define MAX_CMDLINECONSOLES 8
+
static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];

static int preferred_console = -1;
@@ -2500,17 +2503,6 @@ static int __init console_setup(char *str)
if (_braille_console_setup(&str, &brl_options))
return 1;

- /* Save the console for driver subsystem use */
- if (console_opt_save(str, brl_options))
- return 1;
-
- /* Flag register_console() to not call try_enable_default_console() */
- console_set_on_cmdline = 1;
-
- /* Don't attempt to parse a DEVNAME:0.0 style console */
- if (strchr(str, ':'))
- return 1;
-
/*
* Decode str into name, index, options.
*/
@@ -2541,13 +2533,6 @@ 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)
-{
- return __add_preferred_console(name, idx, options, brl_options, true);
-}
-
/**
* add_preferred_console - add a device to the list of preferred consoles.
* @name: device name
@@ -3522,7 +3507,7 @@ void register_console(struct console *newcon)
* Note that a console with tty binding will have CON_CONSDEV
* flag set and will be first in the list.
*/
- if (preferred_console < 0 && !console_set_on_cmdline) {
+ if (preferred_console < 0) {
if (hlist_empty(&console_list) || !console_first()->device ||
console_first()->flags & CON_BOOT) {
try_enable_default_console(newcon);
--
2.45.2


2024-06-13 12:52:35

by Tony Lindgren

[permalink] [raw]
Subject: [PATCH v2 2/2] printk: Add update_preferred_console()

Let's add update_preferred_console() for driver subsystems to call during
init when the console is ready, and it's character device name is known.
For now, we use it only for the serial layer to allow console=DEVNAME:0.0
style hardware based addressing for consoles.

The earlier attempt on doing this caused a regression with the kernel
command line console order as it added calling __add_preferred_console()
again later on during init. A better approach was suggested by Petr where
we add the deferred console to the console_cmdline[] and update it later
on when the console is ready.

Suggested-by: Petr Mladek <[email protected]>
Co-developed-by: Petr Mladek <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
drivers/tty/serial/serial_base_bus.c | 8 ++-
include/linux/printk.h | 2 +
kernel/printk/console_cmdline.h | 1 +
kernel/printk/printk.c | 77 +++++++++++++++++++++++-----
4 files changed, 73 insertions(+), 15 deletions(-)

diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
index 5ebacb982f9e..a34f55ef6f37 100644
--- a/drivers/tty/serial/serial_base_bus.c
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -210,7 +210,13 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev)
static int serial_base_add_one_prefcon(const char *match, const char *dev_name,
int port_id)
{
- return 0;
+ int ret;
+
+ ret = update_preferred_console(match, dev_name, port_id);
+ if (ret == -ENOENT)
+ return 0;
+
+ return ret;
}

#ifdef __sparc__
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 65c5184470f1..fda935280e33 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -60,6 +60,8 @@ static inline const char *printk_skip_headers(const char *buffer)
#define CONSOLE_LOGLEVEL_DEFAULT CONFIG_CONSOLE_LOGLEVEL_DEFAULT
#define CONSOLE_LOGLEVEL_QUIET CONFIG_CONSOLE_LOGLEVEL_QUIET

+int update_preferred_console(const char *match, const char *name, const short idx);
+
extern int console_printk[];

#define console_loglevel (console_printk[0])
diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h
index 3ca74ad391d6..0ab573b6d4dc 100644
--- a/kernel/printk/console_cmdline.h
+++ b/kernel/printk/console_cmdline.h
@@ -6,6 +6,7 @@ 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 dddb15f48d59..0e2e442593bd 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2429,18 +2429,23 @@ static void set_user_specified(struct console_cmdline *c, bool user_specified)
console_set_on_cmdline = 1;
}

-static int __add_preferred_console(const char *name, const short idx, char *options,
+static int __add_preferred_console(const char *name, const short idx,
+ const char *devname, char *options,
char *brl_options, bool user_specified)
{
struct console_cmdline *c;
int i;

+ if (!name && !devname)
+ return -EINVAL;
+
/*
* We use a signed short index for struct console for device drivers to
* indicate a not yet assigned index or port. However, a negative index
- * value is not valid for preferred console.
+ * value is not valid when the console name and index are defined on
+ * the command line.
*/
- if (idx < 0)
+ if (name && idx < 0)
return -EINVAL;

/*
@@ -2448,9 +2453,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 ((name && strcmp(c->name, name) == 0 && c->index == idx) ||
+ (devname && strcmp(c->devname, devname) == 0)) {
if (!brl_options)
preferred_console = i;
set_user_specified(c, user_specified);
@@ -2461,7 +2467,10 @@ static int __add_preferred_console(const char *name, const short idx, char *opti
return -E2BIG;
if (!brl_options)
preferred_console = i;
- strscpy(c->name, name, sizeof(c->name));
+ if (name)
+ strscpy(c->name, name);
+ if (devname)
+ strscpy(c->devname, devname);
c->options = options;
set_user_specified(c, user_specified);
braille_set_options(c, brl_options);
@@ -2486,8 +2495,8 @@ __setup("console_msg_format=", console_msg_format_setup);
*/
static int __init console_setup(char *str)
{
- char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for "ttyS" */
- char *s, *options, *brl_options = NULL;
+ char buf[sizeof(console_cmdline[0].devname)]; /* name with "ttyS" prefix or devname */
+ char *s, *options, *brl_options = NULL, *chardev = NULL, *devname = NULL;
int idx;

/*
@@ -2496,17 +2505,23 @@ static int __init console_setup(char *str)
* for exactly this purpose.
*/
if (str[0] == 0 || strcmp(str, "null") == 0) {
- __add_preferred_console("ttynull", 0, NULL, NULL, true);
+ __add_preferred_console("ttynull", 0, NULL, NULL, NULL, true);
return 1;
}

if (_braille_console_setup(&str, &brl_options))
return 1;

+ /* For a DEVNAME:0.0 style console the character device is unknown early */
+ if (strchr(str, ':'))
+ devname = buf;
+ else
+ chardev = buf;
+
/*
* Decode str into name, index, options.
*/
- if (isdigit(str[0]))
+ if (chardev && isdigit(str[0]))
scnprintf(buf, sizeof(buf), "ttyS%s", str);
else
strscpy(buf, str);
@@ -2523,12 +2538,12 @@ static int __init console_setup(char *str)
#endif

for (s = buf; *s; s++)
- if (isdigit(*s) || *s == ',')
+ if ((chardev && isdigit(*s)) || *s == ',')
break;
idx = simple_strtoul(s, NULL, 10);
*s = 0;

- __add_preferred_console(buf, idx, options, brl_options, true);
+ __add_preferred_console(chardev, idx, devname, options, brl_options, true);
return 1;
}
__setup("console=", console_setup);
@@ -2548,7 +2563,38 @@ __setup("console=", console_setup);
*/
int add_preferred_console(const char *name, const short idx, char *options)
{
- return __add_preferred_console(name, idx, options, NULL, false);
+ return __add_preferred_console(name, idx, NULL, options, NULL, false);
+}
+
+/**
+ * update_preferred_console - Updates a preferred console if a match is found
+ * @devname: Expected console on kernel command line, such as console=DEVNAME:0.0
+ * @name: Name of the console character device to add such as ttyS
+ * @idx: Index for the console
+ *
+ * Allows driver subsystems to update a console after translating the command
+ * line name to the character device name used for the console.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int update_preferred_console(const char *devname, const char *name, const short idx)
+{
+ struct console_cmdline *c = console_cmdline;
+ int i;
+
+ if (!devname || !strlen(devname) || !name || !strlen(name) || idx < 0)
+ return -EINVAL;
+
+ for (i = 0; i < MAX_CMDLINECONSOLES && (c->name[0] || c->devname[0]);
+ i++, c++) {
+ if (!strcmp(devname, c->devname)) {
+ strscpy(c->name, name);
+ c->index = idx;
+ return 0;
+ }
+ }
+
+ return -ENOENT;
}

bool console_suspend_enabled = true;
@@ -3318,8 +3364,11 @@ 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++) {
+ /* Console not yet initialized? */
+ if (!c->name[0])
+ continue;
if (c->user_specified != user_specified)
continue;
if (!newcon->match ||
--
2.45.2


2024-06-14 17:46:38

by Petr Mladek

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] printk: Revert add_preferred_console_match() related commits

On Thu 2024-06-13 15:51:07, Tony Lindgren wrote:
> 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, I added a call to
> __add_preferred_console() later on when the console is ready.
>
> To fix the issue, let's revert the printk related commits:
>
> f03e8c1060f8 ("printk: Save console options for add_preferred_console_match()")
> b73c9cbe4f1f ("printk: Flag register_console() if console is set on command line")
> 8a831c584e6e ("printk: Don't try to parse DEVNAME:0.0 console options")
>
> We need to also drop the call for add_preferred_console_match() from
> serial_base_add_one_prefcon() added by commit 787a1cabac01 ("serial: core:
> Add support for DEVNAME:0.0 style naming for kernel console").
>
> Petr has suggested a better way to handle the deferred consoles that does
> not rely on calling __add_preferred_console() again.
>
> Reported-by: Petr Mladek <[email protected]>
> Link: https://lore.kernel.org/linux-serial/[email protected]/
> Fixes: f03e8c1060f8 ("printk: Save console options for add_preferred_console_match()")
> Signed-off-by: Tony Lindgren <[email protected]>

It seems that it really reverts the right parts.

Reviewed-by: Petr Mladek <[email protected]>

Best Regards,
Petr

2024-06-14 17:47:22

by Petr Mladek

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] printk: Add update_preferred_console()

On Thu 2024-06-13 15:51:08, Tony Lindgren wrote:
> Let's add update_preferred_console() for driver subsystems to call during
> init when the console is ready, and it's character device name is known.
> For now, we use it only for the serial layer to allow console=DEVNAME:0.0
> style hardware based addressing for consoles.
>
> The earlier attempt on doing this caused a regression with the kernel
> command line console order as it added calling __add_preferred_console()
> again later on during init. A better approach was suggested by Petr where
> we add the deferred console to the console_cmdline[] and update it later
> on when the console is ready.

The patch seems to work well. And I am surprised that it is so small ;-)
I have some rather cosmetic comments.

> diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
> index 5ebacb982f9e..a34f55ef6f37 100644
> --- a/drivers/tty/serial/serial_base_bus.c
> +++ b/drivers/tty/serial/serial_base_bus.c
> @@ -210,7 +210,13 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev)
> static int serial_base_add_one_prefcon(const char *match, const char *dev_name,
> int port_id)

I would suggest to rename also functions on the serial_base side.
The function is not adding prefcon. It is doing some match_and_update
job.

> {
> - return 0;
> + int ret;
> +
> + ret = update_preferred_console(match, dev_name, port_id);
> + if (ret == -ENOENT)
> + return 0;
> +
> + return ret;
> }
>
> #ifdef __sparc__
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -2486,8 +2495,8 @@ __setup("console_msg_format=", console_msg_format_setup);
> */
> static int __init console_setup(char *str)
> {
> - char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for "ttyS" */
> - char *s, *options, *brl_options = NULL;

I would add

static_assert(sizeof(console_cmdline[0].devname) >= sizeof(console_cmdline[0].name));

> + char buf[sizeof(console_cmdline[0].devname)]; /* name with "ttyS" prefix or devname */
> + char *s, *options, *brl_options = NULL, *chardev = NULL, *devname = NULL;

The name "chardev" sounds as generic as "devname". I would use one of

+ "name" like the parameter in __add_preferred_console
+ "ttyname" as it is mostly used for "tty*" console names
+ "conname" like a name in struct console.

Also please split the variables per-line so that future diff's are
easier to follow. Something like:

static_assert(sizeof(console_cmdline[0].devname) >= sizeof(console_cmdline[0].name));
char buf[sizeof(console_cmdline[0].devname)];
char *brl_options = NULL;
char *ttyname = NULL;
char *devname = NULL;
char *options;
char *s;
int idx;

> int idx;
>
> /*
> @@ -2496,17 +2505,23 @@ static int __init console_setup(char *str)
> * for exactly this purpose.
> */
> if (str[0] == 0 || strcmp(str, "null") == 0) {
> - __add_preferred_console("ttynull", 0, NULL, NULL, true);
> + __add_preferred_console("ttynull", 0, NULL, NULL, NULL, true);
> return 1;
> }
>
> if (_braille_console_setup(&str, &brl_options))
> return 1;
>
> + /* For a DEVNAME:0.0 style console the character device is unknown early */
> + if (strchr(str, ':'))
> + devname = buf;
> + else
> + chardev = buf;
> +
> /*
> * Decode str into name, index, options.
> */
> - if (isdigit(str[0]))
> + if (chardev && isdigit(str[0]))
> scnprintf(buf, sizeof(buf), "ttyS%s", str);
> else
> strscpy(buf, str);
> @@ -2523,12 +2538,12 @@ static int __init console_setup(char *str)
> #endif
>
> for (s = buf; *s; s++)
> - if (isdigit(*s) || *s == ',')
> + if ((chardev && isdigit(*s)) || *s == ',')
> break;
> idx = simple_strtoul(s, NULL, 10);

The @idx value is not really important when @devname is used.
But it still would be more clear to set it to -1.

/* @idx will get defined when devname matches. */
if (devname)
idx=-1;
else
idx = simple_strtoul(s, NULL, 10);

> *s = 0;
>
> - __add_preferred_console(buf, idx, options, brl_options, true);
> + __add_preferred_console(chardev, idx, devname, options, brl_options, true);
> return 1;
> }
> __setup("console=", console_setup);
> @@ -2548,7 +2563,38 @@ __setup("console=", console_setup);
> */
> int add_preferred_console(const char *name, const short idx, char *options)
> {
> - return __add_preferred_console(name, idx, options, NULL, false);
> + return __add_preferred_console(name, idx, NULL, options, NULL, false);
> +}
> +
> +/**
> + * update_preferred_console - Updates a preferred console if a match is found
> + * @devname: Expected console on kernel command line, such as console=DEVNAME:0.0
> + * @name: Name of the console character device to add such as ttyS
> + * @idx: Index for the console
> + *
> + * Allows driver subsystems to update a console after translating the command
> + * line name to the character device name used for the console.

I am not sure what the above sentence exactly means.
IMHO, the character device is too generic term.

> + *
> + * Return: 0 on success, negative error code on failure.
> + */

My proposal might be kind of naive. Some people might say that
it describes obvious things. But the API is for device driver
users which do not know much about how printk handles
the console command line and the registration.

<proposal>
/**
* match_devname_and_update_preferred_console - Update a preferred console
* when matching devname is found.
* @devname: DEVNAME:0.0 style device name
* @name: Name of the corresponding console driver, e.g. "ttyS"
* @idx: Console index, e.g. port number.
*
* The function checks whether a device with the given @devname is
* preferred via the console=DEVNAME:0.0 command line option.
* It fills the missing console driver name and console index
* so that a later register_console() call could find (match)
* and enable this device.
*
* It might be used when a driver subsystem initializes particular
* devices with already known DEVNAME:0.0 style names. And it
* could predict which console driver name and index this device
* would later get associated with.
*
* Return: 0 on success, negative error code on failure.
*/
</proposal>

At least, this is my understanding of how this works.

I do not know the whole history. And maybe I get something wrong.
IMHO, the main problem is that the printk console code
historically uses TTY device names. But we want to register/enable
the consoles ASAP when the HW devices are ready for writing().
It happens before the TTY subsystem gets initialized so
that we could not use the sysfs kobjects for matching
the tty device driver names with HW device driver names.
And we need this kind of hacks.

But maybe I do not have the right picture about the initialization
and the names of the pieces.


> +int update_preferred_console(const char *devname, const char *name, const short idx)
> +{
> + struct console_cmdline *c = console_cmdline;
> + int i;
> +
> + if (!devname || !strlen(devname) || !name || !strlen(name) || idx < 0)
> + return -EINVAL;
> +
> + for (i = 0; i < MAX_CMDLINECONSOLES && (c->name[0] || c->devname[0]);
> + i++, c++) {
> + if (!strcmp(devname, c->devname)) {

I would add here:

pr_info("associate the preferred console \"%s\" with \"%s%d\"\n",
devname, name, idx);

> + strscpy(c->name, name);
> + c->index = idx;
> + return 0;
> + }
> + }
> +
> + return -ENOENT;
> }
>
> bool console_suspend_enabled = true;

Best Regards,
Petr