mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3] printk: Remove remaining boot consoles when a real console exists
@ 2026-08-21  7:17 Xiaochun Li
  2026-08-25  6:35 ` Xiaochun Li
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Xiaochun Li @ 2026-08-21  7:17 UTC (permalink / raw)
  To: pmladek; +Cc: rostedt, john.ogness, senozhatsky, linux-kernel

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@suse.com>
Link: https://lore.kernel.org/lkml/ahBsSW2bB2CRfW-k@pathway.suse.cz/ # [1]
Signed-off-by: Xiaochun Li <lixiaochun@open-hieco.net>
---
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


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3] printk: Remove remaining boot consoles when a real console exists
  2026-08-21  7:17 [PATCH v3] printk: Remove remaining boot consoles when a real console exists Xiaochun Li
@ 2026-08-25  6:35 ` Xiaochun Li
  2026-08-26 13:19   ` Petr Mladek
  2026-08-26 15:00 ` Petr Mladek
  2026-09-03 15:10 ` Petr Mladek
  2 siblings, 1 reply; 5+ messages in thread
From: Xiaochun Li @ 2026-08-25  6:35 UTC (permalink / raw)
  To: pmladek; +Cc: rostedt, john.ogness, senozhatsky, linux-kernel

On 8/21/2026 3:17 PM, 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@suse.com>
> Link: https://lore.kernel.org/lkml/ahBsSW2bB2CRfW-k@pathway.suse.cz/ # [1]
> Signed-off-by: Xiaochun Li <lixiaochun@open-hieco.net>
> ---
> 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) {

Hi Petr,

Sashiko AI raised the following concern about v3:

| Does this logic unintentionally unregister boot consoles when an unrelated
| real console is present?
| If a system boots with multiple consoles (like console=tty0 console=ttyS0
| earlycon) and an unrelated real console like tty0 (or dummycon) registers
| early, have_real_console will evaluate to true here.
| Because have_real_console is true, the init-section memory check is bypassed
| entirely, and the boot console is unconditionally destroyed:
|       if (keep_bootcon || !have_real_console) {
|               // bypassed
|       }
|       unregister_console_locked(con);
| If the real driver for ttyS0 is modular and has not loaded yet, won't this
| leave the serial console dead and cause a loss of console output during the
| window between late_initcall and the module loading?

I think this concern is valid for the case where an unrelated real console
has registered while the real console corresponding to a remaining boot
console is delayed by deferred probing or module loading.

`have_real_console` is intentionally global in this patch. The purpose is to
handle the case where a real console has already been registered but has not
become `CON_CONSDEV`. In that situation, the existing registration path does
not remove the remaining boot consoles, and duplicate output may persist.

Therefore, when `printk_late_init()` observes any registered real console,
this patch deliberately removes all remaining boot consoles without trying
to establish a one-to-one correspondence between them. This does introduce
a trade-off: a boot console may be removed even though its corresponding real
console has not registered yet, creating a temporary loss of output on that
console.

Unregistering the boot console does not remove records from the printk ring
buffer. A later real console may replay some or all of those records,
depending on its flags and sequence initialization. However, this does not
guarantee that messages generated during the gap will be visible, especially
if the system fails before the real console registers or if the records are
overwritten.

This patch implements only idea 1 from [0]. It does not solve the problem
comprehensively. We plan to investigate idea 2, based on the work in [1],
which should allow the cleanup decision to be made with more precise
information about the corresponding real console.

Do you think the trade-off described above is acceptable for this patch?

Thanks,
Xiaochun

[0] https://lore.kernel.org/lkml/ahBsSW2bB2CRfW-k@pathway.suse.cz/
[1] https://lore.kernel.org/lkml/20260604101459.393162-1-pmladek@suse.com/

> +			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();
>   


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3] printk: Remove remaining boot consoles when a real console exists
  2026-08-25  6:35 ` Xiaochun Li
