Hi all,
With the recent serial core changes, we can now add DEVNAME:0.0 style
addressing for the serial ports. When using DEVNAME:0.0 naming, we don't
need to care which ttyS instance number is allocated depending on HSUART
settings or if the devicetree has added aliases for all the ports.
We also prepare the serial core to handle the ttyS related quirks done
in console_setup() to prepare things for eventually dropping the parsing
from console_setup().
Regards,
Tony
Changes since v6:
- Simplify console_opt_save() and add Co-developed-by for Andy
- Add patch to document new console option as suggested by Sebastian
- Init __free() as noted by Andy and the kernel test robot
- Use strstarts() for the boolean test and drop redundant assignment
as noted by Andy
Changes since v5:
- Include types.h as noted by Andy
- Update patch description to use serial_base_add_one_prefcon() as noded
by Andy
Changes since v4:
- Fix passing empty brl_opt to add_preferred_console() instead of NULL
- Fix console_setup() trying to parse a console index out of a DEVNAME:0.0
option that can be an IO address instead of an index
- Fix issue with default console getting enabled before DEVNAME:0.0
handling device driver calls add_preferred_console()
- Fix error check for kasprintf(), use serial_base_add_one_prefcon() and
coding suggestions by Andy
- Fixed compile errors if console is not enabled in .config as noted
by kernel test robot
Changes since v3:
- Fix handling of brl_options by saving them too in console_opt_save()
- Drop changes to remove console_setup() parsing, further changes to
register_console() are still needed before can leave out the early
parsing
- Added a patch for serial8250_isa_init_preferred_console(), otherwise
the console gets initialized later for x86 when the console_setup()
parsing is dropped as pointed out by Petr
- Initialize __free() variables to NULL as noted by Dan
- Return handled in console_setup() if saving console options fails
- Simplify serial_base_add_preferred_console() and add a helper for
serial_base_add_one_prefcon() as suggested by Andy
- Header include changes to kernel/printk/conopt.c as suggested by Andy
Changes since v2:
- Console name got constified and already applied as suggested by Ilpo
and Andy
- Add printk/conopt.c to save console command line options
- Add a patch to drop old console_setup() character device name parsing
- Use cleanup.h to simplify freeing as suggested by Andy
- Use types.h instead of kernel.h as suggested by Andy
- Use strcspn() as suggested by Andy
- Various coding improvments suggested by Andy
Changes since v1:
- Constify printk add_preferred_console() as suggested by Jiri
- Use proper kernel command line helpers for parsing console as
suggested by Jiri
- Update description for HSUART based on Andy's comments
- Standardize on DEVNAME:0.0 style naming as suggested by Andy
- Added missing put_device() calls paired with device_find_child()
Tony Lindgren (7):
printk: Save console options for add_preferred_console_match()
printk: Don't try to parse DEVNAME:0.0 console options
printk: Flag register_console() if console is set on command line
serial: core: Add support for DEVNAME:0.0 style naming for kernel
console
serial: core: Handle serial console options
serial: 8250: Add preferred console in serial8250_isa_init_ports()
Documentation: kernel-parameters: Add DEVNAME:0.0 format for serial
ports
.../admin-guide/kernel-parameters.txt | 19 +++
drivers/tty/serial/8250/8250_core.c | 5 +
drivers/tty/serial/serial_base.h | 24 +++
drivers/tty/serial/serial_base_bus.c | 136 ++++++++++++++++
drivers/tty/serial/serial_core.c | 4 +
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 ++-
10 files changed, 363 insertions(+), 5 deletions(-)
create mode 100644 kernel/printk/conopt.c
--
2.44.0
If add_preferred_console() is not called early in setup_console(), we can
end up having register_console() call try_enable_default_console() before a
console device has called add_preferred_console().
Let's set console_set_on_cmdline flag in console_setup() to prevent this
from happening.
Signed-off-by: Tony Lindgren <[email protected]>
---
kernel/printk/printk.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2498,6 +2498,9 @@ static int __init console_setup(char *str)
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;
@@ -3501,7 +3504,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) {
+ if (preferred_console < 0 && !console_set_on_cmdline) {
if (hlist_empty(&console_list) || !console_first()->device ||
console_first()->flags & CON_BOOT) {
try_enable_default_console(newcon);
--
2.44.0
In order to start moving the serial console quirks out of console_setup(),
let's add parsing for the quirks to the serial core layer. We can use
serial_base_add_one_prefcon() to handle the quirks.
Note that eventually we may want to set up driver specific console quirk
handling for the serial port device drivers to use. But we need to figure
out which driver(s) need to call the quirk. So for now, we just handle the
sparc quirk directly.
Signed-off-by: Tony Lindgren <[email protected]>
---
drivers/tty/serial/serial_base_bus.c | 49 ++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
--- a/drivers/tty/serial/serial_base_bus.c
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -219,9 +219,58 @@ static int serial_base_add_one_prefcon(const char *match, const char *dev_name,
return ret;
}
+#ifdef __sparc__
+
+/* Handle Sparc ttya and ttyb options as done in console_setup() */
+static int serial_base_add_sparc_console(const char *dev_name, int idx)
+{
+ const char *name;
+
+ switch (idx) {
+ case 0:
+ name = "ttya";
+ break;
+ case 1:
+ name = "ttyb";
+ break;
+ default:
+ return 0;
+ }
+
+ return serial_base_add_one_prefcon(name, dev_name, idx);
+}
+
+#else
+
+static inline int serial_base_add_sparc_console(const char *dev_name, int idx)
+{
+ return 0;
+}
+
+#endif
+
static int serial_base_add_prefcon(const char *name, int idx)
{
const char *char_match __free(kfree) = NULL;
+ const char *nmbr_match __free(kfree) = NULL;
+ int ret;
+
+ /* Handle ttyS specific options */
+ if (strstarts(name, "ttyS")) {
+ /* No name, just a number */
+ nmbr_match = kasprintf(GFP_KERNEL, "%i", idx);
+ if (!nmbr_match)
+ return -ENODEV;
+
+ ret = serial_base_add_one_prefcon(nmbr_match, name, idx);
+ if (ret)
+ return ret;
+
+ /* Sparc ttya and ttyb */
+ ret = serial_base_add_sparc_console(name, idx);
+ if (ret)
+ return ret;
+ }
/* Handle the traditional character device name style console=ttyS0 */
char_match = kasprintf(GFP_KERNEL, "%s%i", name, idx);
--
2.44.0
Currently console_setup() tries to make a console index out of any digits
passed in the kernel command line for console. In the DEVNAME:0.0 case,
the name can contain a device IO address, so bail out on console names
with a ':'.
Signed-off-by: Tony Lindgren <[email protected]>
---
kernel/printk/printk.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2498,6 +2498,10 @@ static int __init console_setup(char *str)
if (console_opt_save(str, brl_options))
return 1;
+ /* Don't attempt to parse a DEVNAME:0.0 style console */
+ if (strchr(str, ':'))
+ return 1;
+
/*
* Decode str into name, index, options.
*/
--
2.44.0
Driver subsystems may need to translate the preferred console name to the
character device name used. We already do some of this in console_setup()
with a few hardcoded names, but that does not scale well.
The console options are parsed early in console_setup(), and the consoles
are added with __add_preferred_console(). At this point we don't know much
about the character device names and device drivers getting probed.
To allow driver subsystems to set up a preferred console, let's save the
kernel command line console options. To add a preferred console from a
driver subsystem with optional character device name translation, let's
add a new function add_preferred_console_match().
This allows the serial core layer to support console=DEVNAME:0.0 style
hardware based addressing in addition to the current console=ttyS0 style
naming. And we can start moving console_setup() character device parsing
to the driver subsystem specific code.
We use a separate array from the console_cmdline array as the character
device name and index may be unknown at the console_setup() time. And
eventually there's no need to call __add_preferred_console() until the
subsystem is ready to handle the console.
Adding the console name in addition to the character device name, and a
flag for an added console, could be added to the struct console_cmdline.
And the console_cmdline array handling could be modified accordingly. But
that complicates things compared saving the console options, and then
adding the consoles when the subsystems handling the consoles are ready.
Co-developed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
include/linux/printk.h | 3 +
kernel/printk/Makefile | 2 +-
kernel/printk/conopt.c | 146 ++++++++++++++++++++++++++++++++
kernel/printk/console_cmdline.h | 6 ++
kernel/printk/printk.c | 14 ++-
5 files changed, 167 insertions(+), 4 deletions(-)
create mode 100644 kernel/printk/conopt.c
diff --git a/include/linux/printk.h b/include/linux/printk.h
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -60,6 +60,9 @@ 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
--- a/kernel/printk/Makefile
+++ b/kernel/printk/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y = printk.o
+obj-y = printk.o conopt.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
new file mode 100644
--- /dev/null
+++ b/kernel/printk/conopt.c
@@ -0,0 +1,146 @@
+// 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
--- a/kernel/printk/console_cmdline.h
+++ b/kernel/printk/console_cmdline.h
@@ -2,6 +2,12 @@
#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
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -383,9 +383,6 @@ 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;
@@ -2497,6 +2494,10 @@ 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;
+
/*
* Decode str into name, index, options.
*/
@@ -2527,6 +2528,13 @@ 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
--
2.44.0
Prepare 8250 ISA ports to drop kernel command line serial console
handling from console_setup().
We need to set the preferred console in serial8250_isa_init_ports()
to drop a dependency to setup_console() handling the ttyS related
quirks. Otherwise when console_setup() handles the ttyS related
options, console gets enabled only at driver probe time.
Note that this mostly affects x86 as this happens based on define
SERIAL_PORT_DFNS.
Signed-off-by: Tony Lindgren <[email protected]>
---
drivers/tty/serial/8250/8250_core.c | 5 +++++
drivers/tty/serial/serial_base.h | 8 ++++++++
drivers/tty/serial/serial_base_bus.c | 21 +++++++++++++++++++++
3 files changed, 34 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -15,6 +15,7 @@
*/
#include <linux/acpi.h>
+#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
@@ -41,6 +42,8 @@
#include <asm/irq.h>
+#include "../serial_base.h" /* For serial_base_add_isa_preferred_console() */
+
#include "8250.h"
/*
@@ -563,6 +566,8 @@ static void __init serial8250_isa_init_ports(void)
port->irqflags |= irqflag;
if (serial8250_isa_config != NULL)
serial8250_isa_config(i, &up->port, &up->capabilities);
+
+ serial_base_add_isa_preferred_console(serial8250_reg.dev_name, i);
}
}
diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h
--- a/drivers/tty/serial/serial_base.h
+++ b/drivers/tty/serial/serial_base.h
@@ -51,6 +51,8 @@ void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port
int serial_base_add_preferred_console(struct uart_driver *drv,
struct uart_port *port);
+int serial_base_add_isa_preferred_console(const char *name, int idx);
+
#else
static inline
@@ -60,4 +62,10 @@ int serial_base_add_preferred_console(struct uart_driver *drv,
return 0;
}
+static inline
+int serial_base_add_isa_preferred_console(const char *name, int idx)
+{
+ return 0;
+}
+
#endif
diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
--- a/drivers/tty/serial/serial_base_bus.c
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -317,6 +317,27 @@ int serial_base_add_preferred_console(struct uart_driver *drv,
return serial_base_add_one_prefcon(port_match, drv->dev_name, port->line);
}
+#ifdef CONFIG_SERIAL_8250_CONSOLE
+
+/*
+ * Early ISA ports initialize the console before there is no struct device.
+ * This should be only called from serial8250_isa_init_preferred_console(),
+ * other callers are likely wrong and should rely on earlycon instead.
+ */
+int serial_base_add_isa_preferred_console(const char *name, int idx)
+{
+ return serial_base_add_prefcon(name, idx);
+}
+
+#else
+
+int serial_base_add_isa_preferred_console(const char *name, int idx)
+{
+ return 0;
+}
+
+#endif
+
#endif
static int serial_base_init(void)
--
2.44.0
Document the console option for DEVNAME:0.0 style addressing for serial
ports.
Suggested-by: Sebastian Reichel <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
---
.../admin-guide/kernel-parameters.txt | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -785,6 +785,25 @@
Documentation/networking/netconsole.rst for an
alternative.
+ <DEVNAME>:<n>.<n>[,options]
+ Use the specified serial port on the serial core bus.
+ The addressing uses DEVNAME of the physical serial port
+ device, followed by the serial core controller instance,
+ and the serial port instance. The options are the same
+ as documented for the ttyS addressing above.
+
+ The mapping of the serial ports to the tty instances
+ can be viewed with:
+
+ $ ls -d /sys/bus/serial-base/devices/*:*.*/tty/*
+ /sys/bus/serial-base/devices/00:04:0.0/tty/ttyS0
+
+ In the above example, the console can be addressed with
+ console=00:04:0.0. Note that a console addressed this
+ way will only get added when the related device driver
+ is ready. The use of an earlycon parameter in addition to
+ the console may be desired for console output early on.
+
uart[8250],io,<addr>[,options]
uart[8250],mmio,<addr>[,options]
uart[8250],mmio16,<addr>[,options]
--
2.44.0
On Wed, Mar 27, 2024 at 12:59:35PM +0200, Tony Lindgren wrote:
> Driver subsystems may need to translate the preferred console name to the
> character device name used. We already do some of this in console_setup()
> with a few hardcoded names, but that does not scale well.
>
> The console options are parsed early in console_setup(), and the consoles
> are added with __add_preferred_console(). At this point we don't know much
> about the character device names and device drivers getting probed.
>
> To allow driver subsystems to set up a preferred console, let's save the
> kernel command line console options. To add a preferred console from a
> driver subsystem with optional character device name translation, let's
> add a new function add_preferred_console_match().
>
> This allows the serial core layer to support console=DEVNAME:0.0 style
> hardware based addressing in addition to the current console=ttyS0 style
> naming. And we can start moving console_setup() character device parsing
> to the driver subsystem specific code.
>
> We use a separate array from the console_cmdline array as the character
> device name and index may be unknown at the console_setup() time. And
> eventually there's no need to call __add_preferred_console() until the
> subsystem is ready to handle the console.
>
> Adding the console name in addition to the character device name, and a
> flag for an added console, could be added to the struct console_cmdline.
> And the console_cmdline array handling could be modified accordingly. But
> that complicates things compared saving the console options, and then
> adding the consoles when the subsystems handling the consoles are ready.
>
> Co-developed-by: Andy Shevchenko <[email protected]>
This requires my SoB as well.
Signed-off-by: Andy Shevchenko <[email protected]>
> Signed-off-by: Tony Lindgren <[email protected]>
--
With Best Regards,
Andy Shevchenko
We can now add hardware based addressing for serial ports. Starting with
commit 84a9582fd203 ("serial: core: Start managing serial controllers to
enable runtime PM"), and all the related fixes to this commit, the serial
core now knows to which serial port controller the ports are connected.
The serial ports can be addressed with DEVNAME:0.0 style naming. The names
are something like 00:04:0.0 for a serial port on qemu, and something like
2800000.serial:0.0 on platform device using systems like ARM64 for example.
The DEVNAME is the unique serial port hardware controller device name, AKA
the name for port->dev. The 0.0 are the serial core controller id and port
id.
Typically 0.0 are used for each controller and port instance unless the
serial port hardware controller has multiple controllers or ports.
Using DEVNAME:0.0 style naming actually solves two long term issues for
addressing the serial ports:
1. According to Andy Shevchenko, using DEVNAME:0.0 style naming fixes an
issue where depending on the BIOS settings, the kernel serial port ttyS
instance number may change if HSUART is enabled
2. Device tree using architectures no longer necessarily need to specify
aliases to find a specific serial port, and we can just allocate the
ttyS instance numbers dynamically in whatever probe order
To do this, let's match the hardware addressing style console name to
the character device name used, and add a preferred console using the
character device name.
Note that when using console=DEVNAME:0.0 style kernel command line, the
8250 serial console gets enabled later compared to using console=ttyS
naming for ISA ports. This is because the serial port DEVNAME to character
device mapping is not known until the serial driver probe time. If used
together with earlycon, this issue is avoided.
Signed-off-by: Tony Lindgren <[email protected]>
---
drivers/tty/serial/serial_base.h | 16 +++++++
drivers/tty/serial/serial_base_bus.c | 66 ++++++++++++++++++++++++++++
drivers/tty/serial/serial_core.c | 4 ++
3 files changed, 86 insertions(+)
diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h
--- a/drivers/tty/serial/serial_base.h
+++ b/drivers/tty/serial/serial_base.h
@@ -45,3 +45,19 @@ void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port
int serial_core_register_port(struct uart_driver *drv, struct uart_port *port);
void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port);
+
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+
+int serial_base_add_preferred_console(struct uart_driver *drv,
+ struct uart_port *port);
+
+#else
+
+static inline
+int serial_base_add_preferred_console(struct uart_driver *drv,
+ struct uart_port *port)
+{
+ return 0;
+}
+
+#endif
diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
--- a/drivers/tty/serial/serial_base_bus.c
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -8,6 +8,7 @@
* The serial core bus manages the serial core controller instances.
*/
+#include <linux/cleanup.h>
#include <linux/container_of.h>
#include <linux/device.h>
#include <linux/idr.h>
@@ -204,6 +205,71 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev)
put_device(&port_dev->dev);
}
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+
+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;
+}
+
+static int serial_base_add_prefcon(const char *name, int idx)
+{
+ const char *char_match __free(kfree) = NULL;
+
+ /* Handle the traditional character device name style console=ttyS0 */
+ char_match = kasprintf(GFP_KERNEL, "%s%i", name, idx);
+ if (!char_match)
+ return -ENOMEM;
+
+ return serial_base_add_one_prefcon(char_match, name, idx);
+}
+
+/**
+ * serial_base_add_preferred_console - Adds a preferred console
+ * @drv: Serial port device driver
+ * @port: Serial port instance
+ *
+ * Tries to add a preferred console for a serial port if specified in the
+ * kernel command line. Supports both the traditional character device such
+ * as console=ttyS0, and a hardware addressing based console=DEVNAME:0.0
+ * style name.
+ *
+ * Translates the kernel command line option using a hardware based addressing
+ * console=DEVNAME:0.0 to the serial port character device such as ttyS0.
+ * Cannot be called early for ISA ports, depends on struct device.
+ *
+ * Note that duplicates are ignored by add_preferred_console().
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int serial_base_add_preferred_console(struct uart_driver *drv,
+ struct uart_port *port)
+{
+ const char *port_match __free(kfree) = NULL;
+ int ret;
+
+ ret = serial_base_add_prefcon(drv->dev_name, port->line);
+ if (ret)
+ return ret;
+
+ port_match = kasprintf(GFP_KERNEL, "%s:%i.%i", dev_name(port->dev),
+ port->ctrl_id, port->port_id);
+ if (!port_match)
+ return -ENOMEM;
+
+ /* Translate a hardware addressing style console=DEVNAME:0.0 */
+ return serial_base_add_one_prefcon(port_match, drv->dev_name, port->line);
+}
+
+#endif
+
static int serial_base_init(void)
{
int ret;
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -3407,6 +3407,10 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port)
if (ret)
goto err_unregister_ctrl_dev;
+ ret = serial_base_add_preferred_console(drv, port);
+ if (ret)
+ goto err_unregister_port_dev;
+
ret = serial_core_add_one_port(drv, port);
if (ret)
goto err_unregister_port_dev;
--
2.44.0
Hi Tony,
On Mar 27, 2024 at 12:59:41 +0200, Tony Lindgren wrote:
> Document the console option for DEVNAME:0.0 style addressing for serial
> ports.
Thanks, this will really add in more context for people unaware.
>
> Suggested-by: Sebastian Reichel <[email protected]>
> Signed-off-by: Tony Lindgren <[email protected]>
> ---
> .../admin-guide/kernel-parameters.txt | 19 +++++++++++++++++++
> 1 file changed, 19 insertions(+)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -785,6 +785,25 @@
> Documentation/networking/netconsole.rst for an
> alternative.
>
> + <DEVNAME>:<n>.<n>[,options]
> + Use the specified serial port on the serial core bus.
> + The addressing uses DEVNAME of the physical serial port
> + device, followed by the serial core controller instance,
> + and the serial port instance. The options are the same
> + as documented for the ttyS addressing above.
> +
> + The mapping of the serial ports to the tty instances
> + can be viewed with:
> +
> + $ ls -d /sys/bus/serial-base/devices/*:*.*/tty/*
> + /sys/bus/serial-base/devices/00:04:0.0/tty/ttyS0
> +
> + In the above example, the console can be addressed with
> + console=00:04:0.0. Note that a console addressed this
> + way will only get added when the related device driver
> + is ready. The use of an earlycon parameter in addition to
> + the console may be desired for console output early on.
Reviewed-by: Dhruva Gole <[email protected]>
--
Best regards,
Dhruva
Hi,
On Mar 27, 2024 at 12:59:38 +0200, Tony Lindgren wrote:
> We can now add hardware based addressing for serial ports. Starting with
> commit 84a9582fd203 ("serial: core: Start managing serial controllers to
> enable runtime PM"), and all the related fixes to this commit, the serial
> core now knows to which serial port controller the ports are connected.
>
> The serial ports can be addressed with DEVNAME:0.0 style naming. The names
> are something like 00:04:0.0 for a serial port on qemu, and something like
> 2800000.serial:0.0 on platform device using systems like ARM64 for example.
>
> The DEVNAME is the unique serial port hardware controller device name, AKA
> the name for port->dev. The 0.0 are the serial core controller id and port
> id.
>
> Typically 0.0 are used for each controller and port instance unless the
> serial port hardware controller has multiple controllers or ports.
>
> Using DEVNAME:0.0 style naming actually solves two long term issues for
> addressing the serial ports:
>
> 1. According to Andy Shevchenko, using DEVNAME:0.0 style naming fixes an
> issue where depending on the BIOS settings, the kernel serial port ttyS
> instance number may change if HSUART is enabled
>
> 2. Device tree using architectures no longer necessarily need to specify
> aliases to find a specific serial port, and we can just allocate the
This is GOOD!
> ttyS instance numbers dynamically in whatever probe order
>
> To do this, let's match the hardware addressing style console name to
> the character device name used, and add a preferred console using the
> character device name.
>
> Note that when using console=DEVNAME:0.0 style kernel command line, the
> 8250 serial console gets enabled later compared to using console=ttyS
> naming for ISA ports. This is because the serial port DEVNAME to character
> device mapping is not known until the serial driver probe time. If used
> together with earlycon, this issue is avoided.
>
> Signed-off-by: Tony Lindgren <[email protected]>
> ---
> drivers/tty/serial/serial_base.h | 16 +++++++
> drivers/tty/serial/serial_base_bus.c | 66 ++++++++++++++++++++++++++++
> drivers/tty/serial/serial_core.c | 4 ++
> 3 files changed, 86 insertions(+)
>
> diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h
> --- a/drivers/tty/serial/serial_base.h
> +++ b/drivers/tty/serial/serial_base.h
> @@ -45,3 +45,19 @@ void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port
>
> int serial_core_register_port(struct uart_driver *drv, struct uart_port *port);
> void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port);
> +
> +#ifdef CONFIG_SERIAL_CORE_CONSOLE
> +
> +int serial_base_add_preferred_console(struct uart_driver *drv,
> + struct uart_port *port);
> +
> +#else
> +
> +static inline
> +int serial_base_add_preferred_console(struct uart_driver *drv,
> + struct uart_port *port)
> +{
> + return 0;
> +}
> +
> +#endif
> diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
> --- a/drivers/tty/serial/serial_base_bus.c
> +++ b/drivers/tty/serial/serial_base_bus.c
> @@ -8,6 +8,7 @@
> * The serial core bus manages the serial core controller instances.
> */
>
> +#include <linux/cleanup.h>
> #include <linux/container_of.h>
> #include <linux/device.h>
> #include <linux/idr.h>
> @@ -204,6 +205,71 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev)
> put_device(&port_dev->dev);
> }
>
> +#ifdef CONFIG_SERIAL_CORE_CONSOLE
> +
> +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;
Can we do this instead?
return (ret == -ENOENT ? 0 : ret);
> +}
> +
> +static int serial_base_add_prefcon(const char *name, int idx)
> +{
> + const char *char_match __free(kfree) = NULL;
> +
> + /* Handle the traditional character device name style console=ttyS0 */
> + char_match = kasprintf(GFP_KERNEL, "%s%i", name, idx);
> + if (!char_match)
> + return -ENOMEM;
> +
> + return serial_base_add_one_prefcon(char_match, name, idx);
> +}
> +
> +/**
> + * serial_base_add_preferred_console - Adds a preferred console
> + * @drv: Serial port device driver
> + * @port: Serial port instance
> + *
> + * Tries to add a preferred console for a serial port if specified in the
> + * kernel command line. Supports both the traditional character device such
> + * as console=ttyS0, and a hardware addressing based console=DEVNAME:0.0
> + * style name.
> + *
> + * Translates the kernel command line option using a hardware based addressing
> + * console=DEVNAME:0.0 to the serial port character device such as ttyS0.
> + * Cannot be called early for ISA ports, depends on struct device.
> + *
> + * Note that duplicates are ignored by add_preferred_console().
> + *
> + * Return: 0 on success, negative error code on failure.
> + */
> +int serial_base_add_preferred_console(struct uart_driver *drv,
> + struct uart_port *port)
> +{
> + const char *port_match __free(kfree) = NULL;
> + int ret;
> +
> + ret = serial_base_add_prefcon(drv->dev_name, port->line);
> + if (ret)
> + return ret;
> +
> + port_match = kasprintf(GFP_KERNEL, "%s:%i.%i", dev_name(port->dev),
> + port->ctrl_id, port->port_id);
> + if (!port_match)
> + return -ENOMEM;
> +
> + /* Translate a hardware addressing style console=DEVNAME:0.0 */
> + return serial_base_add_one_prefcon(port_match, drv->dev_name, port->line);
> +}
> +
> +#endif
> +
> static int serial_base_init(void)
> {
> int ret;
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -3407,6 +3407,10 @@ int serial_core_register_port(struct uart_driver *drv, struct uart_port *port)
> if (ret)
> goto err_unregister_ctrl_dev;
>
> + ret = serial_base_add_preferred_console(drv, port);
> + if (ret)
> + goto err_unregister_port_dev;
> +
Looks okay otherwise,
Reviewed-by: Dhruva Gole <[email protected]>
--
Best regards,
Dhruva
On Thu, Mar 28, 2024 at 12:01:52PM +0530, Dhruva Gole wrote:
> Hi,
>
> On Mar 27, 2024 at 12:59:38 +0200, Tony Lindgren wrote:
> > We can now add hardware based addressing for serial ports. Starting with
> > commit 84a9582fd203 ("serial: core: Start managing serial controllers to
> > enable runtime PM"), and all the related fixes to this commit, the serial
> > core now knows to which serial port controller the ports are connected.
> >
> > The serial ports can be addressed with DEVNAME:0.0 style naming. The names
> > are something like 00:04:0.0 for a serial port on qemu, and something like
> > 2800000.serial:0.0 on platform device using systems like ARM64 for example.
> >
> > The DEVNAME is the unique serial port hardware controller device name, AKA
> > the name for port->dev. The 0.0 are the serial core controller id and port
> > id.
> >
> > Typically 0.0 are used for each controller and port instance unless the
> > serial port hardware controller has multiple controllers or ports.
> >
> > Using DEVNAME:0.0 style naming actually solves two long term issues for
> > addressing the serial ports:
> >
> > 1. According to Andy Shevchenko, using DEVNAME:0.0 style naming fixes an
> > issue where depending on the BIOS settings, the kernel serial port ttyS
> > instance number may change if HSUART is enabled
> >
> > 2. Device tree using architectures no longer necessarily need to specify
> > aliases to find a specific serial port, and we can just allocate the
>
> This is GOOD!
>
> > ttyS instance numbers dynamically in whatever probe order
> >
> > To do this, let's match the hardware addressing style console name to
> > the character device name used, and add a preferred console using the
> > character device name.
> >
> > Note that when using console=DEVNAME:0.0 style kernel command line, the
> > 8250 serial console gets enabled later compared to using console=ttyS
> > naming for ISA ports. This is because the serial port DEVNAME to character
> > device mapping is not known until the serial driver probe time. If used
> > together with earlycon, this issue is avoided.
> >
> > Signed-off-by: Tony Lindgren <[email protected]>
> > ---
> > drivers/tty/serial/serial_base.h | 16 +++++++
> > drivers/tty/serial/serial_base_bus.c | 66 ++++++++++++++++++++++++++++
> > drivers/tty/serial/serial_core.c | 4 ++
> > 3 files changed, 86 insertions(+)
> >
> > diff --git a/drivers/tty/serial/serial_base.h b/drivers/tty/serial/serial_base.h
> > --- a/drivers/tty/serial/serial_base.h
> > +++ b/drivers/tty/serial/serial_base.h
> > @@ -45,3 +45,19 @@ void serial_ctrl_unregister_port(struct uart_driver *drv, struct uart_port *port
> >
> > int serial_core_register_port(struct uart_driver *drv, struct uart_port *port);
> > void serial_core_unregister_port(struct uart_driver *drv, struct uart_port *port);
> > +
> > +#ifdef CONFIG_SERIAL_CORE_CONSOLE
> > +
> > +int serial_base_add_preferred_console(struct uart_driver *drv,
> > + struct uart_port *port);
> > +
> > +#else
> > +
> > +static inline
> > +int serial_base_add_preferred_console(struct uart_driver *drv,
> > + struct uart_port *port)
> > +{
> > + return 0;
> > +}
> > +
> > +#endif
> > diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
> > --- a/drivers/tty/serial/serial_base_bus.c
> > +++ b/drivers/tty/serial/serial_base_bus.c
> > @@ -8,6 +8,7 @@
> > * The serial core bus manages the serial core controller instances.
> > */
> >
> > +#include <linux/cleanup.h>
> > #include <linux/container_of.h>
> > #include <linux/device.h>
> > #include <linux/idr.h>
> > @@ -204,6 +205,71 @@ void serial_base_port_device_remove(struct serial_port_device *port_dev)
> > put_device(&port_dev->dev);
> > }
> >
> > +#ifdef CONFIG_SERIAL_CORE_CONSOLE
> > +
> > +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;
>
> Can we do this instead?
> return (ret == -ENOENT ? 0 : ret);
No, please no.
Just spell it out, like was done here, dealing with ? : is a pain to
read and follow and the generated code should be identical.
Only use ? : in places where it's the only way to do it (i.e. as
function parameters or in printk-like lines.)
Write for people first, compilers second.
thanks,
greg k-h
On Mar 28, 2024 at 08:22:26 +0100, Greg Kroah-Hartman wrote:
> On Thu, Mar 28, 2024 at 12:01:52PM +0530, Dhruva Gole wrote:
> > Hi,
> >
> > On Mar 27, 2024 at 12:59:38 +0200, Tony Lindgren wrote:
> > > We can now add hardware based addressing for serial ports. Starting with
> > > commit 84a9582fd203 ("serial: core: Start managing serial controllers to
> > > enable runtime PM"), and all the related fixes to this commit, the serial
> > > core now knows to which serial port controller the ports are connected.
[...]
> > >
> > > +#ifdef CONFIG_SERIAL_CORE_CONSOLE
> > > +
> > > +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;
> >
> > Can we do this instead?
> > return (ret == -ENOENT ? 0 : ret);
>
> No, please no.
>
> Just spell it out, like was done here, dealing with ? : is a pain to
> read and follow and the generated code should be identical.
>
> Only use ? : in places where it's the only way to do it (i.e. as
> function parameters or in printk-like lines.)
>
> Write for people first, compilers second.
Okay understood, I will keep this in mind from now on.
Thanks.
Tony,
Please feel free to take my R-by and ignore this suggestion as per
Greg's comments.
--
Best regards,
Dhruva
Hi,
I have finally found time to looks at this again.
On Wed 2024-03-27 12:59:35, Tony Lindgren wrote:
> Driver subsystems may need to translate the preferred console name to the
> character device name used. We already do some of this in console_setup()
> with a few hardcoded names, but that does not scale well.
>
> The console options are parsed early in console_setup(), and the consoles
> are added with __add_preferred_console(). At this point we don't know much
> about the character device names and device drivers getting probed.
>
> To allow driver subsystems to set up a preferred console, let's save the
> kernel command line console options. To add a preferred console from a
> driver subsystem with optional character device name translation, let's
> add a new function add_preferred_console_match().
>
> This allows the serial core layer to support console=DEVNAME:0.0 style
> hardware based addressing in addition to the current console=ttyS0 style
> naming. And we can start moving console_setup() character device parsing
> to the driver subsystem specific code.
>
> We use a separate array from the console_cmdline array as the character
> device name and index may be unknown at the console_setup() time. And
> eventually there's no need to call __add_preferred_console() until the
> subsystem is ready to handle the console.
>
> Adding the console name in addition to the character device name, and a
> flag for an added console, could be added to the struct console_cmdline.
> And the console_cmdline array handling could be modified accordingly. But
> that complicates things compared saving the console options, and then
> adding the consoles when the subsystems handling the consoles are ready.
Honestly, I think that the separate array was a bad decision.
It breaks the preferred console handling.
Also I wonder if this duplicates another matching.
Let me explain this in in more details.
First, about breaking the preferred console:
The patchset still causes the regression with /dev/console association
as already reported for v3, see
https://lore.kernel.org/r/ZWnvc6-LnXdjOQLY@alley
I used the following kernel command line:
earlycon=uart8250,io,0x3f8,115200 console=ttyS0,115200 console=tty0 ignore_loglevel log_buf_len=1M
The patchset caused that /dev/console became associated with
ttyS0 instead of tty0, see the "C" flag:
original # cat /proc/consoles
tty0 -WU (EC ) 4:1
ttyS0 -W- (E p a) 4:64
vs.
patched # cat /proc/consoles
ttyS0 -W- (EC p a) 4:64
tty0 -WU (E ) 4:1
I have added some debugging messages which nicely show the reason.
In the original code, __add_preferred_console() is called twice
when processing the command line:
[ 0.099312] __add_preferred_console[0]: ttyS, 0 (preferrred_console == 0)
[ 0.099982] __add_preferred_console[1]: tty, 0 (preferrred_console == 1)
The patchset causes that it is called once again here:
[ 0.216268] __add_preferred_console: Updating preferred console: ttyS, 0 [0]
[ 0.216271] task:swapper/0 state:R running task stack:0 pid:0 tgid:0 ppid:0 flags:0x00000000
[ 0.216318] Call Trace:
[ 0.216327] <TASK>
[ 0.216337] sched_show_task.part.0+0x1dd/0x1e7
[ 0.216355] __add_preferred_console.part.0.cold+0x29/0xa4
[ 0.216374] add_preferred_console_match+0x8e/0xb0
[ 0.216391] serial_base_add_prefcon+0x9c/0x140
[ 0.216408] serial8250_isa_init_ports+0x144/0x160
[ 0.216423] ? __pfx_univ8250_console_init+0x10/0x10
[ 0.216439] univ8250_console_init+0x1c/0x30
[ 0.216452] console_init+0x122/0x1c0
[ 0.216466] start_kernel+0x44a/0x590
[ 0.216480] x86_64_start_reservations+0x24/0x30
[ 0.216493] x86_64_start_kernel+0x90/0x90
[ 0.216506] common_startup_64+0x13e/0x141
[ 0.216528] </TASK>
This extra call tries to add "ttyS, 0" once again and it hits this
code:
static int __add_preferred_console(const char *name, const short idx, char *options,
char *brl_options, bool user_specified)
{
[...]
/*
* See if this tty is not yet registered, and
* if we have a slot free.
*/
for (i = 0, c = console_cmdline;
i < MAX_CMDLINECONSOLES && c->name[0];
i++, c++) {
if (strcmp(c->name, name) == 0 && c->index == idx) {
if (!brl_options)
----> preferred_console = i;
set_user_specified(c, user_specified);
return 0;
}
}
The code thinks that "ttyS0" has been mentioned on the command line
once again. And preferred_console is _wrongly_ set back to '0'.
My view:
The delayed __add_preferred_console() is a way to hell.
The preferences are defined by the ordering on the command line.
All entries have to be added when the command line options are
being proceed to keep the order.
A solution might be to store "devname" separately in
struct console_cmdline and allow empty "name". We could
implement then a function similar to
add_preferred_console_match() which would try to match
"devname" and set/update "name", "index" value when matched.
Note that we might also need to add some synchronization
if it might be possible to modify struct console_cmdline
in parallel.
Second, about the possible duplication:
I might get it wrong. IMHO, in principle, this patchset tries
to achieve similar thing as the "match()" callback, see
the commit c7cef0a84912cab3c9 ("console: Add extensible
console matching").
The .match() callback in struct console is to match, for example,
console=uart8250,io,0x3f8,115200 when the uart8250 driver
calls register_console() when it is being properly initialized
as "ttyS".
BTW: The .match() needs saved options because it internally calls
.setup() callback. IMHO, this is a very ugly detail
which complicates design of the register_console() code.
Both approaches try to match a "driver/device-specific name" with
the generic "ttySX".
console=uart8250,io,0x3f8,115200 => ttyS0
vs.
console=00:00:0.0,115200 => ttyS0
Where console=uart8250,io,0x3f8,115200 is handled by:
- "uart" is added to console_cmdline[]
- matched directly via newcon->match() callback
vs. console=00:00:0.0,115200
- 00:00:0.0 is added to conopt[]
- "ttyS0" added to console_cmdline[] when "00:00:0.0" initialized
- "ttyS0" is then matched directly
Question: Would it it be able to use the existing .match() callback
also to match the DEVNAME?
Or somehow reuse the approach?
Could register_console() know how to generate possible
DEVNAME for the given struct console?
Best Regards,
Petr
Hi,
On Fri, May 24, 2024 at 06:06:21PM +0200, Petr Mladek wrote:
> I have finally found time to looks at this again.
Great good to hear.
> First, about breaking the preferred console:
>
> The patchset still causes the regression with /dev/console association
> as already reported for v3, see
> https://lore.kernel.org/r/ZWnvc6-LnXdjOQLY@alley
Thanks and sorry for missing this issue. I thought I had this issue
already handled, but looking at what I tested with earlier, looks like
I had the console options the wrong way around.
> I used the following kernel command line:
>
> earlycon=uart8250,io,0x3f8,115200 console=ttyS0,115200 console=tty0 ignore_loglevel log_buf_len=1M
>
> The patchset caused that /dev/console became associated with
> ttyS0 instead of tty0, see the "C" flag:
>
> original # cat /proc/consoles
> tty0 -WU (EC ) 4:1
> ttyS0 -W- (E p a) 4:64
>
> vs.
>
> patched # cat /proc/consoles
> ttyS0 -W- (EC p a) 4:64
> tty0 -WU (E ) 4:1
OK
> I have added some debugging messages which nicely show the reason.
> In the original code, __add_preferred_console() is called twice
> when processing the command line:
>
> [ 0.099312] __add_preferred_console[0]: ttyS, 0 (preferrred_console == 0)
> [ 0.099982] __add_preferred_console[1]: tty, 0 (preferrred_console == 1)
OK thanks for tracking down where things go wrong.
> The code thinks that "ttyS0" has been mentioned on the command line
> once again. And preferred_console is _wrongly_ set back to '0'.
>
> My view:
>
> The delayed __add_preferred_console() is a way to hell.
>
> The preferences are defined by the ordering on the command line.
> All entries have to be added when the command line options are
> being proceed to keep the order.
To me it seems we can fix this by keeping track of the console position
in the kernel command line. I'll send a fix for this to discuss.
> A solution might be to store "devname" separately in
> struct console_cmdline and allow empty "name". We could
> implement then a function similar to
> add_preferred_console_match() which would try to match
> "devname" and set/update "name", "index" value when matched.
>
> Note that we might also need to add some synchronization
> if it might be possible to modify struct console_cmdline
> in parallel.
OK certainly no objection from me if we can make this happen without
making things more complex :)
> Second, about the possible duplication:
>
> I might get it wrong. IMHO, in principle, this patchset tries
> to achieve similar thing as the "match()" callback, see
> the commit c7cef0a84912cab3c9 ("console: Add extensible
> console matching").
>
> The .match() callback in struct console is to match, for example,
> console=uart8250,io,0x3f8,115200 when the uart8250 driver
> calls register_console() when it is being properly initialized
> as "ttyS".
>
> BTW: The .match() needs saved options because it internally calls
> .setup() callback. IMHO, this is a very ugly detail
> which complicates design of the register_console() code.
>
>
> Both approaches try to match a "driver/device-specific name" with
> the generic "ttySX".
>
> console=uart8250,io,0x3f8,115200 => ttyS0
> vs.
> console=00:00:0.0,115200 => ttyS0
>
>
> Where console=uart8250,io,0x3f8,115200 is handled by:
>
> - "uart" is added to console_cmdline[]
> - matched directly via newcon->match() callback
>
> vs. console=00:00:0.0,115200
>
> - 00:00:0.0 is added to conopt[]
> - "ttyS0" added to console_cmdline[] when "00:00:0.0" initialized
> - "ttyS0" is then matched directly
>
>
> Question: Would it it be able to use the existing .match() callback
> also to match the DEVNAME?
>
> Or somehow reuse the approach?
Thanks, I'll take a look if .match(), or some parts related to it, can
be used.
> Could register_console() know how to generate possible
> DEVNAME for the given struct console?
I don't think we can make much assumptions about the devices early on,
and we also have the console index -1 issue.
Regards,
Tony
On Mon 2024-05-27 14:13:19, Tony Lindgren wrote:
> Hi,
>
> On Fri, May 24, 2024 at 06:06:21PM +0200, Petr Mladek wrote:
> > I have finally found time to looks at this again.
>
> Great good to hear.
>
> > First, about breaking the preferred console:
> >
> > The patchset still causes the regression with /dev/console association
> > as already reported for v3, see
> > https://lore.kernel.org/r/ZWnvc6-LnXdjOQLY@alley
>
> Thanks and sorry for missing this issue. I thought I had this issue
> already handled, but looking at what I tested with earlier, looks like
> I had the console options the wrong way around.
>
> > I used the following kernel command line:
> >
> > earlycon=uart8250,io,0x3f8,115200 console=ttyS0,115200 console=tty0 ignore_loglevel log_buf_len=1M
> >
> > The patchset caused that /dev/console became associated with
> > ttyS0 instead of tty0, see the "C" flag:
> >
> > original # cat /proc/consoles
> > tty0 -WU (EC ) 4:1
> > ttyS0 -W- (E p a) 4:64
> >
> > vs.
> >
> > patched # cat /proc/consoles
> > ttyS0 -W- (EC p a) 4:64
> > tty0 -WU (E ) 4:1
>
> OK
>
> > I have added some debugging messages which nicely show the reason.
> > In the original code, __add_preferred_console() is called twice
> > when processing the command line:
> >
> > [ 0.099312] __add_preferred_console[0]: ttyS, 0 (preferrred_console == 0)
> > [ 0.099982] __add_preferred_console[1]: tty, 0 (preferrred_console == 1)
>
> OK thanks for tracking down where things go wrong.
>
> > The code thinks that "ttyS0" has been mentioned on the command line
> > once again. And preferred_console is _wrongly_ set back to '0'.
> >
> > My view:
> >
> > The delayed __add_preferred_console() is a way to hell.
> >
> > The preferences are defined by the ordering on the command line.
> > All entries have to be added when the command line options are
> > being proceed to keep the order.
>
> To me it seems we can fix this by keeping track of the console position
> in the kernel command line. I'll send a fix for this to discuss.
Honestly, I would prefer some alternative solution of the whole
problem. From my POV, the current patchset is a kind of a hack.
1. It hides console=DEVNAME:X.Y options so that register_console()
does not know about them.
2. But wait, register_console() might then enable any random console
by default when there are not console= options. For this the 3rd patch
added @console_set_on_cmdline variable which would tell
register_console(): "Hey, I have hidden some user preferences.
I'll tell you about them when the right time comes."
3. When port init matches the pattern, it adds the preferred console
so that the register_console() would know about it.
4. But wait, the ordering of preferred consoles is important.
Which would require more hacks to preserve the ordering.
5. Also serial_base_add_prefcon() adds the preferred console
with the generic name "ttyS" which is not specific
for the matched device. It just hopes that the very next
"register_console()" call will be the one related to
the matching device. Is this really guaranteed on SMP system?
IMHO, the only solution would be to add a function which would
return "ttySX" for the fiven device name.
Honestly, I do not know the hiearachy of the structures in detail.
But the documentation in the 7th patch says:
+ The mapping of the serial ports to the tty instances
+ can be viewed with:
+
+ $ ls -d /sys/bus/serial-base/devices/*:*.*/tty/*
+ /sys/bus/serial-base/devices/00:04:0.0/tty/ttyS0
BTW: I get on my test system:
# ls -1 -d /sys/bus/serial-base/devices/*:*.*/tty/*
/sys/bus/serial-base/devices/00:00:0.0/tty/ttyS0
/sys/bus/serial-base/devices/serial8250:0.1/tty/ttyS1
/sys/bus/serial-base/devices/serial8250:0.2/tty/ttyS2
/sys/bus/serial-base/devices/serial8250:0.3/tty/ttyS3
..
It looks like it should be possible to provide a function which would
return:
"ttyS0" for "00:00:0.0"
"ttyS1" for "serial8250:0.1"
...
This function might then be used in "register_console()"
to convert "console=DEVNAME:0.0" option to "ttyS" + "index".
The advantage would be that the relation between "DEVNAME:0.0"
and "ttyS0" will be clear. And the code would see the same hiearachy
as the user in /sys/bus/serial-base/devices/.
Of course, I might be too naive. Maybe, the sysfs hieararchy is
created too late. Maybe, it is not easy to go throught the
hiearachy...
But still. I wonder if there is a straightforard way which would
allow translation between "ttySX" and "DEVNAME:0.0" naming schemes.
Best Regards,
Petr
On Mon, May 27, 2024 at 03:45:55PM +0200, Petr Mladek wrote:
> On Mon 2024-05-27 14:13:19, Tony Lindgren wrote:
> > To me it seems we can fix this by keeping track of the console position
> > in the kernel command line. I'll send a fix for this to discuss.
>
> Honestly, I would prefer some alternative solution of the whole
> problem. From my POV, the current patchset is a kind of a hack.
>
> 1. It hides console=DEVNAME:X.Y options so that register_console()
> does not know about them.
OK let's make register_console() aware of the DEVNAME:X.Y options.
I like what you're suggesting towards the end of your message for
this.
> 2. But wait, register_console() might then enable any random console
> by default when there are not console= options. For this the 3rd patch
> added @console_set_on_cmdline variable which would tell
> register_console(): "Hey, I have hidden some user preferences.
> I'll tell you about them when the right time comes."
That's to allow setting up a console when the driver is ready. So that
we don't need to rely on the hardcoded device name deciphering at
console_setup() time. Maybe there's a better way to signal that though.
> 3. When port init matches the pattern, it adds the preferred console
> so that the register_console() would know about it.
>
> 4. But wait, the ordering of preferred consoles is important.
> Which would require more hacks to preserve the ordering.
Preserving the ordering part is probably the smallest issue to deal with
here :) I agree we should try to make things simpler though and there
certainly are already lots of magic switches setting up the console.
> 5. Also serial_base_add_prefcon() adds the preferred console
> with the generic name "ttyS" which is not specific
> for the matched device. It just hopes that the very next
> "register_console()" call will be the one related to
> the matching device. Is this really guaranteed on SMP system?
Hmm not sure I get this issue though, when serial_base_add_prefcon() gets
called we know the device name. The "ttyS" parts are needed to avoid
relying on the hardcoded device name deciphering at console_setup() time.
If you're thinking about the serial8250_isa_init_ports() related calls,
the serial port mapping uses SERIAL_PORT_DFNS. And then a hardware
specific 8250 may take over at some point :)
> IMHO, the only solution would be to add a function which would
> return "ttySX" for the fiven device name.
Yes agreed, this will simplify things.
> Honestly, I do not know the hiearachy of the structures in detail.
> But the documentation in the 7th patch says:
>
> + The mapping of the serial ports to the tty instances
> + can be viewed with:
> +
> + $ ls -d /sys/bus/serial-base/devices/*:*.*/tty/*
> + /sys/bus/serial-base/devices/00:04:0.0/tty/ttyS0
>
> BTW: I get on my test system:
>
> # ls -1 -d /sys/bus/serial-base/devices/*:*.*/tty/*
> /sys/bus/serial-base/devices/00:00:0.0/tty/ttyS0
> /sys/bus/serial-base/devices/serial8250:0.1/tty/ttyS1
> /sys/bus/serial-base/devices/serial8250:0.2/tty/ttyS2
> /sys/bus/serial-base/devices/serial8250:0.3/tty/ttyS3
> ...
OK
> It looks like it should be possible to provide a function which would
> return:
>
> "ttyS0" for "00:00:0.0"
> "ttyS1" for "serial8250:0.1"
> ...
>
>
> This function might then be used in "register_console()"
> to convert "console=DEVNAME:0.0" option to "ttyS" + "index".
>
> The advantage would be that the relation between "DEVNAME:0.0"
> and "ttyS0" will be clear. And the code would see the same hiearachy
> as the user in /sys/bus/serial-base/devices/.
OK makes sense to me.
> Of course, I might be too naive. Maybe, the sysfs hieararchy is
> created too late. Maybe, it is not easy to go throught the
> hiearachy...
>
> But still. I wonder if there is a straightforard way which would
> allow translation between "ttySX" and "DEVNAME:0.0" naming schemes.
We can do that on driver probe time no problem. The issues are mostly
related to setting up things early on.
Regards,
Tony
On Fri, May 24, 2024 at 06:06:21PM +0200, Petr Mladek wrote:
> A solution might be to store "devname" separately in
> struct console_cmdline and allow empty "name". We could
> implement then a function similar to
> add_preferred_console_match() which would try to match
> "devname" and set/update "name", "index" value when matched.
This sounds nice, the empty name can be used to defer consoles that
are not known early. And on console_setup() we only set the devname
for such cases.
To me it seems we additionally still need to save the kernel command
line position of the console too in struct kernel_cmdline so we can
set the preferred_console for the deferred cases.
Regards,
Tony
On Fri, May 31, 2024 at 09:54:42AM +0300, Tony Lindgren wrote:
> On Fri, May 24, 2024 at 06:06:21PM +0200, Petr Mladek wrote:
> > A solution might be to store "devname" separately in
> > struct console_cmdline and allow empty "name". We could
> > implement then a function similar to
> > add_preferred_console_match() which would try to match
> > "devname" and set/update "name", "index" value when matched.
>
> This sounds nice, the empty name can be used to defer consoles that
> are not known early. And on console_setup() we only set the devname
> for such cases.
Yup reserving a slot for a devname console at console_setup() time
in console_commandline[] allows keeping the consoles in the right
order again :)
> To me it seems we additionally still need to save the kernel command
> line position of the console too in struct kernel_cmdline so we can
> set the preferred_console for the deferred cases.
Then with the command line consoles in the right order, there's no need
to save the position separately.
And I think then we can also revert commit b73c9cbe4f1f ("printk: Flag
register_console() if console is set on command line"). But I need to
test the fixes some more before sending out.
Regards,
Tony