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

From: Xiaochun Li

Date: Fri Aug 21 2026 - 03:17:43 EST


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>
---
Changes in v3:
- Rename realcon_registered to have_real_console per review feedback.
- Adjust cleanup logic to respect keep_bootcon.

kernel/printk/printk.c | 37 ++++++++++++++++++++++++++-----------
1 file changed, 26 insertions(+), 11 deletions(-)

diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2fe9a963c823..cd837f7db7f4 100644
--- 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();

--
2.52.0