@ 2026-08-26 13:19   ` Petr Mladek
  0 siblings, 0 replies; 5+ messages in thread
From: Petr Mladek @ 2026-08-26 13:19 UTC (permalink / raw)
  To: Xiaochun Li; +Cc: rostedt, john.ogness, senozhatsky, linux-kernel

On Tue 2026-08-25 14:35:10, Xiaochun Li wrote:
> On 8/21/2026 3:17 PM, 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.
> > 
> Sashiko AI raised the following concern about v3:
> 
> | Does this logic unintentionally unregister boot consoles when an unrelated
> | real console is present?
> | If a system boots with multiple consoles (like console=tty0 console=ttyS0
> | earlycon) and an unrelated real console like tty0 (or dummycon) registers
> | early, have_real_console will evaluate to true here.
> | Because have_real_console is true, the init-section memory check is bypassed
> | entirely, and the boot console is unconditionally destroyed:
> |       if (keep_bootcon || !have_real_console) {
> |               // bypassed
> |       }
> |       unregister_console_locked(con);
> | If the real driver for ttyS0 is modular and has not loaded yet, won't this
> | leave the serial console dead and cause a loss of console output during the
> | window between late_initcall and the module loading?
> 
> I think this concern is valid for the case where an unrelated real console
> has registered while the real console corresponding to a remaining boot
> console is delayed by deferred probing or module loading.
> 
> `have_real_console` is intentionally global in this patch. The purpose is to
> handle the case where a real console has already been registered but has not
> become `CON_CONSDEV`. In that situation, the existing registration path does
> not remove the remaining boot consoles, and duplicate output may persist.
> 
> Therefore, when `printk_late_init()` observes any registered real console,
> this patch deliberately removes all remaining boot consoles without trying
> to establish a one-to-one correspondence between them. This does introduce
> a trade-off: a boot console may be removed even though its corresponding real
> console has not registered yet, creating a temporary loss of output on that
> console.
> 
> Unregistering the boot console does not remove records from the printk ring
> buffer. A later real console may replay some or all of those records,
> depending on its flags and sequence initialization. However, this does not
> guarantee that messages generated during the gap will be visible, especially
> if the system fails before the real console registers or if the records are
> overwritten.
> 
> This patch implements only idea 1 from [0]. It does not solve the problem
> comprehensively. We plan to investigate idea 2, based on the work in [1],
> which should allow the cleanup decision to be made with more precise
> information about the corresponding real console.
> 
> Do you think the trade-off described above is acceptable for this patch?

I believe that this is acceptable. The same problem existed even
before. The boot consoles are unregistered at the end of
register_console() when the so-called preferred console gets
registered. The preferred console is defined by
the last console= on the command line.

The preferred console might be the graphical ttyX. So the serial
early console might get unregistered before the proper serial
console driver gets registered and it might cause the above
described gap.

More details:

I have just double checked the code in register_console().
It looks like:

void register_console(struct console *newcon)
{
[...]
	/*
	 * By unregistering the bootconsoles after we enable the real console
	 * we get the "console xxx enabled" message on all the consoles -
	 * boot consoles, real consoles, etc - this is to ensure that end
	 * users know there might be something in the kernel's log buffer that
	 * went to the bootconsole (that they do not see on the real console)
	 */
	con_printk(KERN_INFO, newcon, "enabled\n");
	if (bootcon_registered &&
	    ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
	    !keep_bootcon) {
		struct hlist_node *tmp;

		hlist_for_each_entry_safe(con, tmp, &console_list, node) {
			if (con->flags & CON_BOOT)
				unregister_console_locked(con);
		}
	}
[...]
}

The comment above the code says that the boot consoles are removed when
a real console gets registered. But it is _not_ right.

The meaning of the CON_CONSDEV flags is historically pretty
complicated.

The name CON_CONSDEV suggests that it should be set for
the console driver which is associated with /dev/console.
But it is just the best effort.

The driver associated with /dev/console is selected by
console_device(). And it returns the first driver where
con->device() exists and return !NULL. It does not check
the flag at all.

Unfortunately, con->device() might returns NULL in
register_console() and some real value later. It is related
to the ordering of initialization of various subsystems.
Anyway, the result is that register_console() must guess.

Plus there is the rule that the preferred console (last on
the command line) should get associated with /dev/console.
For this, register_console() must put the preferred console
to be first in console_list.

Now, back to the best effort. CON_CONSDEV is set by:

   + try_enable_preferred_console() _only_ for the preferred console.
     This function is used when some console is preferred on
     the command line or via SPCR or the device tree.

   + try_enable_default_console() for real console drivers.
     This function is used when there is no preferred console.

   + register_console() and unregister_console_locked() for
     the 1st console in the console_list. It is a hack
     to make sure that at least one console has the flag set.
     And it might be set even for a boot console.

   + console_force_preferred_locked() for the given console.
     Some platforms have their own preferred console.

Summary:

It is complicated. But in short:

   1. It might happen that CON_CONSDEV points to boot console
      => register_console() does not remove boot consoles
      when a real console (not the preferred_console one)
      gets registered.

      This is why it makes sense to remove them in printk_late_init().

      => this patch makes sense.


   2. register_console() already might remove boot consoles
      before the corresponding real driver gets registered.

      => the race already exist.

      => this patch looks acceptable to me.

Best Regards,
Petr

PS: I am going to take a break, coffee, and actually review the patch ;-)

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3] printk: Remove remaining boot consoles when a real console exists
  2026-08-21  7:17 [PATCH v3] printk: Remove remaining boot consoles when a real console exists Xiaochun Li
  2026-08-25  6:35 ` Xiaochun Li
