Merge tag 'printk-for-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux

Pull printk updates from Petr Mladek:

 - John Ogness became PRINTK co-maintainer

 - Unregister boot consoles when any proper console is registered at the
   end of the boot phase. Before they were removed only when the
   so-called preferred console, usually set via the last 'console='
   parameter, got registered

 - Fix computation for increasing the printk ringbuffer on systems with
   many CPUs and possibly long backtraces from all CPUs

 - Cleaned up irq_works handling for deferred printk operations

 - Use acquire/release semantic for filled_random_ptr_key

 - Allow to use ':' in console options

 - More code clean up

* tag 'printk-for-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux:
  MAINTAINERS: Make John PRINTK co-maintainer
  lib/vsprintf: Use EXPORT_SYMBOL_IF_KUNIT() to export symbols
  printk: Remove console options before decoding the name
  printk: increase the ring buffer for more than 16 CPUs by default
  printk_ringbuffer: Avoid needless read of prb_desc
  printk: Use two irq_works instead per-CPU
  lib/vsprintf: Use acquire/release for ptr_key publication
  printk: Remove remaining boot consoles when a real console exists
This commit is contained in:
Linus Torvalds committed 2026-10-23 09:44:53 -10:00
commit 5b212610d0
5 files changed
+88 -81

No files matched your search

+1 -1
View File
@@ -21961,8 +21961,8 @@ F: scripts/Makefile.propeller
PRINTK
M: Petr Mladek <pmladek@suse.com>
M: John Ogness <john.ogness@linutronix.de>
R: Steven Rostedt <rostedt@goodmis.org>
R: John Ogness <john.ogness@linutronix.de>
R: Sergey Senozhatsky <senozhatsky@chromium.org>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux.git
+70 -60
View File
@@ -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;
@@ -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;
@@ -4424,36 +4426,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();
@@ -4593,31 +4610,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 +4643,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 +4654,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 +4673,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 +4694,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);
}
/**
+7 -10
View File
@@ -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
+1
View File
@@ -876,3 +876,4 @@ kunit_test_suite(printf_test_suite);
MODULE_AUTHOR("Rasmus Villemoes <linux@rasmusvillemoes.dk>");
MODULE_DESCRIPTION("Test cases for printf facility");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+9 -10
View File
@@ -25,6 +25,7 @@
#include <linux/module.h> /* for KSYM_SYMBOL_LEN */
#include <linux/types.h>
#include <linux/string.h>
#include <linux/string_helpers.h>
#include <linux/ctype.h>
#include <linux/hex.h>
#include <linux/kernel.h>
@@ -54,12 +55,13 @@
#include <asm/byteorder.h> /* cpu_to_le16 */
#include <linux/unaligned.h>
#include <linux/string_helpers.h>
#include <kunit/visibility.h>
#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
@@ -762,9 +764,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 +782,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);
/*
@@ -850,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,