Re: [PATCH v2] printk: Remove remaining boot consoles when a real console exists

From: Xiaochun Li

Date: Thu Aug 20 2026 - 22:15:51 EST


On 8/20/2026 11:13 PM, Petr Mladek wrote:
On Wed 2026-08-05 16:48:46, Xiaochun Li wrote:
Boot consoles are temporary and should be removed once a real console is
available. However, the late init cleanup currently only unregisters boot
consoles that use init section memory. Other boot consoles are expected
to be removed when the real preferred console is registered.

This does not cover cases where a real console has registered, but the
boot console was not removed because the real console did not become the
preferred console. For example, with multiple console= parameters using
the same driver, a real 8250 console may be enabled while the early
console remains registered. The result is duplicate printk output from
both consoles.

In the mailing list discussion, two possible approaches were suggested
to fix this problem [1]. This patch implements the first one: during
printk_late_init(), check whether at least one real console is already
registered. If so, unregister all remaining boot consoles. If no real
console exists yet, keep the existing behavior and unregister only boot
consoles that reference init section memory, avoiding a period with no
console output while waiting for a deferred or modular real console.

Suggested-by: Petr Mladek <pmladek@xxxxxxxx>
Link: https://lore.kernel.org/lkml/ahBsSW2bB2CRfW-k@xxxxxxxxxxxxxxx/ # [1]
Signed-off-by: Xiaochun Li <lixiaochun@xxxxxxxxxxxxxx>
---
kernel/printk/printk.c | 34 +++++++++++++++++++++++-----------
1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2fe9a963c823..5a5ec7ac99bd 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -4420,36 +4420,48 @@ void __init console_init(void)
* be a brief interval in which no messages are logged to the console, which
* makes it difficult to diagnose problems that occur during this time.
*
- * To mitigate this problem somewhat, only unregister consoles whose memory
- * intersects with the init section. Note that all other boot consoles will
- * get unregistered when the real preferred console is registered.
+ * If a real console has already registered, remove all remaining boot consoles.
+ * Otherwise, mitigate the no-console interval by removing only boot consoles
+ * whose memory intersects with the init section.
*/
static int __init printk_late_init(void)
{
+ bool realcon_registered = false;

Nit: I would call the variable "have_real_console" to follow the
existing naming scheme, see have_boot_console,
have_legacy_console, ...

Please, change it in v2 if we need it, ...


struct hlist_node *tmp;
struct console *con;
int ret;
console_list_lock();
+ for_each_console(con) {
+ if (!(con->flags & CON_BOOT)) {
+ realcon_registered = true;
+ break;
+ }
+ }

Sashiko AI worries:

| Can this cause a period of missing console output if a non-visible console
| (like netconsole or pstore) registers early?

I do not agree. Users are able to read the messages on netconsole
or pstore, so I consider them valid real consoles.

| It looks like this checks only for the absence of CON_BOOT to consider a
| real console present. If a real hardware serial console is deferred or
| loaded as a module later, wouldn't we need to check for CON_CONSDEV to
| ensure the preferred hardware console is actually available before dropping
| the boot console?

It does not make much sense. The CON_CONSDEV flag does not
guarantee anything. It is just the best effort to mart console
which will get used by /dev/console.

hlist_for_each_entry_safe(con, tmp, &console_list, node) {
if (!(con->flags & CON_BOOT))
continue;
- /* Check addresses that might be used for enabled consoles. */
- if (init_section_intersects(con, sizeof(*con)) ||
- init_section_contains(con->write, 0) ||
- init_section_contains(con->read, 0) ||
- init_section_contains(con->device, 0) ||
- init_section_contains(con->unblank, 0) ||
- init_section_contains(con->data, 0)) {
+ if (!realcon_registered) {
+ /* Check addresses that might be used for enabled consoles. */
+ if (!init_section_intersects(con, sizeof(*con)) &&
+ !init_section_contains(con->write, 0) &&
+ !init_section_contains(con->read, 0) &&
+ !init_section_contains(con->device, 0) &&
+ !init_section_contains(con->unblank, 0) &&
+ !init_section_contains(con->data, 0))
+ continue;
+
/*
* Please, consider moving the reported consoles out
* of the init section.
*/
pr_warn("bootconsole [%s%d] uses init memory and must be disabled even before the real one is ready\n",
con->name, con->index);
- unregister_console_locked(con);
}
+
+ unregister_console_locked(con);

Another comment from Sashiko AI:

| Does this unconditional unregistration bypass the keep_bootcon command-line
| parameter?
|
| If a user boots with keep_bootcon to preserve early boot consoles for
| debugging, it appears this code will bypass the keep_bootcon flag check
| traditionally used in register_console() and unexpectedly unregister the
| debugging consoles once a real console registers.

This one describes a real problem. We have to check the "keep_bootcon"
flag. I suggest something like:

--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -4420,36 +4420,51 @@ void __init console_init(void)
* be a brief interval in which no messages are logged to the console, which
* makes it difficult to diagnose problems that occur during this time.
*
- * To mitigate this problem somewhat, only unregister consoles whose memory
- * intersects with the init section. Note that all other boot consoles will
- * get unregistered when the real preferred console is registered.
+ * If a real console has already registered, remove all remaining boot consoles.
+ * Otherwise, mitigate the no-console interval by removing only boot consoles
+ * whose memory intersects with the init section.
*/
static int __init printk_late_init(void)
{
+ bool have_real_console = false;
struct hlist_node *tmp;
struct console *con;
int ret;
console_list_lock();
+ for_each_console(con) {
+ if (!(con->flags & CON_BOOT)) {
+ have_real_console = true;
+ break;
+ }
+ }
+
hlist_for_each_entry_safe(con, tmp, &console_list, node) {
if (!(con->flags & CON_BOOT))
continue;
- /* Check addresses that might be used for enabled consoles. */
- if (init_section_intersects(con, sizeof(*con)) ||
- init_section_contains(con->write, 0) ||
- init_section_contains(con->read, 0) ||
- init_section_contains(con->device, 0) ||
- init_section_contains(con->unblank, 0) ||
- init_section_contains(con->data, 0)) {
+ /*
+ * Keep the boot console when requested or as a fallback
+ * unless it is using an init section.
+ */
+ if (keep_bootcon || !have_real_console) {
+ if (!init_section_intersects(con, sizeof(*con)) &&
+ !init_section_contains(con->write, 0) &&
+ !init_section_contains(con->read, 0) &&
+ !init_section_contains(con->device, 0) &&
+ !init_section_contains(con->unblank, 0) &&
+ !init_section_contains(con->data, 0))
+ continue;
+
/*
* Please, consider moving the reported consoles out
* of the init section.
*/
pr_warn("bootconsole [%s%d] uses init memory and must be disabled even before the real one is ready\n",
con->name, con->index);
- unregister_console_locked(con);
}
+
+ unregister_console_locked(con);
}
console_list_unlock();

Best Regards,
Petr

Thanks for your detailed review and for helping to clarify the questions
raised (including those regarding Sashiko AI). I will send a V3 that
incorporates your suggestions.

Best regards,
Xiaochun Li