@ 2026-08-26 15:00 ` Petr Mladek
  2026-09-03 15:10 ` Petr Mladek
  2 siblings, 0 replies; 5+ messages in thread
From: Petr Mladek @ 2026-08-26 15:00 UTC (permalink / raw)
  To: Xiaochun Li; +Cc: rostedt, john.ogness, senozhatsky, linux-kernel

On Fri 2026-08-21 15:17:21, 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@suse.com>
> Link: https://lore.kernel.org/lkml/ahBsSW2bB2CRfW-k@pathway.suse.cz/ # [1]
> Signed-off-by: Xiaochun Li <lixiaochun@open-hieco.net>

The patch looks good:

Reviewed-by: Petr Mladek <pmladek@suse.com>
Tested-by: Petr Mladek <pmladek@suse.com>

The patch looks ready for linux-next. I am going to wait one week
for more potential feedback. Then I will push it to printk/linux.git
unless anyone complains in the meantime.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v3] printk: Remove remaining boot consoles when a real console exists
  2026-08-21  7:17 [PATCH v3] printk: Remove remaining boot consoles when a real console exists Xiaochun Li
  2026-08-25  6:35 ` Xiaochun Li
  2026-08-26 15:00 ` Petr Mladek
@ 2026-09-03 15:10 ` Petr Mladek
  2 siblings, 0 replies; 5+ messages in thread
From: Petr Mladek @ 2026-09-03 15:10 UTC (permalink / raw)
  To: Xiaochun Li; +Cc: rostedt, john.ogness, senozhatsky, linux-kernel

On Fri 2026-08-21 15:17:21, 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@suse.com>
> Link: https://lore.kernel.org/lkml/ahBsSW2bB2CRfW-k@pathway.suse.cz/ # [1]
> Signed-off-by: Xiaochun Li <lixiaochun@open-hieco.net>

JFYI, the patch has been comitted into printk/linux.git,
branch for-7.4-console-registration.

Best Regards,
Petr

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-09-03 15:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-21  7:17 [PATCH v3] printk: Remove remaining boot consoles when a real console exists Xiaochun Li
2026-08-25  6:35 ` Xiaochun Li
2026-08-26 13:19   ` Petr Mladek
2026-08-26 15:00 ` Petr Mladek
2026-09-03 15:10 ` Petr Mladek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®