From 2dd7ee9638a0d9d94a574c60f221da00514e6146 Mon Sep 17 00:00:00 2001 From: Xiaochun Li Date: Fri, 21 Aug 2026 15:17:21 +0800 Subject: [PATCH 1/8] printk: Remove remaining boot consoles when a real console exists 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 Link: https://lore.kernel.org/lkml/ahBsSW2bB2CRfW-k@pathway.suse.cz/ # [1] Signed-off-by: Xiaochun Li Reviewed-by: Petr Mladek Tested-by: Petr Mladek Link: https://patch.msgid.link/20260821071721.178517-1-lixiaochun@open-hieco.net Signed-off-by: Petr Mladek --- 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 3fcdf4b4e2e5..7722a0f81ba8 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -4424,36 +4424,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(); From 72d907f23ef092d63caac96ca3af6fcae4506008 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 2 Sep 2026 15:47:59 +0800 Subject: [PATCH 2/8] lib/vsprintf: Use acquire/release for ptr_key publication Replace the smp_wmb() + WRITE_ONCE() and READ_ONCE() + smp_rmb() barrier pair with smp_store_release()/smp_load_acquire() on filled_random_ptr_key. This expresses the publish/subscribe pattern more clearly and allows architectures with native acquire/release instructions (e.g. arm64's STLR/LDAR) to avoid the cost of full one-way barriers (DMB ISHST/ISHLD). No functional change intended. Cc: Petr Mladek Cc: Steven Rostedt Cc: Andy Shevchenko Cc: Rasmus Villemoes Cc: Sergey Senozhatsky Cc: Andrew Morton Assisted-by: DeepSeek:DeepSeek-V3 Signed-off-by: Jinjie Ruan Reviewed-by: Petr Mladek Link: https://patch.msgid.link/20260902074805.398540-3-ruanjinjie@huawei.com Signed-off-by: Petr Mladek --- lib/vsprintf.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e285e8bc4712..d6f235e143b7 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -762,9 +762,8 @@ static int fill_ptr_key(struct notifier_block *nb, unsigned long action, void *d { get_random_bytes(&ptr_key, sizeof(ptr_key)); - /* Pairs with smp_rmb() before reading ptr_key. */ - smp_wmb(); - WRITE_ONCE(filled_random_ptr_key, true); + /* Pairs with smp_load_acquire() before reading ptr_key. */ + smp_store_release(&filled_random_ptr_key, true); return NOTIFY_DONE; } @@ -781,12 +780,10 @@ static inline int __ptr_to_hashval(const void *ptr, unsigned long *hashval_out) { unsigned long hashval; - if (!READ_ONCE(filled_random_ptr_key)) + /* Pairs with smp_store_release() after writing ptr_key. */ + if (!smp_load_acquire(&filled_random_ptr_key)) return -EBUSY; - /* Pairs with smp_wmb() after writing ptr_key. */ - smp_rmb(); - #ifdef CONFIG_64BIT hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key); /* From 731383dcf511c4d9ee00b3dc9f4f65689e233401 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 14 Sep 2026 16:34:12 +0200 Subject: [PATCH 3/8] printk: Use two irq_works instead per-CPU In commit b845b517b5e37 ("printk: robustify printk") initially got its delayed wake up which is today implemented via the irq_work. Back then it was a variable which was assigned to 1 if a pending wake up was waiting. This variable was checked on each timer tick. Therefore it makes sense to assign a per-CPU variable with this task to avoid cache line bouncing when each CPU reads the variable on each timer tick simultaneously. Today we have a irq_work which is enqueued onto a list and handled if it has work items assigned. This list is already per-CPU. We could replace the per-CPU irq_work and the per-CPU printk_pending with two irq_work structs: One for PRINTK_PENDING_WAKEUP and the other for PRINTK_PENDING_OUTPUT. While unrolling the conditions in __wake_up_klogd() it becomes obvious that PRINTK_PENDING_WAKEUP is always passed as an argument but never checked. The return value wq_has_sleeper() of does not really matter it's sole purpose is to enforce the needed memory barrier. Redo the irq_work by: - Use one irq_work (pending_wakeup_work) for klogd wakes. Schedule it only if there is a sleeper waiting. - Use one irq_work (&pending_output_work) for output printing. Schedule it only if PRINTK_PENDING_OUTPUT passed. - Drop unused PRINTK_PENDING_WAKEUP. - Remove the preempt_disable() section. Its purpose was to ensure that printk_pending and wake_up_klogd_work is accessed from the same CPU. Worst case is defer_console_output() where two irq_work are scheduled but those get processed one after the other during the HZ tick so it shouldn't get worse. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Petr Mladek Reviewed-by: John Ogness Link: https://patch.msgid.link/20260914143412.1sMcp89V@linutronix.de Signed-off-by: Petr Mladek --- kernel/printk/printk.c | 53 ++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 3fcdf4b4e2e5..a3c4ee6d9f1d 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -4593,31 +4593,26 @@ bool pr_flush(int timeout_ms, bool reset_on_progress) /* * Delayed printk version, for scheduler-internal messages: */ -#define PRINTK_PENDING_WAKEUP 0x01 -#define PRINTK_PENDING_OUTPUT 0x02 +#define PRINTK_PENDING_OUTPUT 0x01 -static DEFINE_PER_CPU(int, printk_pending); - -static void wake_up_klogd_work_func(struct irq_work *irq_work) +static void pending_wake_fn(struct irq_work *irq_work) { - int pending = this_cpu_xchg(printk_pending, 0); - - if (pending & PRINTK_PENDING_OUTPUT) { - if (force_legacy_kthread()) { - if (printk_legacy_kthread) - wake_up_interruptible(&legacy_wait); - } else { - if (console_trylock()) - console_unlock(); - } - } - - if (pending & PRINTK_PENDING_WAKEUP) - wake_up_interruptible(&log_wait); + wake_up_interruptible(&log_wait); } -static DEFINE_PER_CPU(struct irq_work, wake_up_klogd_work) = - IRQ_WORK_INIT_LAZY(wake_up_klogd_work_func); +static void pending_output_fn(struct irq_work *irq_work) +{ + if (force_legacy_kthread()) { + if (printk_legacy_kthread) + wake_up_interruptible(&legacy_wait); + } else { + if (console_trylock()) + console_unlock(); + } +} + +static struct irq_work pending_wakeup_work = IRQ_WORK_INIT_LAZY(pending_wake_fn); +static struct irq_work pending_output_work = IRQ_WORK_INIT_LAZY(pending_output_fn); static void __wake_up_klogd(int val) { @@ -4631,7 +4626,6 @@ static void __wake_up_klogd(int val) if (WARN_ON_ONCE(console_irqwork_blocked)) return; - preempt_disable(); /* * Guarantee any new records can be seen by tasks preparing to wait * before this context checks if the wait queue is empty. @@ -4643,12 +4637,11 @@ static void __wake_up_klogd(int val) * * This pairs with devkmsg_read:A and syslog_print:A. */ - if (wq_has_sleeper(&log_wait) || /* LMM(__wake_up_klogd:A) */ - (val & PRINTK_PENDING_OUTPUT)) { - this_cpu_or(printk_pending, val); - irq_work_queue(this_cpu_ptr(&wake_up_klogd_work)); - } - preempt_enable(); + if (wq_has_sleeper(&log_wait)) /* LMM(__wake_up_klogd:A) */ + irq_work_queue(&pending_wakeup_work); + + if (val & PRINTK_PENDING_OUTPUT) + irq_work_queue(&pending_output_work); } /** @@ -4663,7 +4656,7 @@ static void __wake_up_klogd(int val) */ void wake_up_klogd(void) { - __wake_up_klogd(PRINTK_PENDING_WAKEUP); + __wake_up_klogd(0); } /** @@ -4684,7 +4677,7 @@ void defer_console_output(void) * New messages may have been added directly to the ringbuffer * using vprintk_store(), so wake any waiters as well. */ - __wake_up_klogd(PRINTK_PENDING_WAKEUP | PRINTK_PENDING_OUTPUT); + __wake_up_klogd(PRINTK_PENDING_OUTPUT); } /** From 267f1127c0f4035448acc6f2c3321eb38e54eecc Mon Sep 17 00:00:00 2001 From: Wang Wensheng Date: Sat, 26 Sep 2026 17:45:02 +0800 Subject: [PATCH 4/8] printk_ringbuffer: Avoid needless read of prb_desc Since commit f244b4dc53e5 ("printk: ringbuffer: Improve prb_next_seq() performance"), desc_read() allows @desc_out to be NULL. We can drop the local `desc` variable in desc_reopen_last() and prb_first_seq(). Also update the comment for desc_read(). v1->v2: Update the comment suggested by John. Signed-off-by: Wang Wensheng Reviewed-by: Petr Mladek Reviewed-by: John Ogness Link: https://patch.msgid.link/20260926094502.251204-1-wsw9603@163.com Signed-off-by: Petr Mladek --- kernel/printk/printk_ringbuffer.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/kernel/printk/printk_ringbuffer.c b/kernel/printk/printk_ringbuffer.c index 85c0c854b3ce..d38e7c2f6887 100644 --- a/kernel/printk/printk_ringbuffer.c +++ b/kernel/printk/printk_ringbuffer.c @@ -439,13 +439,12 @@ static enum desc_state get_desc_state(unsigned long id, } /* - * Get a copy of a specified descriptor and return its queried state. If the - * descriptor is in an inconsistent state (miss or reserved), the caller can - * only expect the descriptor's @state_var field to be valid. + * Get the state of a specified descriptor. * - * The sequence number and caller_id can be optionally retrieved. Like all - * non-state_var data, they are only valid if the descriptor is in a - * consistent state. + * Optionally, a copy of the descriptor, sequence number and/or caller ID can + * be retrieved. However, (with the exception of @desc_out->state_var) all + * retrieved values are invalid if the descriptor is in an inconsistent state + * (miss or reserved). */ static enum desc_state desc_read(struct prb_desc_ring *desc_ring, unsigned long id, struct prb_desc *desc_out, @@ -1335,7 +1334,6 @@ static struct prb_desc *desc_reopen_last(struct prb_desc_ring *desc_ring, { unsigned long prev_state_val; enum desc_state d_state; - struct prb_desc desc; struct prb_desc *d; unsigned long id; u32 cid; @@ -1346,7 +1344,7 @@ static struct prb_desc *desc_reopen_last(struct prb_desc_ring *desc_ring, * To reduce unnecessarily reopening, first check if the descriptor * state and caller ID are correct. */ - d_state = desc_read(desc_ring, id, &desc, NULL, &cid); + d_state = desc_read(desc_ring, id, NULL, NULL, &cid); if (d_state != desc_committed || cid != caller_id) return NULL; @@ -2007,14 +2005,13 @@ u64 prb_first_seq(struct printk_ringbuffer *rb) { struct prb_desc_ring *desc_ring = &rb->desc_ring; enum desc_state d_state; - struct prb_desc desc; unsigned long id; u64 seq; for (;;) { id = atomic_long_read(&rb->desc_ring.tail_id); /* LMM(prb_first_seq:A) */ - d_state = desc_read(desc_ring, id, &desc, &seq, NULL); /* LMM(prb_first_seq:B) */ + d_state = desc_read(desc_ring, id, NULL, &seq, NULL); /* LMM(prb_first_seq:B) */ /* * This loop will not be infinite because the tail is From ae30792e536c80aed87fb11ec9140e55ed99da09 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 27 Sep 2026 18:17:54 +0900 Subject: [PATCH 5/8] printk: increase the ring buffer for more than 16 CPUs by default The default LOG_BUF_SHIFT is 17, which means 128 KB, and LOG_CPU_MAX_BUF_SHIFT is 12, which means 4 KB per CPU. By default the ring buffer is increased when (num_possible_cpus() - 1) * 4 KB > 64 KB, which requires more than 17 CPUs. Remove the explicit subtraction of one, so the ring buffer is increased for more than 16 CPUs, which is a power of two. Also fix the outdated comment. Suggested-by: Petr Mladek Signed-off-by: Sang-Heon Jeon Reviewed-by: John Ogness Reviewed-by: Petr Mladek Link: https://patch.msgid.link/20260927091756.50831-1-ekffu200098@gmail.com Signed-off-by: Petr Mladek --- kernel/printk/printk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index a3c4ee6d9f1d..45c3cfc28e78 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1085,9 +1085,9 @@ static void __init log_buf_add_cpu(void) if (num_possible_cpus() == 1) return; - cpu_extra = (num_possible_cpus() - 1) * __LOG_CPU_MAX_BUF_LEN; + cpu_extra = num_possible_cpus() * __LOG_CPU_MAX_BUF_LEN; - /* by default this will only continue through for large > 64 CPUs */ + /* by default this will only continue through for more than 16 CPUs */ if (cpu_extra <= __LOG_BUF_LEN / 2) return; From b80fc54890d44cd35360ca122b72224fe78061bf Mon Sep 17 00:00:00 2001 From: David Engraf Date: Thu, 24 Sep 2026 10:00:57 +0300 Subject: [PATCH 6/8] printk: Remove console options before decoding the name This fixes a regression when a console option includes ':'. Commit 7640f1a44eba ("printk: Add match_devname_and_update_preferred_console()") introduced console=DEVNAME:0.0 hardware style addressing by looking for a colon. If the colon is part of an option the name is handled as devname instead of ttyname. Fix by handling the options first which will add a NULL terminator to the string. Note that parsing the options first means that also idx parsing needs changing. Handle the idx parsing by refactoring the code (thanks to Petr Mladek). Signed-off-by: David Engraf Reviewed-by: Tony Lindgren Reviewed-by: Petr Mladek Reviewed-by: John Ogness Link: https://patch.msgid.link/20260924070153.250783-2-david.engraf@sysgo.com [pmladek@suse.com: Updated the commit message as suggested by Tony Lindgren] Signed-off-by: Petr Mladek --- kernel/printk/printk.c | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 45c3cfc28e78..9070f20230bb 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2646,24 +2646,25 @@ static int __init console_setup(char *str) if (_braille_console_setup(&str, &brl_options)) return 1; + /* + * Decode str into name, index and options. Start with options, since + * it might also contain a ':' used for DEVNAME. + */ + options = strchr(str, ','); + if (options) + *(options++) = 0; + /* For a DEVNAME:0.0 style console the character device is unknown early */ if (strchr(str, ':')) devname = buf; else ttyname = buf; - /* - * Decode str into name, index, options. - */ if (ttyname && isdigit(str[0])) scnprintf(buf, sizeof(buf), "ttyS%s", str); else strscpy(buf, str); - options = strchr(str, ','); - if (options) - *(options++) = 0; - #ifdef __sparc__ if (!strcmp(str, "ttya")) strscpy(buf, "ttyS0"); @@ -2671,17 +2672,18 @@ static int __init console_setup(char *str) strscpy(buf, "ttyS1"); #endif - for (s = buf; *s; s++) - if ((ttyname && isdigit(*s)) || *s == ',') - break; - - /* @idx will get defined when devname matches. */ - if (devname) - idx = -1; - else + if (ttyname) { + /* Detect @idx in ttyname and remove it. */ + for (s = ttyname; *s; s++) { + if (isdigit(*s)) + break; + } idx = simple_strtoul(s, NULL, 10); - - *s = 0; + *s = 0; + } else { + /* @idx will get defined when devname matches. */ + idx = -1; + } __add_preferred_console(ttyname, idx, devname, options, brl_options, true); return 1; From a75d9cc33d8102cd9fd103a7f0466b8ffb2a2d1b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 14 Jul 2026 14:21:43 +0200 Subject: [PATCH 7/8] lib/vsprintf: Use EXPORT_SYMBOL_IF_KUNIT() to export symbols Replace EXPORT_SYMBOL_FOR_MODULES() with EXPORT_SYMBOL_IF_KUNIT() to mark the symbols as visible only if CONFIG_KUNIT is enabled. Kunit test should import the namespace EXPORTED_FOR_KUNIT_TESTING to use these marked symbols. This is the standard way for all KUnit tests. Signed-off-by: Andy Shevchenko Acked-by: Tamir Duberstein Link: https://patch.msgid.link/20260714122143.743227-1-andriy.shevchenko@linux.intel.com Signed-off-by: Petr Mladek --- lib/tests/printf_kunit.c | 1 + lib/vsprintf.c | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/tests/printf_kunit.c b/lib/tests/printf_kunit.c index eccf041ebd56..290d59b22c29 100644 --- a/lib/tests/printf_kunit.c +++ b/lib/tests/printf_kunit.c @@ -876,3 +876,4 @@ kunit_test_suite(printf_test_suite); MODULE_AUTHOR("Rasmus Villemoes "); MODULE_DESCRIPTION("Test cases for printf facility"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index d6f235e143b7..a430753dd702 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -25,6 +25,7 @@ #include /* for KSYM_SYMBOL_LEN */ #include #include +#include #include #include #include @@ -54,12 +55,13 @@ #include /* cpu_to_le16 */ #include -#include +#include + #include "kstrtox.h" /* Disable pointer hashing if requested */ bool no_hash_pointers __ro_after_init; -EXPORT_SYMBOL_FOR_MODULES(no_hash_pointers, "printf_kunit"); +EXPORT_SYMBOL_IF_KUNIT(no_hash_pointers); /* * Hashed pointers policy selected by "hash_pointers=..." boot param @@ -847,7 +849,7 @@ static char *default_pointer(char *buf, char *end, const void *ptr, } int kptr_restrict __read_mostly; -EXPORT_SYMBOL_FOR_MODULES(kptr_restrict, "printf_kunit"); +EXPORT_SYMBOL_IF_KUNIT(kptr_restrict); static noinline_for_stack char *restricted_pointer(char *buf, char *end, const void *ptr, From c56e202c1a48918392c61cbdd3caf04afe425970 Mon Sep 17 00:00:00 2001 From: Petr Mladek Date: Wed, 14 Oct 2026 13:50:29 +0200 Subject: [PATCH 8/8] MAINTAINERS: Make John PRINTK co-maintainer John is author of the huge rework of the printk subsystem. He has a deep understanding of the code and great awareness of the various user expectations. He is willing to step into the maintainer role which will be a great help. Reviewed-by: John Ogness Reviewed-by: Steven Rostedt Reviewed-by: Sergey Senozhatsky Link: https://patch.msgid.link/20261014115029.203908-1-pmladek@suse.com Signed-off-by: Petr Mladek --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a830d3b252e2..ccc313d31e54 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21683,8 +21683,8 @@ F: scripts/Makefile.propeller PRINTK M: Petr Mladek +M: John Ogness R: Steven Rostedt -R: John Ogness R: Sergey Senozhatsky S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git