* [PATCH v7 00/13] printk: console: Per-console loglevels
@ 2025-11-18 19:06 Chris Down
2025-11-18 19:06 ` [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console Chris Down
` (12 more replies)
0 siblings, 13 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:06 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
There was a lot of review feedback last round, I sincerely hope I didn't
miss anything :-)
Thanks, Petr, Greg, John, and Tony for the feedback and reviews.
v7:
- Disallow writing -1 to the global console_loglevel sysctl
- Change printk_get_next_message() to take effective loglevel
- Update suppress_message_printing() to take effective loglevel
- Use CONSOLE_LOGLEVEL_MOTORMOUTH for devkmsg_read()
- Fix documentation per Petr's review comments
- Add console_srcu_read_loglevel() for lockless reading
- Rename per_console_loglevel_is_set() to has_per_console_loglevel()
- Refactor API to take int con_level for better lockdep checking
- Update callers to read con_level using console_srcu_read_loglevel()
- Remove hierarchy comment since it's obvious from the code
- Use LOGLEVEL_DEFAULT as base level instead of hardcoded -1
- Use pr_warn_once() in sysrq handling instead of manual warned variable
- Split sysrq handling into separate commit
- Update code/ABI documentation to change loglevel bounds from 0 to 1
- Remove 'enabled' attribute from sysfs interface
- Move #ifdefs out of sysfs.c
- Fix console_clamp_loglevel() to use documented bounds
- Initialise classdev and level in try_enable_default_console()
- Fix race condition, use console_list_lock() in console_setup_class()
- Remove NULL check before device_unregister()
- Add data_race() annotations to READ_ONCE/WRITE_ONCE for KCSAN
- Remove pr_warn() for unknown loglevel source
- Add error handling for kzalloc() failure
- Make console_class constant via class_register() per Greg
- Export do_proc_dointvec() and do_proc_dointvec_conv()
- Refactor to use @conv callback approach
- Rename printk_console_loglevel() to proc_dointvec_console_loglevel()
- Simplify level assignment to `newcon->level = c->level;` per Petr
- Add missing loglevel= parameter documentation
- Change printk_sysctl_deprecated to proc_dointvec_printk_deprecated
- Add missing kernel parameter documentation for ignore_loglevel
- Reject KERN_EMERG (0) consistently
- Add comprehensive usage examples to per-console-loglevel.rst
- Add troubleshooting section with common issues and solutions
- Update ABI documentation with error conditions and permissions
- Document new struct console fields (level and classdev)
- Rename clamp_loglevel() to console_clamp_loglevel() for clarity
- Add kernel doc comments for internal API functions where it helps
- Initialise classdev in try_enable_default_console() explicitly
- Mark console devices with device_set_pm_not_required()
- Add syslog_lock around SYSLOG_ACTION_CONSOLE_{OFF,ON}
v6:
- Add .rst suffix to documentation in do_syslog
- Add loglevel table to per-console-loglevel.rst and serial-console.rst
- Add newlines between multiline bullets in per-console-loglevel.rst
- Make effective_loglevel doc more clear
- Remove ignore_per_console_loglevel doc, it's not shown in sysfs now
- Use READ_ONCE/WRITE_ONCE for con->level
- Ignore/restore per console loglevel in sysrq
- Add new fields to comment above struct console
- Remove now unused flags field on console_cmdline
- Remove WARN_ON_ONCE(!con) in effective loglevel checks
- Avoid underflow in find_and_remove_console_option if val_buf_size == 0
- Better error message on oversize in find_and_remove_loglevel_option
- Reject if clamped in find_and_remove_loglevel_option
- Clarify level setting logic in __add_preferred_console
- Move console emission check to printk_delay itself
- Use console_src_read_flags in enabled_show
- Infer if extended from con in printk_get_next_message, don't pass args
- Remove misleading comment about early consoles in console_init
- Use a goto in loglevel_store to avoid setting level in multiple places
- Mention only @flags and @level are valid in printk_get_next_message
- Use LOGLEVEL_DEBUG for max clamp in sysctls
- Update for class_create interface changes
- Move sysctl functionality out to sysfs.c
- Purge default_console_loglevel
- Update sysctl docs for kernel.printk deprecation
v5:
- Fix syntax in boot_delay
v4:
- Change base to Linus' master
- Use SRCU iterators for console walks
- Override per-console loglevels on magic sysrq
- Fix htmldocs
- Fix mistaken __user annotation in sysctl callbacks
- Consistently use indexed names (eg. ttyS0 instead of ttyS)
- Remove "The loglevel for a console can be set in many places" comment
- Remove CON_LOGLEVEL flag and infer based on >0
- Open code our dev_get_drvdata console stashing
- Split out console_effective_loglevel functions per Petr's suggestion
- Make boot_delay_msec/printk_delay check if it would be emitted
- Simplify warning on SYSLOG_ACTION_CONSOLE_LEVEL
- Save/restore ignore_per_console_loglevel on syslog console actions
- Unify min/max level checks across sysfs/proc/syslog
- Add find_and_remove_console_option to avoid affecting io/mmio options
v3:
- Update to work with John's kthread patches
- Remove force_console_loglevel, now we only have global and local levels
- Remove minimum_console_loglevel control and document how to change it
- The minimum loglevel is now only honoured on setting global/local level
- Add ignore_per_console_loglevel
- Return -EINVAL if trying to set below minimum console level
- Add parser for named console= options
- Fix docs around ignore_loglevel: it can be changed at runtime
- Fix ordering in "in order of authority" docs
- Remove duplicated default_console_loglevel doc
- Only warn once on syslog() use
v2:
- Dynamically allocate struct device*
- Document sysfs attributes in Documentation/ABI/
- Use sysfs_emit() instead of sprintf() in dev sysfs files
- Remove WARN_ON() for device_add/IS_ERR(console_class)
- Remove "soon" comment for kernel.printk
- Fix !CONFIG_PRINTK build
- Fix device_unregister() NULL dereference if called before class setup
- Add new documentation to MAINTAINERS
Chris Down (13):
printk: Avoid delaying messages that aren't solicited by any console
printk: Use effective loglevel for suppression and extended console
state
printk: console: Implement core per-console loglevel infrastructure
printk: Ignore per-console loglevel in sysrq
printk: Add synchronisation for concurrent console state changes
printk: Support toggling per-console loglevel via syslog() and cmdline
printk: console: Introduce sysfs interface for per-console loglevels
printk: Constrain hardware-addressed console checks to name position
printk: Support setting initial console loglevel via console= on
cmdline
printk: Add sysctl interface to set global loglevels
printk: docs: Add comprehensive guidance for per-console loglevels
printk: Deprecate the kernel.printk sysctl interface
printk: Purge default_console_loglevel
Documentation/ABI/testing/sysfs-class-console | 58 +++
Documentation/admin-guide/index.rst | 1 +
.../admin-guide/kernel-parameters.txt | 31 +-
.../admin-guide/per-console-loglevel.rst | 261 ++++++++++++++
Documentation/admin-guide/serial-console.rst | 37 +-
Documentation/admin-guide/sysctl/kernel.rst | 25 +-
Documentation/core-api/printk-basics.rst | 35 +-
Documentation/networking/netconsole.rst | 17 +
MAINTAINERS | 2 +
drivers/tty/sysrq.c | 22 ++
include/linux/console.h | 45 ++-
include/linux/printk.h | 9 +-
include/linux/sysctl.h | 7 +
kernel/printk/Makefile | 2 +-
kernel/printk/console_cmdline.h | 1 +
kernel/printk/internal.h | 20 +-
kernel/printk/nbcon.c | 4 +-
kernel/printk/printk.c | 339 ++++++++++++++++--
kernel/printk/sysctl.c | 66 +++-
kernel/printk/sysfs.c | 213 +++++++++++
kernel/sysctl.c | 18 +-
21 files changed, 1141 insertions(+), 72 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-console
create mode 100644 Documentation/admin-guide/per-console-loglevel.rst
create mode 100644 kernel/printk/sysfs.c
base-commit: 372a12bd5df0199aa234eaf8ef31ed7ecd61d40f
--
2.51.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
@ 2025-11-18 19:06 ` Chris Down
2025-11-18 19:33 ` Chris Down
2025-11-18 19:06 ` [PATCH v7 02/13] printk: Use effective loglevel for suppression and extended console state Chris Down
` (11 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:06 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
printk_delay() may introduce delays even when no console wants to emit
the message, which is unnecessary and may hold back messages we actually
care about. Add a check in printk_delay() to determine if any console
will print the message to avoid introducing unnecessary delays. This
change aligns with the existing behaviour of boot-delayed printk
messages, which already have a similar check.
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Chris Down <chris@chrisdown.name>
---
kernel/printk/printk.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 5aee9ffb16b9..ff8b6dbb29a7 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1307,14 +1307,12 @@ static int __init boot_delay_setup(char *str)
}
early_param("boot_delay", boot_delay_setup);
-static void boot_delay_msec(int level)
+static void boot_delay_msec(void)
{
unsigned long long k;
unsigned long timeout;
- bool suppress = !is_printk_force_console() &&
- suppress_message_printing(level);
- if ((boot_delay == 0 || system_state >= SYSTEM_RUNNING) || suppress)
+ if (boot_delay == 0 || system_state >= SYSTEM_RUNNING)
return;
k = (unsigned long long)loops_per_msec * boot_delay;
@@ -1334,7 +1332,7 @@ static void boot_delay_msec(int level)
}
}
#else
-static inline void boot_delay_msec(int level)
+static inline void boot_delay_msec(void)
{
}
#endif
@@ -2116,7 +2114,11 @@ int printk_delay_msec __read_mostly;
static inline void printk_delay(int level)
{
- boot_delay_msec(level);
+ /* If the message is forced (e.g. panic), we must delay */
+ if (!is_printk_force_console() && suppress_message_printing(level))
+ return;
+
+ boot_delay_msec();
if (unlikely(printk_delay_msec)) {
int m = printk_delay_msec;
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 02/13] printk: Use effective loglevel for suppression and extended console state
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
2025-11-18 19:06 ` [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console Chris Down
@ 2025-11-18 19:06 ` Chris Down
2025-11-18 19:07 ` [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure Chris Down
` (10 subsequent siblings)
12 siblings, 0 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:06 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
In preparation for supporting per-console loglevels, modify
printk_get_next_message() to accept the effective console loglevel
instead of individual arguments that mimic console fields. This keeps
the function console-agnostic and lockless as intended, while still
allowing per-console loglevel support to be added in later patches.
devkmsg_read() uses CONSOLE_LOGLEVEL_MOTORMOUTH to never suppress
messages. All other consoles pass their effective loglevel (currently
console_loglevel, will be per-console in later patches).
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Chris Down <chris@chrisdown.name>
---
kernel/printk/internal.h | 2 +-
kernel/printk/nbcon.c | 2 +-
kernel/printk/printk.c | 22 +++++++++++++---------
3 files changed, 15 insertions(+), 11 deletions(-)
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index f72bbfa266d6..1ed86577896c 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -333,7 +333,7 @@ struct printk_message {
};
bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
- bool is_extended, bool may_supress);
+ bool is_extended, int con_eff_level);
#ifdef CONFIG_PRINTK
void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped);
diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
index 558ef3177976..eb4c8faa213d 100644
--- a/kernel/printk/nbcon.c
+++ b/kernel/printk/nbcon.c
@@ -993,7 +993,7 @@ static bool nbcon_emit_next_record(struct nbcon_write_context *wctxt, bool use_a
if (!nbcon_context_enter_unsafe(ctxt))
return false;
- ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended, true);
+ ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended, console_loglevel);
if (!ctxt->backlog)
return nbcon_context_exit_unsafe(ctxt);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index ff8b6dbb29a7..745f89a18189 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -807,7 +807,8 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
if (ret)
return ret;
- if (!printk_get_next_message(&pmsg, atomic64_read(&user->seq), true, false)) {
+ if (!printk_get_next_message(&pmsg, atomic64_read(&user->seq), true,
+ CONSOLE_LOGLEVEL_MOTORMOUTH)) {
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
goto out;
@@ -825,7 +826,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
*/
ret = wait_event_interruptible(log_wait,
printk_get_next_message(&pmsg, atomic64_read(&user->seq), true,
- false)); /* LMM(devkmsg_read:A) */
+ CONSOLE_LOGLEVEL_MOTORMOUTH)); /* LMM(devkmsg_read:A) */
if (ret)
goto out;
}
@@ -1279,9 +1280,9 @@ module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_loglevel,
"ignore loglevel setting (prints all kernel messages to the console)");
-static bool suppress_message_printing(int level)
+static bool suppress_message_printing(int level, int con_eff_level)
{
- return (level >= console_loglevel && !ignore_loglevel);
+ return (level >= con_eff_level && !ignore_loglevel);
}
#ifdef CONFIG_BOOT_PRINTK_DELAY
@@ -2115,7 +2116,7 @@ int printk_delay_msec __read_mostly;
static inline void printk_delay(int level)
{
/* If the message is forced (e.g. panic), we must delay */
- if (!is_printk_force_console() && suppress_message_printing(level))
+ if (!is_printk_force_console() && suppress_message_printing(level, console_loglevel))
return;
boot_delay_msec();
@@ -2946,14 +2947,15 @@ void console_prepend_replay(struct printk_message *pmsg)
* @is_extended specifies if the message should be formatted for extended
* console output.
*
- * @may_supress specifies if records may be skipped based on loglevel.
+ * @con_eff_level is the effective console loglevel to use for suppression
+ * checks.
*
* Returns false if no record is available. Otherwise true and all fields
* of @pmsg are valid. (See the documentation of struct printk_message
* for information about the @pmsg fields.)
*/
bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
- bool is_extended, bool may_suppress)
+ bool is_extended, int con_eff_level)
{
struct printk_buffers *pbufs = pmsg->pbufs;
const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf);
@@ -2982,13 +2984,14 @@ bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
pmsg->seq = r.info->seq;
pmsg->dropped = r.info->seq - seq;
+
force_con = r.info->flags & LOG_FORCE_CON;
/*
* Skip records that are not forced to be printed on consoles and that
* has level above the console loglevel.
*/
- if (!force_con && may_suppress && suppress_message_printing(r.info->level))
+ if (!force_con && suppress_message_printing(r.info->level, con_eff_level))
goto out;
if (is_extended) {
@@ -3064,7 +3067,8 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co
*handover = false;
- if (!printk_get_next_message(&pmsg, con->seq, is_extended, true))
+ if (!printk_get_next_message(&pmsg, con->seq, is_extended,
+ console_loglevel))
return false;
con->dropped += pmsg.dropped;
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
2025-11-18 19:06 ` [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console Chris Down
2025-11-18 19:06 ` [PATCH v7 02/13] printk: Use effective loglevel for suppression and extended console state Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-18 19:34 ` Chris Down
2025-11-19 16:49 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 04/13] printk: Ignore per-console loglevel in sysrq Chris Down
` (9 subsequent siblings)
12 siblings, 2 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
Consoles can have vastly different latencies and throughputs. For
example, writing a message to the serial console can take on the order
of tens of milliseconds to get the UART to successfully write a message.
While this might be fine for a single, one-off message, this can cause
significant application-level stalls in situations where the kernel
writes large amounts of information to the console.
This means that while you might want to send at least INFO level
messages to (for example) netconsole, which is relatively fast, you may
only want to send at least WARN level messages to the serial console.
Such an implementation would permit debugging using the serial console
in cases that netconsole doesn't receive messages during particularly
bad system issues, while still keeping the noise low enough to avoid
inducing latency in userspace applications. To mitigate this, add such
an interface, extending the existing console loglevel controls to allow
each console to have its own loglevel.
One can't just disable the serial console, because one may actually need
it in situations where the machine is in a bad enough state that nothing
is received on netconsole. One also can't just bump the loglevel at
runtime after the issue, because usually the machine is already so
wedged by this point that it isn't responsive to such requests.
This commit adds the internal infrastructure to support per-console
log levels, which will be configurable through sysfs and the kernel
command line in future commits.
The global console_loglevel is preserved and used as the default log
level for all consoles. Each console can override this global level
with its own specific log level stored in struct console. To override
the global level, the per-console log level must be greater than 0;
otherwise, the default value of LOGLEVEL_DEFAULT (-1) ensures the
global level is used.
The existing ignore_loglevel command line parameter will override
both the global and per-console log levels.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
include/linux/console.h | 41 +++++++++--
include/linux/printk.h | 6 +-
kernel/printk/internal.h | 12 ++++
kernel/printk/nbcon.c | 4 +-
kernel/printk/printk.c | 150 ++++++++++++++++++++++++++++++++++++++-
5 files changed, 203 insertions(+), 10 deletions(-)
diff --git a/include/linux/console.h b/include/linux/console.h
index 8f10d0a85bb4..c0749a48fc3f 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -314,6 +314,8 @@ struct nbcon_write_context {
* @match: Callback for matching a console (Optional)
* @flags: Console flags. See enum cons_flags
* @index: Console index, e.g. port number
+ * @level: Per-console loglevel. -1 means use global console_loglevel,
+ * values > 0 specify console-specific filtering level
* @cflag: TTY control mode flags
* @ispeed: TTY input speed
* @ospeed: TTY output speed
@@ -342,6 +344,7 @@ struct console {
int (*match)(struct console *co, char *name, int idx, char *options);
short flags;
short index;
+ int level;
int cflag;
uint ispeed;
uint ospeed;
@@ -518,11 +521,6 @@ extern struct hlist_head console_list;
static inline short console_srcu_read_flags(const struct console *con)
{
WARN_ON_ONCE(!console_srcu_read_lock_is_held());
-
- /*
- * The READ_ONCE() matches the WRITE_ONCE() when @flags are modified
- * for registered consoles with console_srcu_write_flags().
- */
return data_race(READ_ONCE(con->flags));
}
@@ -544,6 +542,39 @@ static inline void console_srcu_write_flags(struct console *con, short flags)
WRITE_ONCE(con->flags, flags);
}
+/**
+ * console_srcu_read_loglevel - Locklessly read the console specific loglevel
+ * of a possibly registered console
+ * @con: struct console pointer of console to read loglevel from
+ *
+ * Locklessly reading @con->level provides a consistent read value because
+ * there is at most one CPU modifying @con->level and that CPU is using only
+ * read-modify-write operations to do so.
+ *
+ * Requires console_srcu_read_lock to be held, which implies that @con might
+ * be a registered console. The purpose of holding console_srcu_read_lock is
+ * to guarantee that the console state is valid (CON_SUSPENDED/CON_ENABLED)
+ * and that no exit/cleanup routines will run if the console is currently
+ * undergoing unregistration.
+ *
+ * If the caller is holding the console_list_lock or it is _certain_ that
+ * @con is not and will not become registered, the caller may read
+ * @con->level directly instead.
+ *
+ * Context: Any context.
+ * Return: The current value of the @con->level field.
+ */
+static inline int console_srcu_read_loglevel(const struct console *con)
+{
+ WARN_ON_ONCE(!console_srcu_read_lock_is_held());
+
+ /*
+ * The READ_ONCE() matches the WRITE_ONCE() when @level is modified
+ * for registered consoles.
+ */
+ return data_race(READ_ONCE(con->level));
+}
+
/* Variant of console_is_registered() when the console_list_lock is held. */
static inline bool console_is_registered_locked(const struct console *con)
{
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 45c663124c9b..644584edf3e9 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -209,6 +209,7 @@ void printk_legacy_allow_panic_sync(void);
extern bool nbcon_device_try_acquire(struct console *con);
extern void nbcon_device_release(struct console *con);
void nbcon_atomic_flush_unsafe(void);
+bool has_per_console_loglevel(const struct console *con);
bool pr_flush(int timeout_ms, bool reset_on_progress);
#else
static inline __printf(1, 0)
@@ -322,7 +323,10 @@ static inline void nbcon_device_release(struct console *con)
static inline void nbcon_atomic_flush_unsafe(void)
{
}
-
+static inline bool has_per_console_loglevel(const struct console *con)
+{
+ return false;
+}
static inline bool pr_flush(int timeout_ms, bool reset_on_progress)
{
return true;
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 1ed86577896c..41e37b44778f 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -15,6 +15,18 @@ int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write,
#define printk_sysctl_init() do { } while (0)
#endif
+enum loglevel_source {
+ LLS_GLOBAL,
+ LLS_LOCAL,
+ LLS_IGNORE_LOGLEVEL,
+};
+
+int console_clamp_loglevel(int level);
+
+enum loglevel_source
+console_effective_loglevel_source(int con_level);
+int console_effective_loglevel(int con_level);
+
#define con_printk(lvl, con, fmt, ...) \
printk(lvl pr_fmt("%s%sconsole [%s%d] " fmt), \
(con->flags & CON_NBCON) ? "" : "legacy ", \
diff --git a/kernel/printk/nbcon.c b/kernel/printk/nbcon.c
index eb4c8faa213d..fb99aaa8c5d8 100644
--- a/kernel/printk/nbcon.c
+++ b/kernel/printk/nbcon.c
@@ -961,6 +961,7 @@ static bool nbcon_emit_next_record(struct nbcon_write_context *wctxt, bool use_a
struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
struct console *con = ctxt->console;
bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED;
+ int con_level = console_srcu_read_loglevel(con);
struct printk_message pmsg = {
.pbufs = ctxt->pbufs,
};
@@ -993,7 +994,8 @@ static bool nbcon_emit_next_record(struct nbcon_write_context *wctxt, bool use_a
if (!nbcon_context_enter_unsafe(ctxt))
return false;
- ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended, console_loglevel);
+ ctxt->backlog = printk_get_next_message(&pmsg, ctxt->seq, is_extended,
+ console_effective_loglevel(con_level));
if (!ctxt->backlog)
return nbcon_context_exit_unsafe(ctxt);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 745f89a18189..928d77c56c77 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -197,6 +197,24 @@ static int __init control_devkmsg(char *str)
}
__setup("printk.devkmsg=", control_devkmsg);
+/**
+ * console_clamp_loglevel - Clamp a loglevel to valid console loglevel range
+ *
+ * @level: The loglevel to clamp
+ *
+ * Console loglevels must be within the range [LOGLEVEL_ALERT, LOGLEVEL_DEBUG + 1].
+ * This function clamps a given level to this valid range.
+ *
+ * Note: This does not allow LOGLEVEL_EMERG (0) for per-console loglevels, as
+ * level 0 is reserved for emergency messages that should always go to all consoles.
+ *
+ * Return: The clamped loglevel value
+ */
+int console_clamp_loglevel(int level)
+{
+ return clamp(level, LOGLEVEL_ALERT, LOGLEVEL_DEBUG + 1);
+}
+
char devkmsg_log_str[DEVKMSG_STR_MAX_SIZE] = "ratelimit";
#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write,
@@ -1280,11 +1298,120 @@ module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_loglevel,
"ignore loglevel setting (prints all kernel messages to the console)");
+bool has_per_console_loglevel(const struct console *con)
+{
+ return con && (console_srcu_read_loglevel(con) > 0);
+}
+
+/**
+ * is_valid_per_console_loglevel - Check if a loglevel is valid for per-console
+ *
+ * @con_level: The loglevel to check
+ *
+ * Per-console loglevels must be strictly positive (> 0). Level 0 (KERN_EMERG)
+ * is reserved for emergency messages that should go to all consoles (and so is
+ * disallowed), and -1 (LOGLEVEL_DEFAULT) means use the global console_loglevel.
+ *
+ * Return: true if con_level is a valid per-console loglevel (> 0), false
+ * otherwise
+ */
+static bool is_valid_per_console_loglevel(int con_level)
+{
+ return (con_level > 0);
+}
+
+/**
+ * console_effective_loglevel_source - Determine the source of effective loglevel
+ *
+ * @con_level: The console's per-console loglevel value
+ *
+ * This function determines which loglevel authority is in effect for a console,
+ * based on the hierarchy of controls:
+ *
+ * 1. ignore_loglevel (overrides everything - prints all messages)
+ * 2. per-console loglevel (if set and not ignored)
+ * 3. global console_loglevel (fallback)
+ *
+ * Return: The loglevel source (LLS_IGNORE_LOGLEVEL, LLS_LOCAL, or LLS_GLOBAL)
+ */
+enum loglevel_source
+console_effective_loglevel_source(int con_level)
+{
+ if (ignore_loglevel)
+ return LLS_IGNORE_LOGLEVEL;
+
+ if (is_valid_per_console_loglevel(con_level))
+ return LLS_LOCAL;
+
+ return LLS_GLOBAL;
+}
+
+/**
+ * console_effective_loglevel - Get the effective loglevel for a console
+ *
+ * @con_level: The console's per-console loglevel value
+ *
+ * This function returns the actual loglevel value that should be used for
+ * message filtering for a console, taking into account all loglevel controls
+ * (global, per-console, and ignore_loglevel).
+ *
+ * The effective loglevel is used to determine which messages get printed to
+ * the console. Messages with priority less than the effective level are printed.
+ *
+ * Return: The effective loglevel value to use for filtering
+ */
+int console_effective_loglevel(int con_level)
+{
+ enum loglevel_source source;
+ int level;
+
+ source = console_effective_loglevel_source(con_level);
+
+ switch (source) {
+ case LLS_IGNORE_LOGLEVEL:
+ level = CONSOLE_LOGLEVEL_MOTORMOUTH;
+ break;
+ case LLS_LOCAL:
+ level = con_level;
+ break;
+ case LLS_GLOBAL:
+ level = console_loglevel;
+ break;
+ default:
+ pr_warn("Unhandled console loglevel source: %d", source);
+ level = console_loglevel;
+ break;
+ }
+
+ return level;
+}
+
static bool suppress_message_printing(int level, int con_eff_level)
{
return (level >= con_eff_level && !ignore_loglevel);
}
+static bool suppress_message_printing_everywhere(int level)
+{
+ bool suppress_everywhere = true;
+ struct console *con;
+ int cookie;
+
+ cookie = console_srcu_read_lock();
+
+ for_each_console_srcu(con) {
+ int con_level = console_srcu_read_loglevel(con);
+
+ if (!suppress_message_printing(level, console_effective_loglevel(con_level))) {
+ suppress_everywhere = false;
+ break;
+ }
+ }
+ console_srcu_read_unlock(cookie);
+
+ return suppress_everywhere;
+}
+
#ifdef CONFIG_BOOT_PRINTK_DELAY
static int boot_delay; /* msecs delay after each printk during bootup */
@@ -2116,7 +2243,8 @@ int printk_delay_msec __read_mostly;
static inline void printk_delay(int level)
{
/* If the message is forced (e.g. panic), we must delay */
- if (!is_printk_force_console() && suppress_message_printing(level, console_loglevel))
+ if (!is_printk_force_console() &&
+ suppress_message_printing_everywhere(level))
return;
boot_delay_msec();
@@ -3059,6 +3187,7 @@ struct printk_buffers printk_shared_pbufs;
static bool console_emit_next_record(struct console *con, bool *handover, int cookie)
{
bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED;
+ int con_level = console_srcu_read_loglevel(con);
char *outbuf = &printk_shared_pbufs.outbuf[0];
struct printk_message pmsg = {
.pbufs = &printk_shared_pbufs,
@@ -3068,7 +3197,7 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co
*handover = false;
if (!printk_get_next_message(&pmsg, con->seq, is_extended,
- console_loglevel))
+ console_effective_loglevel(con_level)))
return false;
con->dropped += pmsg.dropped;
@@ -3817,6 +3946,9 @@ static int try_enable_preferred_console(struct console *newcon,
if (newcon->index < 0)
newcon->index = c->index;
+ /* TODO: will be configurable in a later patch */
+ newcon->level = LOGLEVEL_DEFAULT;
+
if (_braille_register_console(newcon, c))
return 0;
@@ -3835,8 +3967,12 @@ static int try_enable_preferred_console(struct console *newcon,
* without matching. Accept the pre-enabled consoles only when match()
* and setup() had a chance to be called.
*/
- if (newcon->flags & CON_ENABLED && c->user_specified == user_specified)
+ if (newcon->flags & CON_ENABLED && c->user_specified == user_specified) {
+ /* Ensure level is initialized for pre-enabled consoles */
+ if (newcon->level == 0)
+ newcon->level = LOGLEVEL_DEFAULT;
return 0;
+ }
return -ENOENT;
}
@@ -4039,6 +4175,14 @@ void register_console(struct console *newcon)
}
newcon->dropped = 0;
+
+ /*
+ * Don't unconditionally overwrite, it may have been set on the command
+ * line already.
+ */
+ if (newcon->level == 0)
+ newcon->level = LOGLEVEL_DEFAULT;
+
init_seq = get_init_console_seq(newcon, bootcon_registered);
if (newcon->flags & CON_NBCON) {
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 04/13] printk: Ignore per-console loglevel in sysrq
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (2 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-19 16:51 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 05/13] printk: Add synchronisation for concurrent console state changes Chris Down
` (8 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
When the global console loglevel is changed via sysrq, clear any
per-console loglevels that may have been set. This ensures that the
sysrq loglevel change takes effect immediately across all consoles.
Warn once if any per-console loglevels were being overridden.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
drivers/tty/sysrq.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 97f8a9a52285..1763eaefd581 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -51,6 +51,7 @@
#include <linux/syscalls.h>
#include <linux/of.h>
#include <linux/rcupdate.h>
+#include <linux/console.h>
#include <asm/ptrace.h>
#include <asm/irq_regs.h>
@@ -101,10 +102,25 @@ __setup("sysrq_always_enabled", sysrq_always_enabled_setup);
static void sysrq_handle_loglevel(u8 key)
{
u8 loglevel = key - '0';
+ int cookie;
+ struct console *con;
console_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
pr_info("Loglevel set to %u\n", loglevel);
console_loglevel = loglevel;
+
+ /*
+ * When changing loglevel via sysrq, override all per-console loglevels
+ * to ensure the new global loglevel takes effect immediately and all
+ * consoles see the messages.
+ */
+ cookie = console_srcu_read_lock();
+ for_each_console_srcu(con) {
+ if (has_per_console_loglevel(con))
+ pr_warn_once("Overriding per-console loglevel from sysrq\n");
+ WRITE_ONCE(con->level, LOGLEVEL_DEFAULT);
+ }
+ console_srcu_read_unlock(cookie);
}
static const struct sysrq_key_op sysrq_loglevel_op = {
.handler = sysrq_handle_loglevel,
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 05/13] printk: Add synchronisation for concurrent console state changes
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (3 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 04/13] printk: Ignore per-console loglevel in sysrq Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-19 16:58 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 06/13] printk: Support toggling per-console loglevel via syslog() and cmdline Chris Down
` (7 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
The syslog actions SYSLOG_ACTION_CONSOLE_OFF and
SYSLOG_ACTION_CONSOLE_ON currently run without any locking. This creates
a race condition if two processes attempt to toggle the console state
simultaneously.
While this race has existed for donkey's years, it was previously
somewhat tolerable because it involved only a single integer variable
(saved_console_loglevel). However, upcoming changes will introduce a
second piece of state (saved_ignore_per_console_loglevel) to be saved
and restored. With two variables, this can result in saved state getting
lost.
Here is a demonstration:
CPU 0 (SYSLOG_ACTION_CONSOLE_OFF) CPU 1 (SYSLOG_ACTION_CONSOLE_ON)
--------------------------------- --------------------------------
// saved_console_loglevel is DEFAULT
if (saved == DEFAULT) (True)
saved = console_loglevel (7)
// Race triggers here
if (saved != DEFAULT) (True)
loglevel = saved (7)
saved = DEFAULT
// CPU 0 continues, unaware saved
// was just reset by CPU 1
loglevel = minimum_console_loglevel (1)
The result is that the console is now set to the minimum loglevel, but
saved_console_loglevel is LOGLEVEL_DEFAULT. A subsequent CONSOLE_ON call
will see saved == LOGLEVEL_DEFAULT and thus refuse to restore the
original loglevel. The console is effectively stuck in quiet mode until
manually reset via sysctl. Oh dear.
The callers of do_syslog are syscalls (so user context) or procfs write
handlers. These contexts are allowed to sleep, so acquiring a mutex is
safe. We also already make use of syslog_lock in
SYSLOG_ACTION_SIZE_UNREAD and SYSLOG_ACTION_READ, so this is consistent.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
kernel/printk/printk.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 928d77c56c77..b8679b0da42f 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1903,16 +1903,20 @@ int do_syslog(int type, char __user *buf, int len, int source)
break;
/* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_OFF:
+ mutex_lock(&syslog_lock);
if (saved_console_loglevel == LOGLEVEL_DEFAULT)
saved_console_loglevel = console_loglevel;
console_loglevel = minimum_console_loglevel;
+ mutex_unlock(&syslog_lock);
break;
/* Enable logging to console */
case SYSLOG_ACTION_CONSOLE_ON:
+ mutex_lock(&syslog_lock);
if (saved_console_loglevel != LOGLEVEL_DEFAULT) {
console_loglevel = saved_console_loglevel;
saved_console_loglevel = LOGLEVEL_DEFAULT;
}
+ mutex_unlock(&syslog_lock);
break;
/* Set level of messages printed to console */
case SYSLOG_ACTION_CONSOLE_LEVEL:
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 06/13] printk: Support toggling per-console loglevel via syslog() and cmdline
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (4 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 05/13] printk: Add synchronisation for concurrent console state changes Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-20 10:05 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels Chris Down
` (6 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
A new module parameter (ignore_per_console_loglevel) is added, which can
be set via the kernel command line or at runtime through
/sys/module/printk/parameters/ignore_per_console_loglevel. When set, the
per-console loglevels are ignored, and the global console loglevel
(console_loglevel) is used for all consoles.
During sysrq, we temporarily disable per-console loglevels to ensure all
requisite messages are printed to the console. This is necessary because
sysrq is often used in dire circumstances where access to
/sys/class/console may not be trivially possible.
Additionally, the syslog actions SYSLOG_ACTION_CONSOLE_ON and
SYSLOG_ACTION_CONSOLE_OFF are augmented to save and restore the state of
ignore_per_console_loglevel. This allows administrators to enable or
disable per-console loglevels dynamically using the syslog() system
call, as supported in userspace by things like dmesg.
This is useful when debugging issues with message emission, or when
needing to quickly break glass and revert to global loglevel only.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
Documentation/admin-guide/index.rst | 1 +
.../admin-guide/kernel-parameters.txt | 7 ++
.../admin-guide/per-console-loglevel.rst | 71 +++++++++++++++++++
MAINTAINERS | 1 +
drivers/tty/sysrq.c | 6 ++
include/linux/printk.h | 2 +
kernel/printk/printk.c | 29 +++++++-
7 files changed, 114 insertions(+), 3 deletions(-)
create mode 100644 Documentation/admin-guide/per-console-loglevel.rst
diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst
index 259d79fbeb94..5f0ae9c4017b 100644
--- a/Documentation/admin-guide/index.rst
+++ b/Documentation/admin-guide/index.rst
@@ -155,6 +155,7 @@ How to configure your hardware within your Linux system.
media/index
nvme-multipath
parport
+ per-console-loglevel
pnp
rapidio
rtc
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 3edc5ce0e2a3..7b62a99489b8 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2177,6 +2177,13 @@
could change it dynamically, usually by
/sys/module/printk/parameters/ignore_loglevel.
+ ignore_per_console_loglevel [KNL,EARLY]
+ Ignore all per-console loglevel settings
+ and use only the global console_loglevel for all
+ consoles. This can also be set at runtime via
+ /sys/module/printk/parameters/ignore_per_console_loglevel.
+ See Documentation/admin-guide/per-console-loglevel.rst.
+
ignore_rlimit_data
Ignore RLIMIT_DATA setting for data mappings,
print warning at first misuse. Can be changed via
diff --git a/Documentation/admin-guide/per-console-loglevel.rst b/Documentation/admin-guide/per-console-loglevel.rst
new file mode 100644
index 000000000000..1f8f1eabc75c
--- /dev/null
+++ b/Documentation/admin-guide/per-console-loglevel.rst
@@ -0,0 +1,71 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. _per_console_loglevel:
+
+Per-console loglevel support
+============================
+
+Motivation
+----------
+
+Consoles can have vastly different latencies and throughputs. For example,
+writing a message to the serial console can take on the order of tens of
+milliseconds to get the UART to successfully write a message. While this might
+be fine for a single, one-off message, this can cause significant
+application-level stalls in situations where the kernel writes large amounts of
+information to the console.
+
+This means that while you might want to send at least INFO level messages to
+(for example) netconsole, which is relatively fast, you may only want to send at
+least WARN level messages to the serial console. This permits debugging
+using the serial console in cases that netconsole doesn't receive messages
+during particularly bad system issues, while still keeping the noise low enough
+to avoid inducing latency in userspace applications.
+
+Loglevel
+--------
+
+Kernel loglevels are defined thus:
+
++---+--------------+-----------------------------------+
+| 0 | KERN_EMERG | system is unusable |
++---+--------------+-----------------------------------+
+| 1 | KERN_ALERT | action must be taken immediately |
++---+--------------+-----------------------------------+
+| 2 | KERN_CRIT | critical conditions |
++---+--------------+-----------------------------------+
+| 3 | KERN_ERR | error conditions |
++---+--------------+-----------------------------------+
+| 4 | KERN_WARNING | warning conditions |
++---+--------------+-----------------------------------+
+| 5 | KERN_NOTICE | normal but significant condition |
++---+--------------+-----------------------------------+
+| 6 | KERN_INFO | informational |
++---+--------------+-----------------------------------+
+| 7 | KERN_DEBUG | debug-level messages |
++---+--------------+-----------------------------------+
+
+Tunables
+--------
+
+In order to allow tuning per-console loglevels, the following controls exist:
+
+Global
+~~~~~~
+
+The global loglevel is set by the ``kernel.console_loglevel`` sysctl, which can
+also be set as ``loglevel=`` on the kernel command line.
+
+The printk module also takes two parameters which modify this behaviour
+further:
+
+* ``ignore_loglevel`` on the kernel command line or set in printk parameters:
+ Emit all messages. All other controls are ignored if this is present.
+
+* ``ignore_per_console_loglevel`` on the kernel command line or set in printk
+ parameters: Ignore all per-console loglevels and use the global loglevel.
+
+The default value for ``kernel.console_loglevel`` comes from
+``CONFIG_CONSOLE_LOGLEVEL_DEFAULT``, or ``CONFIG_CONSOLE_LOGLEVEL_QUIET`` if
+``quiet`` is passed on the kernel command line.
+
diff --git a/MAINTAINERS b/MAINTAINERS
index e56494c7a956..8018a4db2d9f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20505,6 +20505,7 @@ 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
+F: Documentation/admin-guide/per-console-loglevel.rst
F: Documentation/core-api/printk-basics.rst
F: include/linux/printk.h
F: kernel/printk/
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 1763eaefd581..6cd2750e962b 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -52,6 +52,7 @@
#include <linux/of.h>
#include <linux/rcupdate.h>
#include <linux/console.h>
+#include <linux/printk.h>
#include <asm/ptrace.h>
#include <asm/irq_regs.h>
@@ -599,12 +600,16 @@ static void __sysrq_put_key_op(u8 key, const struct sysrq_key_op *op_p)
void __handle_sysrq(u8 key, bool check_mask)
{
const struct sysrq_key_op *op_p;
+ bool orig_ignore_per_console_loglevel;
int orig_suppress_printk;
int i;
orig_suppress_printk = suppress_printk;
suppress_printk = 0;
+ orig_ignore_per_console_loglevel = ignore_per_console_loglevel;
+ ignore_per_console_loglevel = true;
+
rcu_sysrq_start();
rcu_read_lock();
/*
@@ -650,6 +655,7 @@ void __handle_sysrq(u8 key, bool check_mask)
rcu_read_unlock();
rcu_sysrq_end();
+ ignore_per_console_loglevel = orig_ignore_per_console_loglevel;
suppress_printk = orig_suppress_printk;
}
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 644584edf3e9..e36cae1805b2 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -73,6 +73,8 @@ extern int console_printk[];
#define minimum_console_loglevel (console_printk[2])
#define default_console_loglevel (console_printk[3])
+extern bool ignore_per_console_loglevel;
+
extern void console_verbose(void);
/* strlen("ratelimit") + 1 */
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index b8679b0da42f..80204cbb7bc8 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -104,6 +104,9 @@ DEFINE_STATIC_SRCU(console_srcu);
*/
int __read_mostly suppress_printk;
+/* The sysrq infrastructure needs this even on !CONFIG_PRINTK. */
+bool __read_mostly ignore_per_console_loglevel;
+
#ifdef CONFIG_LOCKDEP
static struct lockdep_map console_lock_dep_map = {
.name = "console_lock"
@@ -1298,9 +1301,21 @@ module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_loglevel,
"ignore loglevel setting (prints all kernel messages to the console)");
+static int __init ignore_per_console_loglevel_setup(char *str)
+{
+ ignore_per_console_loglevel = true;
+ return 0;
+}
+
+early_param("ignore_per_console_loglevel", ignore_per_console_loglevel_setup);
+module_param(ignore_per_console_loglevel, bool, 0644);
+MODULE_PARM_DESC(
+ ignore_per_console_loglevel,
+ "ignore per-console loglevel setting (only respect global console loglevel)");
+
bool has_per_console_loglevel(const struct console *con)
{
- return con && (console_srcu_read_loglevel(con) > 0);
+ return !ignore_per_console_loglevel && con && (console_srcu_read_loglevel(con) > 0);
}
/**
@@ -1340,7 +1355,7 @@ console_effective_loglevel_source(int con_level)
if (ignore_loglevel)
return LLS_IGNORE_LOGLEVEL;
- if (is_valid_per_console_loglevel(con_level))
+ if (!ignore_per_console_loglevel && is_valid_per_console_loglevel(con_level))
return LLS_LOCAL;
return LLS_GLOBAL;
@@ -1863,6 +1878,7 @@ int do_syslog(int type, char __user *buf, int len, int source)
struct printk_info info;
bool clear = false;
static int saved_console_loglevel = LOGLEVEL_DEFAULT;
+ static bool saved_ignore_per_console_loglevel;
int error;
error = check_syslog_permissions(type, source);
@@ -1904,9 +1920,12 @@ int do_syslog(int type, char __user *buf, int len, int source)
/* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_OFF:
mutex_lock(&syslog_lock);
- if (saved_console_loglevel == LOGLEVEL_DEFAULT)
+ if (saved_console_loglevel == LOGLEVEL_DEFAULT) {
saved_console_loglevel = console_loglevel;
+ saved_ignore_per_console_loglevel = ignore_per_console_loglevel;
+ }
console_loglevel = minimum_console_loglevel;
+ ignore_per_console_loglevel = true;
mutex_unlock(&syslog_lock);
break;
/* Enable logging to console */
@@ -1914,12 +1933,16 @@ int do_syslog(int type, char __user *buf, int len, int source)
mutex_lock(&syslog_lock);
if (saved_console_loglevel != LOGLEVEL_DEFAULT) {
console_loglevel = saved_console_loglevel;
+ ignore_per_console_loglevel = saved_ignore_per_console_loglevel;
saved_console_loglevel = LOGLEVEL_DEFAULT;
}
mutex_unlock(&syslog_lock);
break;
/* Set level of messages printed to console */
case SYSLOG_ACTION_CONSOLE_LEVEL:
+ if (!ignore_per_console_loglevel)
+ pr_warn_once(
+ "SYSLOG_ACTION_CONSOLE_LEVEL is ignored by consoles with an explicitly set per-console loglevel, see Documentation/admin-guide/per-console-loglevel.rst\n");
if (len < 1 || len > 8)
return -EINVAL;
if (len < minimum_console_loglevel)
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (5 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 06/13] printk: Support toggling per-console loglevel via syslog() and cmdline Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-20 16:50 ` Petr Mladek
2025-11-21 11:38 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 08/13] printk: Constrain hardware-addressed console checks to name position Chris Down
` (5 subsequent siblings)
12 siblings, 2 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
A sysfs interface under /sys/class/console/ is created that permits
viewing and configuring per-console attributes. This is the main
interface with which we expect users to interact with and configure
per-console loglevels.
Each console device now has its own directory (for example,
/sys/class/console/ttyS0/) containing the following attributes:
- effective_loglevel (ro): The effective loglevel for the console after
considering all loglevel authorities (e.g., global loglevel,
per-console loglevel).
- effective_loglevel_source (ro): The source of the effective loglevel
(e.g., local, global, ignore_loglevel).
- loglevel (rw): The per-console loglevel. Writing a value between 0
(KERN_EMERG) and 8 (KERN_DEBUG + 1) sets the per-console loglevel.
Writing -1 disables the per-console loglevel.
In terms of technical implementation, we embed a device pointer in the
console struct, and register each console using it so we can expose
attributes in sysfs. We currently expose the following attributes:
% ls -l /sys/class/console/ttyS0/
total 0
lrwxrwxrwx 1 root root 0 Oct 23 13:17 subsystem -> ../../../../class/console/
-r--r--r-- 1 root root 4096 Oct 23 13:18 effective_loglevel
-r--r--r-- 1 root root 4096 Oct 23 13:18 effective_loglevel_source
-rw-r--r-- 1 root root 4096 Oct 23 13:18 loglevel
-rw-r--r-- 1 root root 4096 Oct 23 13:17 uevent
The lifecycle of this classdev looks like this on registration:
register_console(con)/printk_late_init()
console_register_device(con)
device_initialize(con->classdev) # kref_init: refcount = 1
device_add(con->classdev) # get_device: refcount++ (to 2)
At stable state, the refcount is two.
Console unregistration looks like this:
unregister_console_locked(con)
struct device *dev = console->classdev;
console->classdev = NULL;
device_unregister(dev)
device_del(dev)
device_remove_class_symlinks(dev)
sysfs_delete_link()
kernfs_remove_by_name_ns()
__kernfs_remove()
kernfs_drain()
kernfs_drain_open_files() # wait for close()
kobject_del(&dev->kobj) # removes from sysfs, does NOT change refcount
put_device(dev) # kref_put: refcount-- (from 2 to 1)
put_device(dev) # kref_put: refcount-- (from 1 to 0)
kobject_release()
kobject_cleanup()
device_release()
console_classdev_release(dev)
kfree(dev)
Signed-off-by: Chris Down <chris@chrisdown.name>
---
Documentation/ABI/testing/sysfs-class-console | 58 +++++
.../admin-guide/per-console-loglevel.rst | 38 ++++
Documentation/core-api/printk-basics.rst | 35 +--
Documentation/networking/netconsole.rst | 13 ++
MAINTAINERS | 1 +
include/linux/console.h | 4 +
kernel/printk/Makefile | 2 +-
kernel/printk/internal.h | 6 +
kernel/printk/printk.c | 14 ++
kernel/printk/sysfs.c | 213 ++++++++++++++++++
10 files changed, 366 insertions(+), 18 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-class-console
create mode 100644 kernel/printk/sysfs.c
diff --git a/Documentation/ABI/testing/sysfs-class-console b/Documentation/ABI/testing/sysfs-class-console
new file mode 100644
index 000000000000..8c0f0cf3f6c5
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-console
@@ -0,0 +1,58 @@
+What: /sys/class/console/
+Date: November 2025
+Contact: Chris Down <chris@chrisdown.name>
+Description: Interface for viewing and setting per-console attributes, like
+ the per-console loglevel. For a high-level document describing
+ the motivations for this interface and related non-sysfs
+ controls, see
+ Documentation/admin-guide/per-console-loglevel.rst.
+
+What: /sys/class/console/<C>/effective_loglevel
+Date: November 2025
+Contact: Chris Down <chris@chrisdown.name>
+Permissions: 0444 (world readable)
+Description: Read only. The currently effective loglevel for this console.
+ All messages emitted with a loglevel below the effective value
+ will be emitted to the console.
+
+What: /sys/class/console/<C>/effective_loglevel_source
+Date: November 2025
+Contact: Chris Down <chris@chrisdown.name>
+Permissions: 0444 (world readable)
+Description: Read only. The currently effective loglevel source for this
+ console -- for example, whether it was set globally, or whether
+ it was set locally for this console.
+
+ Possible values are:
+ =============== ============================================
+ local The loglevel comes from the console's
+ per-console loglevel setting.
+ global The loglevel comes from the global
+ console_loglevel.
+ ignore_loglevel Both the per-console loglevel and global
+ loglevel are ignored as ignore_loglevel is
+ present on the kernel command line.
+ =============== ============================================
+
+What: /sys/class/console/<C>/loglevel
+Date: November 2025
+Contact: Chris Down <chris@chrisdown.name>
+Permissions: 0644 (root read/write, user read)
+Description: Read write. The current per-console loglevel, which will take
+ effect if not overridden by other non-sysfs controls (see
+ Documentation/admin-guide/per-console-loglevel.rst).
+
+ Valid values:
+ 1-8: LOGLEVEL_ALERT (1) to LOGLEVEL_DEBUG + 1 (8)
+ -1: Use global console_loglevel (default)
+ 0: Explicitly rejected (KERN_EMERG not allowed)
+
+ Error codes:
+ EINVAL: Non-numeric input
+ ERANGE: Value out of valid range (< 1 or > 8, excluding -1)
+ ERANGE: Value is 0 (KERN_EMERG not allowed for per-console)
+ ERANGE: Value below system minimum_console_loglevel
+
+ The special value -1 disables the per-console loglevel, making
+ the console use the global loglevel instead.
+
diff --git a/Documentation/admin-guide/per-console-loglevel.rst b/Documentation/admin-guide/per-console-loglevel.rst
index 1f8f1eabc75c..4908d5d8ed4f 100644
--- a/Documentation/admin-guide/per-console-loglevel.rst
+++ b/Documentation/admin-guide/per-console-loglevel.rst
@@ -69,3 +69,41 @@ The default value for ``kernel.console_loglevel`` comes from
``CONFIG_CONSOLE_LOGLEVEL_DEFAULT``, or ``CONFIG_CONSOLE_LOGLEVEL_QUIET`` if
``quiet`` is passed on the kernel command line.
+Console attributes
+~~~~~~~~~~~~~~~~~~
+
+Registered consoles are exposed at ``/sys/class/console``. For example, if you
+are using ``ttyS0``, the console backing it can be viewed at
+``/sys/class/console/ttyS0/``. The following files are available:
+
+* ``effective_loglevel`` (r): The effective loglevel after considering all
+ loglevel authorities. For example, it shows the value of the console-specific
+ loglevel when a console-specific loglevel is defined, and shows the global
+ console loglevel value when the console-specific one is not defined.
+
+* ``effective_loglevel_source`` (r): The loglevel authority which resulted in
+ the effective loglevel being set. The following values can be present:
+
+ * ``local``: The console-specific loglevel is in effect.
+
+ * ``global``: The global loglevel (``kernel.console_loglevel``) is in
+ effect. Set a console-specific loglevel to override it.
+
+ * ``ignore_loglevel``: ``ignore_loglevel`` was specified on the kernel
+ command line or at ``/sys/module/printk/parameters/ignore_loglevel``.
+ Disable it to use level controls.
+
+* ``loglevel`` (rw): The local, console-specific loglevel for this console.
+ This will be in effect if no other global control overrides it. Look at
+ ``effective_loglevel`` and ``effective_loglevel_source`` to verify that.
+
+Deprecated
+~~~~~~~~~~
+
+* ``kernel.printk`` sysctl: this takes four values, setting
+ ``kernel.console_loglevel``, ``kernel.default_message_loglevel``, the minimum
+ console loglevel, and a fourth unused value. The interface is generally
+ considered to quite confusing, doesn't perform checks on the values given,
+ and is unaware of per-console loglevel semantics.
+
+Chris Down <chris@chrisdown.name>, 18-November-2025
diff --git a/Documentation/core-api/printk-basics.rst b/Documentation/core-api/printk-basics.rst
index 2dde24ca7d9f..bfad359505bb 100644
--- a/Documentation/core-api/printk-basics.rst
+++ b/Documentation/core-api/printk-basics.rst
@@ -54,32 +54,33 @@ string, the log level is not a separate argument). The available log levels are:
The log level specifies the importance of a message. The kernel decides whether
to show the message immediately (printing it to the current console) depending
-on its log level and the current *console_loglevel* (a kernel variable). If the
-message priority is higher (lower log level value) than the *console_loglevel*
-the message will be printed to the console.
+on its log level and the current global *console_loglevel* or local per-console
+loglevel (kernel variables). If the message priority is higher (lower log level
+value) than the effective loglevel the message will be printed to the console.
If the log level is omitted, the message is printed with ``KERN_DEFAULT``
level.
-You can check the current *console_loglevel* with::
+You can check the current console's loglevel -- for example if you want to
+check the loglevel for serial consoles:
- $ cat /proc/sys/kernel/printk
- 4 4 1 7
+ $ cat /sys/class/console/ttyS0/effective_loglevel
+ 6
+ $ cat /sys/class/console/ttyS0/effective_loglevel_source
+ local
-The result shows the *current*, *default*, *minimum* and *boot-time-default* log
-levels.
+To change the default loglevel for all consoles, simply write the desired level
+to ``/proc/sys/kernel/console_loglevel``. For example::
-To change the current console_loglevel simply write the desired level to
-``/proc/sys/kernel/printk``. For example, to print all messages to the console::
+ # echo 5 > /proc/sys/kernel/console_loglevel
- # echo 8 > /proc/sys/kernel/printk
+This sets the console_loglevel to print KERN_WARNING (4) or more severe
+messages to console. Consoles with a per-console loglevel set will ignore it
+unless ``ignore_per_console_loglevel`` is set on the kernel command line or at
+``/sys/module/printk/parameters/ignore_per_console_loglevel``.
-Another way, using ``dmesg``::
-
- # dmesg -n 5
-
-sets the console_loglevel to print KERN_WARNING (4) or more severe messages to
-console. See ``dmesg(1)`` for more information.
+For more information on per-console loglevels, see
+Documentation/admin-guide/per-console-loglevel.rst.
As an alternative to printk() you can use the ``pr_*()`` aliases for
logging. This family of macros embed the log level in the macro names. For
diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
index 59cb9982afe6..5fabcf9367d1 100644
--- a/Documentation/networking/netconsole.rst
+++ b/Documentation/networking/netconsole.rst
@@ -78,6 +78,19 @@ Built-in netconsole starts immediately after the TCP stack is
initialized and attempts to bring up the supplied dev at the supplied
address.
+You can also set a loglevel at runtime::
+
+ $ ls -l /sys/class/console/netcon0/
+ total 0
+ lrwxrwxrwx 1 root root 0 May 18 13:28 subsystem -> ../../../../class/console/
+ -r--r--r-- 1 root root 4096 May 18 13:28 effective_loglevel
+ -r--r--r-- 1 root root 4096 May 18 13:28 effective_loglevel_source
+ -r--r--r-- 1 root root 4096 May 18 13:28 enabled
+ -rw-r--r-- 1 root root 4096 May 18 13:28 loglevel
+ -rw-r--r-- 1 root root 4096 May 18 13:28 uevent
+
+See Documentation/admin-guide/per-console-loglevel.rst for more information.
+
The remote host has several options to receive the kernel messages,
for example:
diff --git a/MAINTAINERS b/MAINTAINERS
index 8018a4db2d9f..9a509a0bc65a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20505,6 +20505,7 @@ 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
+F: Documentation/ABI/testing/sysfs-class-console
F: Documentation/admin-guide/per-console-loglevel.rst
F: Documentation/core-api/printk-basics.rst
F: include/linux/printk.h
diff --git a/include/linux/console.h b/include/linux/console.h
index c0749a48fc3f..4b2b87079cd8 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -16,6 +16,7 @@
#include <linux/atomic.h>
#include <linux/bits.h>
+#include <linux/device.h>
#include <linux/irq_work.h>
#include <linux/rculist.h>
#include <linux/rcuwait.h>
@@ -323,6 +324,8 @@ struct nbcon_write_context {
* @dropped: Number of unreported dropped ringbuffer records
* @data: Driver private data
* @node: hlist node for the console list
+ * @classdev: sysfs class device for this console, used to expose
+ * per-console controls in /sys/class/console/<name>/
*
* @nbcon_state: State for nbcon consoles
* @nbcon_seq: Sequence number of the next record for nbcon to print
@@ -352,6 +355,7 @@ struct console {
unsigned long dropped;
void *data;
struct hlist_node node;
+ struct device *classdev;
/* nbcon console specific members */
diff --git a/kernel/printk/Makefile b/kernel/printk/Makefile
index f8004ac3983d..19e4919a13a7 100644
--- a/kernel/printk/Makefile
+++ b/kernel/printk/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y = printk.o
-obj-$(CONFIG_PRINTK) += printk_safe.o nbcon.o
+obj-$(CONFIG_PRINTK) += sysfs.o printk_safe.o nbcon.o
obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o
obj-$(CONFIG_PRINTK_INDEX) += index.o
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 41e37b44778f..3b3a3c982412 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -48,6 +48,9 @@ int console_effective_loglevel(int con_level);
#ifdef CONFIG_PRINTK
+void console_register_device(struct console *new);
+void console_setup_class(void);
+
#ifdef CONFIG_PRINTK_CALLER
#define PRINTK_PREFIX_MAX 48
#else
@@ -219,6 +222,9 @@ static inline void nbcon_kthreads_wake(void) { }
static inline bool console_is_usable(struct console *con, short flags,
bool use_atomic) { return false; }
+static inline void console_register_device(struct console *new) { }
+static inline void console_setup_class(void) { }
+
#endif /* CONFIG_PRINTK */
extern bool have_boot_console;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 80204cbb7bc8..62114aa61999 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -4010,6 +4010,9 @@ static void try_enable_default_console(struct console *newcon)
if (newcon->index < 0)
newcon->index = 0;
+ newcon->level = LOGLEVEL_DEFAULT;
+ newcon->classdev = NULL;
+
if (console_call_setup(newcon, NULL) != 0)
return;
@@ -4264,6 +4267,7 @@ void register_console(struct console *newcon)
if (use_device_lock)
newcon->device_unlock(newcon, flags);
+ console_register_device(newcon);
console_sysfs_notify();
/*
@@ -4379,6 +4383,13 @@ static int unregister_console_locked(struct console *console)
if (console->flags & CON_NBCON)
nbcon_free(console);
+ if (console->classdev) {
+ struct device *dev = console->classdev;
+ console->classdev = NULL;
+ device_unregister(dev);
+ put_device(dev);
+ }
+
console_sysfs_notify();
if (console->exit)
@@ -4528,6 +4539,9 @@ static int __init printk_late_init(void)
console_cpu_notify, NULL);
WARN_ON(ret < 0);
printk_sysctl_init();
+
+ console_setup_class();
+
return 0;
}
late_initcall(printk_late_init);
diff --git a/kernel/printk/sysfs.c b/kernel/printk/sysfs.c
new file mode 100644
index 000000000000..5252e6e04908
--- /dev/null
+++ b/kernel/printk/sysfs.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/console.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include "internal.h"
+
+static const char *
+console_effective_loglevel_source_str(const struct console *con)
+{
+ enum loglevel_source source;
+ const char *str;
+ int con_level;
+ int cookie;
+
+ cookie = console_srcu_read_lock();
+ con_level = console_srcu_read_loglevel(con);
+ console_srcu_read_unlock(cookie);
+ source = console_effective_loglevel_source(con_level);
+
+ switch (source) {
+ case LLS_IGNORE_LOGLEVEL:
+ str = "ignore_loglevel";
+ break;
+ case LLS_LOCAL:
+ str = "local";
+ break;
+ case LLS_GLOBAL:
+ str = "global";
+ break;
+ default:
+ str = "unknown";
+ break;
+ }
+
+ return str;
+}
+
+static ssize_t effective_loglevel_source_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct console *con = dev_get_drvdata(dev);
+ const char *str;
+
+ str = console_effective_loglevel_source_str(con);
+ return sysfs_emit(buf, "%s\n", str);
+}
+
+static DEVICE_ATTR_RO(effective_loglevel_source);
+
+static ssize_t effective_loglevel_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct console *con = dev_get_drvdata(dev);
+ int con_level;
+ int cookie;
+
+ cookie = console_srcu_read_lock();
+ con_level = console_srcu_read_loglevel(con);
+ console_srcu_read_unlock(cookie);
+ return sysfs_emit(buf, "%d\n", console_effective_loglevel(con_level));
+}
+
+static DEVICE_ATTR_RO(effective_loglevel);
+
+static ssize_t loglevel_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct console *con = dev_get_drvdata(dev);
+ int con_level;
+ int cookie;
+
+ cookie = console_srcu_read_lock();
+ con_level = console_srcu_read_loglevel(con);
+ console_srcu_read_unlock(cookie);
+ return sysfs_emit(buf, "%d\n", con_level);
+}
+
+static ssize_t loglevel_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct console *con = dev_get_drvdata(dev);
+ ssize_t ret;
+ int level;
+ int cookie;
+
+ ret = kstrtoint(buf, 10, &level);
+ if (ret < 0)
+ return ret;
+
+ /* -1 means "use global loglevel" */
+ if (level == -1)
+ goto out;
+
+ /*
+ * Reject level 0 (KERN_EMERG) - per-console loglevel must be > 0.
+ * Emergency messages should go to all consoles, so they cannot be
+ * filtered per-console.
+ */
+ if (level == 0)
+ return -ERANGE;
+
+ if (console_clamp_loglevel(level) != level)
+ return -ERANGE;
+
+ /*
+ * If the system has a minimum console loglevel set (via sysctl or
+ * kernel parameter), enforce it. This prevents setting per-console
+ * loglevels below the system minimum.
+ */
+ if (minimum_console_loglevel > CONSOLE_LOGLEVEL_MIN &&
+ level < minimum_console_loglevel)
+ return -ERANGE;
+
+out:
+ cookie = console_srcu_read_lock();
+ WRITE_ONCE(con->level, level);
+ console_srcu_read_unlock(cookie);
+
+ return size;
+}
+
+static DEVICE_ATTR_RW(loglevel);
+
+static struct attribute *console_sysfs_attrs[] = {
+ &dev_attr_loglevel.attr,
+ &dev_attr_effective_loglevel_source.attr,
+ &dev_attr_effective_loglevel.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(console_sysfs);
+
+static const struct class console_class = {
+ .name = "console",
+ .dev_groups = console_sysfs_groups,
+};
+static bool console_class_registered;
+
+static void console_classdev_release(struct device *dev)
+{
+ kfree(dev);
+}
+
+void console_register_device(struct console *con)
+{
+ /*
+ * We might be called from register_console() before the class is
+ * registered. If that happens, we'll take care of it in
+ * printk_late_init.
+ */
+ if (!console_class_registered)
+ return;
+
+ if (WARN_ON(con->classdev))
+ return;
+
+ con->classdev = kzalloc(sizeof(struct device), GFP_KERNEL);
+ if (!con->classdev)
+ return;
+
+ device_initialize(con->classdev);
+ dev_set_name(con->classdev, "%s%d", con->name, con->index);
+ dev_set_drvdata(con->classdev, con);
+ con->classdev->release = console_classdev_release;
+ con->classdev->class = &console_class;
+
+ /*
+ * This class device exists solely to expose attributes (like loglevel)
+ * and does not control physical power states. Power is managed by the
+ * underlying hardware device. Disable PM entirely to prevent the
+ * creation of confusing and unused power sysfs attributes.
+ */
+ device_set_pm_not_required(con->classdev);
+
+ if (device_add(con->classdev)) {
+ put_device(con->classdev);
+ con->classdev = NULL;
+ }
+}
+
+void console_setup_class(void)
+{
+ struct console *con;
+ int cookie;
+ int err;
+
+ /*
+ * printk exists for the lifetime of the kernel, it cannot be unloaded,
+ * so we should never end up back in here.
+ */
+ if (WARN_ON(console_class_registered))
+ return;
+
+ err = class_register(&console_class);
+ if (err)
+ return;
+
+ /*
+ * Take console_list_lock() before exposing the class globally.
+ * This ensures register_console() (which holds the lock) cannot
+ * see the class until it's fully initialized with dev_groups.
+ */
+ console_list_lock();
+ console_class_registered = true;
+ cookie = console_srcu_read_lock();
+ for_each_console_srcu(con)
+ console_register_device(con);
+ console_srcu_read_unlock(cookie);
+ console_list_unlock();
+}
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 08/13] printk: Constrain hardware-addressed console checks to name position
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (6 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-18 19:07 ` [PATCH v7 09/13] printk: Support setting initial console loglevel via console= on cmdline Chris Down
` (4 subsequent siblings)
12 siblings, 0 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
Commit 7640f1a44eba ("printk: Add
match_devname_and_update_preferred_console()") adds support for
DEVNAME:n:n style console= registration. It determines whether or not
this is a hardware-addressed console by looking at whether the console=
string has a colon in it. However, this interferes with loglevel:n and
potentially other future named options, which also make use of the
character.
In order to avoid this, only search positions before options.
Reviewed-by: Petr Mladek <pmladek@suse.com>
Reviewed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
Signed-off-by: Chris Down <chris@chrisdown.name>
---
kernel/printk/printk.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 62114aa61999..fb54101133d2 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2733,6 +2733,7 @@ static int __init console_setup(char *str)
char *devname = NULL;
char *options;
char *s;
+ char *first_delim;
int idx;
/*
@@ -2748,8 +2749,13 @@ static int __init console_setup(char *str)
if (_braille_console_setup(&str, &brl_options))
return 1;
- /* For a DEVNAME:0.0 style console the character device is unknown early */
- if (strchr(str, ':'))
+ /*
+ * For a DEVNAME:0.0 style console the character device is unknown
+ * early. We must restrict this to before any comma to avoid interfering
+ * with named options, like "loglevel".
+ */
+ first_delim = strpbrk(str, ":,");
+ if (first_delim && *first_delim == ':')
devname = buf;
else
ttyname = buf;
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 09/13] printk: Support setting initial console loglevel via console= on cmdline
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (7 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 08/13] printk: Constrain hardware-addressed console checks to name position Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-21 14:05 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 10/13] printk: Add sysctl interface to set global loglevels Chris Down
` (3 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
Extend the console= kernel command line parameter to support specifying
per-console loglevels at boot time. This is achieved by introducing a
new loglevel option that can be passed as a loglevel option within a
console= stanza.
For example, this is an example of how one might configure netconsole
devices to print messages with a higher priority than loglevel 3
(KERN_ERR) at startup:
console=netcon0,loglevel:3
Signed-off-by: Chris Down <chris@chrisdown.name>
---
.../admin-guide/kernel-parameters.txt | 24 ++--
Documentation/admin-guide/serial-console.rst | 37 +++++-
Documentation/networking/netconsole.rst | 6 +-
kernel/printk/console_cmdline.h | 1 +
kernel/printk/printk.c | 108 +++++++++++++++++-
5 files changed, 161 insertions(+), 15 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 7b62a99489b8..dec9e29ba8d2 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -819,13 +819,18 @@
ttyS<n>[,options]
ttyUSB0[,options]
Use the specified serial port. The options are of
- the form "bbbbpnf", where "bbbb" is the baud rate,
- "p" is parity ("n", "o", or "e"), "n" is number of
- bits, and "f" is flow control ("r" for RTS or
- omit it). Default is "9600n8".
+ the form "bbbbpnf,extra", where "bbbb" is the baud
+ rate, "p" is parity ("n", "o", or "e"), "n" is
+ number of bits, and "f" is flow control ("r" for RTS
+ or omit it). Default is "9600n8".
- See Documentation/admin-guide/serial-console.rst for more
- information. See
+ At present the only extra option is "loglevel" to
+ set the per-console loglevel. For example:
+
+ console=ttyS0,9600n8,loglevel:3
+
+ See Documentation/admin-guide/serial-console.rst for
+ more information. See
Documentation/networking/netconsole.rst for an
alternative.
@@ -3411,8 +3416,11 @@
loglevel= [KNL,EARLY]
All Kernel Messages with a loglevel smaller than the
console loglevel will be printed to the console. It can
- also be changed with klogd or other programs. The
- loglevels are defined as follows:
+ also be changed with klogd or other programs. Note that
+ this can be overridden per-console, see
+ Documentation/admin-guide/per-console-loglevel.rst.
+
+ The loglevels are defined as follows:
0 (KERN_EMERG) system is unusable
1 (KERN_ALERT) action must be taken immediately
diff --git a/Documentation/admin-guide/serial-console.rst b/Documentation/admin-guide/serial-console.rst
index 1609e7479249..b8163e6eb95a 100644
--- a/Documentation/admin-guide/serial-console.rst
+++ b/Documentation/admin-guide/serial-console.rst
@@ -32,6 +32,33 @@ The format of this option is::
and F is flow control ('r' for RTS). Default is
9600n8. The maximum baudrate is 115200.
+ One can also specify the per-console loglevel for this
+ console by providing a loglevel parameter, for example
+ "loglevel:4" to set this console's loglevel to 4. The
+ value provided can be from 1 (LOGLEVEL_ALERT) to 8
+ (LOGLEVEL_DEBUG + 1), and messages below that will be
+ emitted onto the console as they become available.
+
+The available loglevels are defined thus:
+
++---+--------------+-----------------------------------+
+| 0 | KERN_EMERG | system is unusable |
++---+--------------+-----------------------------------+
+| 1 | KERN_ALERT | action must be taken immediately |
++---+--------------+-----------------------------------+
+| 2 | KERN_CRIT | critical conditions |
++---+--------------+-----------------------------------+
+| 3 | KERN_ERR | error conditions |
++---+--------------+-----------------------------------+
+| 4 | KERN_WARNING | warning conditions |
++---+--------------+-----------------------------------+
+| 5 | KERN_NOTICE | normal but significant condition |
++---+--------------+-----------------------------------+
+| 6 | KERN_INFO | informational |
++---+--------------+-----------------------------------+
+| 7 | KERN_DEBUG | debug-level messages |
++---+--------------+-----------------------------------+
+
You can specify multiple console= options on the kernel command line.
The behavior is well defined when each device type is mentioned only once.
@@ -39,11 +66,14 @@ In this case, the output will appear on all requested consoles. And
the last device will be used when you open ``/dev/console``.
So, for example::
- console=ttyS1,9600 console=tty0
+ console=ttyS1,9600,loglevel:5 console=tty0
defines that opening ``/dev/console`` will get you the current foreground
-virtual console, and kernel messages will appear on both the VGA
-console and the 2nd serial port (ttyS1 or COM2) at 9600 baud.
+virtual console, and kernel messages will appear on both the VGA console and
+the 2nd serial port (ttyS1 or COM2) at 9600 baud. The optional loglevel "5"
+indicates that this console will emit messages more serious than
+LOGLEVEL_NOTICE (that is, LOGLEVEL_WARNING and below, since more serious
+messages have lower ordering).
The behavior is more complicated when the same device type is defined more
times. In this case, there are the following two rules:
@@ -145,3 +175,4 @@ Replace the sample values as needed.
the integration of these patches into m68k, ppc and alpha.
Miquel van Smoorenburg <miquels@cistron.nl>, 11-Jun-2000
+Chris Down <chris@chrisdown.name>, 18-Nov-2025
diff --git a/Documentation/networking/netconsole.rst b/Documentation/networking/netconsole.rst
index 5fabcf9367d1..5a7405986934 100644
--- a/Documentation/networking/netconsole.rst
+++ b/Documentation/networking/netconsole.rst
@@ -78,7 +78,11 @@ Built-in netconsole starts immediately after the TCP stack is
initialized and attempts to bring up the supplied dev at the supplied
address.
-You can also set a loglevel at runtime::
+You can also set a loglevel at boot time on the kernel command line::
+
+ console=netcon0,loglevel:2
+
+This can also be changed at runtime::
$ ls -l /sys/class/console/netcon0/
total 0
diff --git a/kernel/printk/console_cmdline.h b/kernel/printk/console_cmdline.h
index 0ab573b6d4dc..cb3b99d31d80 100644
--- a/kernel/printk/console_cmdline.h
+++ b/kernel/printk/console_cmdline.h
@@ -7,6 +7,7 @@ struct console_cmdline
char name[16]; /* Name of the driver */
int index; /* Minor dev. to use */
char devname[32]; /* DEVNAME:0.0 style device name */
+ int level; /* Log level to use */
bool user_specified; /* Specified by command line vs. platform */
char *options; /* Options for the driver */
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index fb54101133d2..a6e1fa924294 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -2660,12 +2660,108 @@ static void set_user_specified(struct console_cmdline *c, bool user_specified)
console_set_on_cmdline = 1;
}
+/**
+ * find_and_remove_console_option - Find and remove a named option from console options string
+ * @options: The console options string (will be modified in-place)
+ * @key: The option name to find (e.g., "loglevel")
+ * @val_buf: Buffer to store the option value (if present)
+ * @val_buf_size: Size of @val_buf
+ *
+ * This function searches for a named option in a comma-separated options string
+ * (e.g., "9600n8,loglevel:3,other:value"). If found, it extracts the value
+ * (the part after ':') and removes the entire option from the string.
+ *
+ * The function modifies @options in-place by:
+ * 1. Temporarily null-terminating option names and values during parsing
+ * 2. Restoring separators if the option isn't found
+ * 3. Removing the found option by shifting the remaining string
+ *
+ * Return: true if the option was found and removed, false otherwise
+ */
+static bool find_and_remove_console_option(char *options, const char *key,
+ char *val_buf, size_t val_buf_size)
+{
+ bool found = false, first = true;
+ char *option, *next = options;
+
+ while ((option = strsep(&next, ","))) {
+ char *value;
+
+ value = strchr(option, ':');
+ if (value)
+ *(value++) = '\0';
+
+ if (strcmp(option, key) == 0) {
+ found = true;
+ if (value) {
+ if (strlen(value) >= val_buf_size) {
+ pr_warn("Can't copy console option value for %s:%s: not enough space (%zu)\n",
+ option, value, val_buf_size);
+ found = false;
+ } else {
+ strscpy(val_buf, value, val_buf_size);
+ }
+ } else
+ *val_buf = '\0';
+ }
+
+ if (found)
+ break;
+
+ if (next)
+ *(next - 1) = ',';
+ if (value)
+ *(value - 1) = ':';
+
+ first = false;
+ }
+
+ if (found) {
+ if (next)
+ memmove(option, next, strlen(next) + 1);
+ else if (first)
+ *option = '\0';
+ else
+ *--option = '\0';
+ }
+
+ return found;
+}
+
+static int find_and_remove_loglevel_option(char *options)
+{
+ char val[16];
+ int loglevel;
+
+ if (!find_and_remove_console_option(options, "loglevel", val,
+ sizeof(val)))
+ return -ENOENT;
+
+ if (kstrtoint(val, 10, &loglevel)) {
+ pr_warn("Invalid console loglevel, ignoring: %s\n", val);
+ return -EINVAL;
+ }
+
+ /* Reject level 0 (KERN_EMERG) - per-console loglevel must be > 0 */
+ if (loglevel == 0) {
+ pr_warn("Per-console loglevel 0 (KERN_EMERG) is not allowed, ignoring\n");
+ return -ERANGE;
+ }
+
+ if (console_clamp_loglevel(loglevel) != loglevel) {
+ pr_warn("Per-console loglevel out of range, ignoring: %d\n", loglevel);
+ return -ERANGE;
+ }
+
+ return loglevel;
+}
+
static int __add_preferred_console(const char *name, const short idx,
const char *devname, char *options,
char *brl_options, bool user_specified)
{
struct console_cmdline *c;
- int i;
+ int i, ret;
if (!name && !devname)
return -EINVAL;
@@ -2702,6 +2798,13 @@ static int __add_preferred_console(const char *name, const short idx,
strscpy(c->name, name);
if (devname)
strscpy(c->devname, devname);
+
+ ret = find_and_remove_loglevel_option(options);
+ if (ret >= 0)
+ c->level = ret;
+ else
+ c->level = -1;
+
c->options = options;
set_user_specified(c, user_specified);
braille_set_options(c, brl_options);
@@ -3979,8 +4082,7 @@ static int try_enable_preferred_console(struct console *newcon,
if (newcon->index < 0)
newcon->index = c->index;
- /* TODO: will be configurable in a later patch */
- newcon->level = LOGLEVEL_DEFAULT;
+ newcon->level = c->level;
if (_braille_register_console(newcon, c))
return 0;
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 10/13] printk: Add sysctl interface to set global loglevels
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (8 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 09/13] printk: Support setting initial console loglevel via console= on cmdline Chris Down
@ 2025-11-18 19:07 ` Chris Down
2025-11-21 14:23 ` Petr Mladek
2025-11-18 19:08 ` [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels Chris Down
` (2 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:07 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
Introduce two new sysctl interfaces for configuring global loglevels:
- kernel.console_loglevel: Sets the global console loglevel, determining
the minimum priority of messages printed to consoles. Messages with a
loglevel lower than this value will be printed.
- kernel.default_message_loglevel: Sets the default loglevel for
messages that do not specify an explicit loglevel.
The kernel.printk sysctl was previously used to set multiple loglevel
parameters simultaneously, but it was confusing and lacked proper
validation. By introducing these dedicated sysctl interfaces, we provide
a clearer and more granular way to configure the loglevels.
Signed-off-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Chris Down <chris@chrisdown.name>
---
Documentation/admin-guide/sysctl/kernel.rst | 17 ++++++-
include/linux/sysctl.h | 7 +++
kernel/printk/sysctl.c | 52 +++++++++++++++++++++
kernel/sysctl.c | 18 +++----
4 files changed, 84 insertions(+), 10 deletions(-)
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index f3ee807b5d8b..043d5f663b7d 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -1079,6 +1079,20 @@ otherwise the 'doze' mode will be used.
==============================================================
+Some of these settings may be overridden per-console, see
+Documentation/admin-guide/per-console-loglevel.rst. See ``man 2 syslog`` for
+more information on the different loglevels.
+
+console_loglevel
+================
+
+Messages with a higher priority than this will be printed to consoles.
+
+default_message_loglevel
+========================
+
+Messages without an explicit priority will be printed with this priority.
+
printk
======
@@ -1087,8 +1101,7 @@ The four values in printk denote: ``console_loglevel``,
``default_console_loglevel`` respectively.
These values influence printk() behavior when printing or
-logging error messages. See '``man 2 syslog``' for more info on
-the different loglevels.
+logging error messages.
======================== =====================================
console_loglevel messages with a higher priority than
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index 92e9146b1104..c2f72d172f64 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -235,6 +235,13 @@ extern struct ctl_table_header *register_sysctl_mount_point(const char *path);
void do_sysctl_args(void);
bool sysctl_is_alias(char *param);
+int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp, int *valp,
+ int write, void *data);
+int do_proc_dointvec(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos,
+ int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
+ int write, void *data),
+ void *data);
int do_proc_douintvec(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp,
diff --git a/kernel/printk/sysctl.c b/kernel/printk/sysctl.c
index da77f3f5c1fe..feec6cf939af 100644
--- a/kernel/printk/sysctl.c
+++ b/kernel/printk/sysctl.c
@@ -11,6 +11,9 @@
static const int ten_thousand = 10000;
+static int min_msg_loglevel = LOGLEVEL_EMERG;
+static int max_msg_loglevel = LOGLEVEL_DEBUG;
+
static int proc_dointvec_minmax_sysadmin(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
@@ -20,6 +23,39 @@ static int proc_dointvec_minmax_sysadmin(const struct ctl_table *table, int writ
return proc_dointvec_minmax(table, write, buffer, lenp, ppos);
}
+static int do_proc_dointvec_console_loglevel(bool *negp, unsigned long *lvalp,
+ int *valp,
+ int write, void *data)
+{
+ int level, ret;
+
+ /*
+ * If writing, first do so via a temporary local int so we can
+ * bounds-check it before touching *valp.
+ */
+ int *intp = write ? &level : valp;
+
+ ret = do_proc_dointvec_conv(negp, lvalp, intp, write, data);
+ if (ret)
+ return ret;
+
+ if (write) {
+ if (level != console_clamp_loglevel(level))
+ return -ERANGE;
+ WRITE_ONCE(*valp, level);
+ }
+
+ return 0;
+}
+
+static int proc_dointvec_console_loglevel(const struct ctl_table *table,
+ int write, void *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ return do_proc_dointvec(table, write, buffer, lenp, ppos,
+ do_proc_dointvec_console_loglevel, NULL);
+}
+
static const struct ctl_table printk_sysctls[] = {
{
.procname = "printk",
@@ -76,6 +112,22 @@ static const struct ctl_table printk_sysctls[] = {
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_TWO,
},
+ {
+ .procname = "console_loglevel",
+ .data = &console_loglevel,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_console_loglevel,
+ },
+ {
+ .procname = "default_message_loglevel",
+ .data = &default_message_loglevel,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &min_msg_loglevel,
+ .extra2 = &max_msg_loglevel,
+ },
};
void __init printk_sysctl_init(void)
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index cb6196e3fa99..3ed010b8f6b3 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -354,9 +354,9 @@ static void proc_put_char(void **buf, size_t *size, char c)
}
}
-static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
- int *valp,
- int write, void *data)
+int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
+ int *valp,
+ int write, void *data)
{
if (write) {
if (*negp) {
@@ -380,6 +380,7 @@ static int do_proc_dointvec_conv(bool *negp, unsigned long *lvalp,
}
return 0;
}
+EXPORT_SYMBOL(do_proc_dointvec_conv);
static int do_proc_douintvec_conv(unsigned long *lvalp,
unsigned int *valp,
@@ -471,15 +472,16 @@ static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table,
return err;
}
-static int do_proc_dointvec(const struct ctl_table *table, int write,
- void *buffer, size_t *lenp, loff_t *ppos,
- int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
- int write, void *data),
- void *data)
+int do_proc_dointvec(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos,
+ int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
+ int write, void *data),
+ void *data)
{
return __do_proc_dointvec(table->data, table, write,
buffer, lenp, ppos, conv, data);
}
+EXPORT_SYMBOL(do_proc_dointvec);
static int do_proc_douintvec_w(unsigned int *tbl_data,
const struct ctl_table *table,
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (9 preceding siblings ...)
2025-11-18 19:07 ` [PATCH v7 10/13] printk: Add sysctl interface to set global loglevels Chris Down
@ 2025-11-18 19:08 ` Chris Down
2025-11-21 15:52 ` Petr Mladek
2025-11-23 21:21 ` John Ogness
2025-11-18 19:08 ` [PATCH v7 12/13] printk: Deprecate the kernel.printk sysctl interface Chris Down
2025-11-18 19:08 ` [PATCH v7 13/13] printk: Purge default_console_loglevel Chris Down
12 siblings, 2 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:08 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
The per-console loglevel feature documentation could use some practical
guidance. This commit adds:
- Examples section covering runtime configuration, effective loglevel
checking, and boot-time configuration
- Common use case demonstrating high-performance netconsole with quiet
serial console fallback
- Performance impact section explaining how per-console loglevels reduce
latency by filtering messages before slow console writes
- Troubleshooting section addressing common issues like messages not
appearing, loglevel constraints, and minimum console loglevel
- Edge cases section documenting behavior with concurrent writes,
console unregistration, and global loglevel changes
The guidance interleaves advice about many parts of this patchset, so
let's have it in a distinct commit.
This documentation will help users understand how to effectively use and
debug per-console loglevels.
Signed-off-by: Chris Down <chris@chrisdown.name>
---
.../admin-guide/per-console-loglevel.rst | 152 ++++++++++++++++++
1 file changed, 152 insertions(+)
diff --git a/Documentation/admin-guide/per-console-loglevel.rst b/Documentation/admin-guide/per-console-loglevel.rst
index 4908d5d8ed4f..69eede12e20f 100644
--- a/Documentation/admin-guide/per-console-loglevel.rst
+++ b/Documentation/admin-guide/per-console-loglevel.rst
@@ -97,6 +97,158 @@ are using ``ttyS0``, the console backing it can be viewed at
This will be in effect if no other global control overrides it. Look at
``effective_loglevel`` and ``effective_loglevel_source`` to verify that.
+Examples
+--------
+
+Setting per-console loglevel at runtime
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Set serial console to only show warnings and above (level 4)::
+
+ echo 4 > /sys/class/console/ttyS0/loglevel
+
+Set netconsole to show info and above (level 6)::
+
+ echo 6 > /sys/class/console/netcon0/loglevel
+
+Reset a console to use the global loglevel::
+
+ echo -1 > /sys/class/console/ttyS0/loglevel
+
+Checking effective loglevel
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Check what loglevel is actually in effect for a console::
+
+ $ cat /sys/class/console/ttyS0/effective_loglevel
+ 4
+ $ cat /sys/class/console/ttyS0/effective_loglevel_source
+ local
+
+If the source shows ``global``, the console is using the global loglevel.
+If it shows ``local``, the console is using its per-console loglevel.
+If it shows ``ignore_loglevel``, all loglevel controls are being ignored.
+
+Boot-time configuration
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Set different loglevels for different consoles at boot::
+
+ console=ttyS0,115200n8,loglevel:3 console=tty0,loglevel:5
+
+This sets the serial console (ttyS0) to level 3 (KERN_ERR) and the VGA
+console (tty0) to level 5 (KERN_NOTICE).
+
+For netconsole::
+
+ netconsole=@/,@192.168.1.1/ console=netcon0,loglevel:6
+
+Common use case - high performance with serial fallback
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A common configuration is to set netconsole to a verbose level for normal
+debugging, while keeping the serial console quiet to avoid performance impact,
+but still available for emergencies::
+
+ # Netconsole gets INFO and above (verbose)
+ echo 6 > /sys/class/console/netcon0/loglevel
+
+ # Serial console gets only WARN and above (quiet, for emergencies)
+ echo 4 > /sys/class/console/ttyS0/loglevel
+
+This allows you to see informational messages on the fast netconsole without
+the latency impact of writing them to the slow serial port.
+
+Performance Impact
+------------------
+
+When a console has a higher (less verbose) loglevel than the global level,
+messages that would normally be sent to that console are filtered out before
+the console write callback is invoked. This eliminates the latency that would
+be incurred by writing those messages to slow consoles (e.g., serial ports).
+
+For example, setting a serial console to WARN level (4) while keeping
+netconsole at INFO level (6) prevents INFO and NOTICE messages from being
+written to the slow serial port, reducing application stalls during verbose
+logging periods.
+
+Serial console writes can take tens of milliseconds per message. During
+periods of heavy logging (e.g., during network debugging or block I/O tracing),
+this can cause significant application-level stalls. By setting a higher
+per-console loglevel for the serial console, you can avoid these stalls while
+still capturing all messages on faster consoles like netconsole.
+
+Troubleshooting
+---------------
+
+Messages not appearing on console despite setting loglevel
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Check effective loglevel source::
+
+ cat /sys/class/console/<name>/effective_loglevel_source
+
+ If it shows ``ignore_loglevel``, you have the ``printk.ignore_loglevel``
+ kernel parameter set, which overrides all level controls. Remove it from
+ your kernel command line or set it to N in sysfs::
+
+ echo N > /sys/module/printk/parameters/ignore_loglevel
+
+2. Check if per-console loglevels are being ignored::
+
+ cat /sys/module/printk/parameters/ignore_per_console_loglevel
+
+ If it shows ``Y``, per-console settings are disabled. Set it to N::
+
+ echo N > /sys/module/printk/parameters/ignore_per_console_loglevel
+
+3. Verify the message priority is high enough::
+
+ cat /sys/class/console/<name>/effective_loglevel
+
+ Messages must have priority less than this value to appear. For example,
+ if effective_loglevel is 4, only messages with priority 0-3 (EMERG, ALERT,
+ CRIT, ERR) will be printed. If you want to see WARN messages (priority 4),
+ you need to increase the effective_loglevel to at least 5.
+
+Cannot set loglevel to 0
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Per-console loglevels cannot be set to 0 (KERN_EMERG). This is by design, as
+level 0 is reserved for the most critical system messages that should always
+go to all consoles. To use the global loglevel, set the per-console loglevel
+to -1::
+
+ echo -1 > /sys/class/console/<name>/loglevel
+
+Setting below minimum_console_loglevel fails
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you get an error when trying to set a loglevel, check the system-wide
+minimum::
+
+ cat /proc/sys/kernel/console_loglevel
+
+Per-console loglevels cannot be set below this minimum. This is a safety
+feature to ensure critical messages are always visible.
+
+Edge cases
+~~~~~~~~~~
+
+**Setting all consoles to high loglevels**: If you set all consoles to
+very high loglevels (e.g., 1 or 2), most messages won't appear anywhere.
+This is valid but probably not what you want. Keep at least one console
+at a reasonable level for monitoring.
+
+**Console unregistration while sysfs file is open**: If a console is
+unregistered (e.g., module unloaded) while you have its sysfs files open,
+the files will become stale. Close and reopen them, or they will eventually
+return errors.
+
+**Global loglevel changes**: If you change the global console_loglevel
+via sysctl, consoles set to -1 (use global) will immediately reflect the
+new level. Consoles with explicit per-console levels are unaffected.
+
Deprecated
~~~~~~~~~~
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 12/13] printk: Deprecate the kernel.printk sysctl interface
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (10 preceding siblings ...)
2025-11-18 19:08 ` [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels Chris Down
@ 2025-11-18 19:08 ` Chris Down
2025-11-18 19:08 ` [PATCH v7 13/13] printk: Purge default_console_loglevel Chris Down
12 siblings, 0 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:08 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
The kernel.printk sysctl interface is deprecated in favour of more
granular and clearer sysctl interfaces for controlling loglevels, namely
kernel.console_loglevel and kernel.default_message_loglevel.
kernel.printk has a number of fairly significant usability problems:
- It takes four values in a specific order, which is not intuitive.
Speaking from personal experience, even people working on printk
sometimes need to look up the order of these values, which doesn't
suggest much in the way of perspicuity.
- There is no validation on the input values, so users can set them to
invalid or nonsensical values without receiving any errors or
warnings. This can lead to unpredictable behaviour.
- One of the four values, default_console_loglevel, is not used in the
kernel (see below), making it redundant and potentially confusing.
- Overall, the interface is complex, hard to understand, and combines
multiple controls into a single sysctl, which is not ideal. It should
be separated into distinct controls for clarity.
Setting kernel.printk now calls printk_sysctl_deprecated, which emits a
pr_warn. The warning informs users about the deprecation and suggests
using the new sysctl interfaces instead.
By deprecating kernel.printk, we aim to:
- Encourage users to adopt the new, dedicated sysctl interfaces
(kernel.console_loglevel and kernel.default_message_loglevel), which
are more straightforward and easier to understand.
- Improve input validation and error handling, ensuring that users
receive appropriate feedback when setting invalid values.
- Simplify the configuration of loglevels by exposing only the controls
that are necessary and relevant.
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Chris Down <chris@chrisdown.name>
---
Documentation/admin-guide/sysctl/kernel.rst | 3 +++
kernel/printk/sysctl.c | 14 +++++++++++++-
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index 043d5f663b7d..5ac6db03c2fb 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -1096,6 +1096,9 @@ Messages without an explicit priority will be printed with this priority.
printk
======
+This sysctl is deprecated and will be removed in future. Please consider using
+``kernel.console_loglevel`` or ``kernel.default_message_loglevel`` instead.
+
The four values in printk denote: ``console_loglevel``,
``default_message_loglevel``, ``minimum_console_loglevel`` and
``default_console_loglevel`` respectively.
diff --git a/kernel/printk/sysctl.c b/kernel/printk/sysctl.c
index feec6cf939af..921e0f80ee8f 100644
--- a/kernel/printk/sysctl.c
+++ b/kernel/printk/sysctl.c
@@ -7,6 +7,7 @@
#include <linux/printk.h>
#include <linux/capability.h>
#include <linux/ratelimit.h>
+#include <linux/console.h>
#include "internal.h"
static const int ten_thousand = 10000;
@@ -56,13 +57,24 @@ static int proc_dointvec_console_loglevel(const struct ctl_table *table,
do_proc_dointvec_console_loglevel, NULL);
}
+static int proc_dointvec_printk_deprecated(const struct ctl_table *table, int write,
+ void *buffer, size_t *lenp, loff_t *ppos)
+{
+ int res = proc_dointvec(table, write, buffer, lenp, ppos);
+
+ if (write)
+ pr_warn_once("printk: The kernel.printk sysctl is deprecated. Consider using kernel.console_loglevel or kernel.default_message_loglevel instead.\n");
+
+ return res;
+}
+
static const struct ctl_table printk_sysctls[] = {
{
.procname = "printk",
.data = &console_loglevel,
.maxlen = 4*sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec,
+ .proc_handler = proc_dointvec_printk_deprecated,
},
{
.procname = "printk_ratelimit",
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH v7 13/13] printk: Purge default_console_loglevel
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
` (11 preceding siblings ...)
2025-11-18 19:08 ` [PATCH v7 12/13] printk: Deprecate the kernel.printk sysctl interface Chris Down
@ 2025-11-18 19:08 ` Chris Down
12 siblings, 0 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:08 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
default_console_loglevel (then DEFAULT_LOGLEVEL) was created in Linux
0.99.13 (September 1993) to power what we now call
SYSLOG_ACTION_CONSOLE_{ON,OFF}. It was originally hardcoded to 7.
In Linux 2.3.12 (March 1997), Chris Horn made it configurable by sysctl
through kernel.printk.
Its demise came about in July 2009 in commit 1aaad49e856c ("printk:
Restore previous console_loglevel when re-enabling logging"), which
replaces default_console_loglevel with the previously used
console_loglevel. However, the documentation was never updated, and we
still kept on telling users that this sets the default value for
console_loglevel, even though this hasn't been true for over 15 years.
Purge both the documentation and all remaining references to
default_console_loglevel. It will still be initialised in the sysctl
array.
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Chris Down <chris@chrisdown.name>
---
Documentation/admin-guide/sysctl/kernel.rst | 5 ++---
include/linux/printk.h | 1 -
kernel/printk/printk.c | 2 +-
3 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index 5ac6db03c2fb..37f2f417eaf7 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -1100,8 +1100,8 @@ This sysctl is deprecated and will be removed in future. Please consider using
``kernel.console_loglevel`` or ``kernel.default_message_loglevel`` instead.
The four values in printk denote: ``console_loglevel``,
-``default_message_loglevel``, ``minimum_console_loglevel`` and
-``default_console_loglevel`` respectively.
+``default_message_loglevel``, ``minimum_console_loglevel`` and an unused legacy
+value respectively.
These values influence printk() behavior when printing or
logging error messages.
@@ -1113,7 +1113,6 @@ default_message_loglevel messages without an explicit priority
will be printed with this priority
minimum_console_loglevel minimum (highest) value to which
console_loglevel can be set
-default_console_loglevel default value for console_loglevel
======================== =====================================
diff --git a/include/linux/printk.h b/include/linux/printk.h
index e36cae1805b2..432bef48e212 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -71,7 +71,6 @@ extern int console_printk[];
#define console_loglevel (console_printk[0])
#define default_message_loglevel (console_printk[1])
#define minimum_console_loglevel (console_printk[2])
-#define default_console_loglevel (console_printk[3])
extern bool ignore_per_console_loglevel;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index a6e1fa924294..170fa8e14ea9 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -66,7 +66,7 @@ int console_printk[4] = {
CONSOLE_LOGLEVEL_DEFAULT, /* console_loglevel */
MESSAGE_LOGLEVEL_DEFAULT, /* default_message_loglevel */
CONSOLE_LOGLEVEL_MIN, /* minimum_console_loglevel */
- CONSOLE_LOGLEVEL_DEFAULT, /* default_console_loglevel */
+ /* legacy default console loglevel, unused */
};
EXPORT_SYMBOL_GPL(console_printk);
--
2.51.2
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console
2025-11-18 19:06 ` [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console Chris Down
@ 2025-11-18 19:33 ` Chris Down
2025-11-19 15:46 ` Petr Mladek
0 siblings, 1 reply; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:33 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
Of course, one reviews the whole patch set and then misses...
Chris Down writes:
>- boot_delay_msec(level);
>+ /* If the message is forced (e.g. panic), we must delay */
That should obviously say "_not_ delay". I'll fix it on next resend.
>+ if (!is_printk_force_console() && suppress_message_printing(level))
>+ return;
>+
>+ boot_delay_msec();
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure
2025-11-18 19:07 ` [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure Chris Down
@ 2025-11-18 19:34 ` Chris Down
2025-11-19 16:49 ` Petr Mladek
1 sibling, 0 replies; 27+ messages in thread
From: Chris Down @ 2025-11-18 19:34 UTC (permalink / raw)
To: Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
Chris Down writes:
>-
>- /*
>- * The READ_ONCE() matches the WRITE_ONCE() when @flags are modified
>- * for registered consoles with console_srcu_write_flags().
>- */
> return data_race(READ_ONCE(con->flags));
> }
Not sure what happened here, but that was unintentional :-) That should be left
intact. I'll fix it for next resend.
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console
2025-11-18 19:33 ` Chris Down
@ 2025-11-19 15:46 ` Petr Mladek
0 siblings, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-19 15:46 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:33:20, Chris Down wrote:
> Of course, one reviews the whole patch set and then misses...
I think that this is a classic post-sent panic ;-)
> Chris Down writes:
> > - boot_delay_msec(level);
> > + /* If the message is forced (e.g. panic), we must delay */
>
> That should obviously say "_not_ delay". I'll fix it on next resend.
IMHO, the original message is correct. The message is forced => shown
=> the delay allows to read the message on a screen.
> > + if (!is_printk_force_console() && suppress_message_printing(level))
> > + return;
> > +
> > + boot_delay_msec();
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure
2025-11-18 19:07 ` [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure Chris Down
2025-11-18 19:34 ` Chris Down
@ 2025-11-19 16:49 ` Petr Mladek
1 sibling, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-19 16:49 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:06, Chris Down wrote:
> This commit adds the internal infrastructure to support per-console
> log levels, which will be configurable through sysfs and the kernel
> command line in future commits.
>
> --- a/include/linux/console.h
> +++ b/include/linux/console.h
> @@ -518,11 +521,6 @@ extern struct hlist_head console_list;
> static inline short console_srcu_read_flags(const struct console *con)
> {
> WARN_ON_ONCE(!console_srcu_read_lock_is_held());
> -
> - /*
> - * The READ_ONCE() matches the WRITE_ONCE() when @flags are modified
> - * for registered consoles with console_srcu_write_flags().
> - */
> return data_race(READ_ONCE(con->flags));
> }
As you have already pointed out, the above change looks unintentional.
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -3835,8 +3967,12 @@ static int try_enable_preferred_console(struct console *newcon,
> * without matching. Accept the pre-enabled consoles only when match()
> * and setup() had a chance to be called.
> */
> - if (newcon->flags & CON_ENABLED && c->user_specified == user_specified)
> + if (newcon->flags & CON_ENABLED && c->user_specified == user_specified) {
> + /* Ensure level is initialized for pre-enabled consoles */
> + if (newcon->level == 0)
> + newcon->level = LOGLEVEL_DEFAULT;
This change does not harm. But it seems to be redundant after all.
The same check/set is done also in register_console(), see below.
> return 0;
> + }
>
> return -ENOENT;
> }
> @@ -4039,6 +4175,14 @@ void register_console(struct console *newcon)
> }
>
> newcon->dropped = 0;
> +
> + /*
> + * Don't unconditionally overwrite, it may have been set on the command
> + * line already.
> + */
> + if (newcon->level == 0)
> + newcon->level = LOGLEVEL_DEFAULT;
> +
> init_seq = get_init_console_seq(newcon, bootcon_registered);
>
> if (newcon->flags & CON_NBCON) {
Otherwise, it looks good to me. With two hunks removed, feel free to
use:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 04/13] printk: Ignore per-console loglevel in sysrq
2025-11-18 19:07 ` [PATCH v7 04/13] printk: Ignore per-console loglevel in sysrq Chris Down
@ 2025-11-19 16:51 ` Petr Mladek
0 siblings, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-19 16:51 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:11, Chris Down wrote:
> When the global console loglevel is changed via sysrq, clear any
> per-console loglevels that may have been set. This ensures that the
> sysrq loglevel change takes effect immediately across all consoles.
>
> Warn once if any per-console loglevels were being overridden.
>
> Signed-off-by: Chris Down <chris@chrisdown.name>
Looks good to me:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 05/13] printk: Add synchronisation for concurrent console state changes
2025-11-18 19:07 ` [PATCH v7 05/13] printk: Add synchronisation for concurrent console state changes Chris Down
@ 2025-11-19 16:58 ` Petr Mladek
0 siblings, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-19 16:58 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:17, Chris Down wrote:
> The syslog actions SYSLOG_ACTION_CONSOLE_OFF and
> SYSLOG_ACTION_CONSOLE_ON currently run without any locking. This creates
> a race condition if two processes attempt to toggle the console state
> simultaneously.
>
> While this race has existed for donkey's years, it was previously
> somewhat tolerable because it involved only a single integer variable
> (saved_console_loglevel). However, upcoming changes will introduce a
> second piece of state (saved_ignore_per_console_loglevel) to be saved
> and restored. With two variables, this can result in saved state getting
> lost.
>
> Here is a demonstration:
>
> CPU 0 (SYSLOG_ACTION_CONSOLE_OFF) CPU 1 (SYSLOG_ACTION_CONSOLE_ON)
> --------------------------------- --------------------------------
> // saved_console_loglevel is DEFAULT
> if (saved == DEFAULT) (True)
> saved = console_loglevel (7)
> // Race triggers here
> if (saved != DEFAULT) (True)
> loglevel = saved (7)
> saved = DEFAULT
> // CPU 0 continues, unaware saved
> // was just reset by CPU 1
> loglevel = minimum_console_loglevel (1)
>
> The result is that the console is now set to the minimum loglevel, but
> saved_console_loglevel is LOGLEVEL_DEFAULT. A subsequent CONSOLE_ON call
> will see saved == LOGLEVEL_DEFAULT and thus refuse to restore the
> original loglevel. The console is effectively stuck in quiet mode until
> manually reset via sysctl. Oh dear.
>
> The callers of do_syslog are syscalls (so user context) or procfs write
> handlers. These contexts are allowed to sleep, so acquiring a mutex is
> safe. We also already make use of syslog_lock in
> SYSLOG_ACTION_SIZE_UNREAD and SYSLOG_ACTION_READ, so this is consistent.
>
> Signed-off-by: Chris Down <chris@chrisdown.name>
Looks good to me:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 06/13] printk: Support toggling per-console loglevel via syslog() and cmdline
2025-11-18 19:07 ` [PATCH v7 06/13] printk: Support toggling per-console loglevel via syslog() and cmdline Chris Down
@ 2025-11-20 10:05 ` Petr Mladek
0 siblings, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-20 10:05 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:22, Chris Down wrote:
> A new module parameter (ignore_per_console_loglevel) is added, which can
> be set via the kernel command line or at runtime through
> /sys/module/printk/parameters/ignore_per_console_loglevel. When set, the
> per-console loglevels are ignored, and the global console loglevel
> (console_loglevel) is used for all consoles.
>
> During sysrq, we temporarily disable per-console loglevels to ensure all
> requisite messages are printed to the console. This is necessary because
> sysrq is often used in dire circumstances where access to
> /sys/class/console may not be trivially possible.
>
> Additionally, the syslog actions SYSLOG_ACTION_CONSOLE_ON and
> SYSLOG_ACTION_CONSOLE_OFF are augmented to save and restore the state of
> ignore_per_console_loglevel. This allows administrators to enable or
> disable per-console loglevels dynamically using the syslog() system
> call, as supported in userspace by things like dmesg.
>
> This is useful when debugging issues with message emission, or when
> needing to quickly break glass and revert to global loglevel only.
>
> Signed-off-by: Chris Down <chris@chrisdown.name>
Looks good to me:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels
2025-11-18 19:07 ` [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels Chris Down
@ 2025-11-20 16:50 ` Petr Mladek
2025-11-21 11:38 ` Petr Mladek
1 sibling, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-20 16:50 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:27, Chris Down wrote:
> A sysfs interface under /sys/class/console/ is created that permits
> viewing and configuring per-console attributes. This is the main
> interface with which we expect users to interact with and configure
> per-console loglevels.
>
> --- a/Documentation/admin-guide/per-console-loglevel.rst
> +++ b/Documentation/admin-guide/per-console-loglevel.rst
> @@ -69,3 +69,41 @@ The default value for ``kernel.console_loglevel`` comes from
> ``CONFIG_CONSOLE_LOGLEVEL_DEFAULT``, or ``CONFIG_CONSOLE_LOGLEVEL_QUIET`` if
> ``quiet`` is passed on the kernel command line.
>
> +Console attributes
> +~~~~~~~~~~~~~~~~~~
> +
> +Registered consoles are exposed at ``/sys/class/console``. For example, if you
> +are using ``ttyS0``, the console backing it can be viewed at
> +``/sys/class/console/ttyS0/``. The following files are available:
> +
> +* ``effective_loglevel`` (r): The effective loglevel after considering all
> + loglevel authorities. For example, it shows the value of the console-specific
> + loglevel when a console-specific loglevel is defined, and shows the global
> + console loglevel value when the console-specific one is not defined.
> +
> +* ``effective_loglevel_source`` (r): The loglevel authority which resulted in
> + the effective loglevel being set. The following values can be present:
> +
> + * ``local``: The console-specific loglevel is in effect.
> +
> + * ``global``: The global loglevel (``kernel.console_loglevel``) is in
> + effect. Set a console-specific loglevel to override it.
> +
> + * ``ignore_loglevel``: ``ignore_loglevel`` was specified on the kernel
> + command line or at ``/sys/module/printk/parameters/ignore_loglevel``.
> + Disable it to use level controls.
> +
> +* ``loglevel`` (rw): The local, console-specific loglevel for this console.
> + This will be in effect if no other global control overrides it. Look at
> + ``effective_loglevel`` and ``effective_loglevel_source`` to verify that.
> +
> +Deprecated
> +~~~~~~~~~~
> +
> +* ``kernel.printk`` sysctl: this takes four values, setting
> + ``kernel.console_loglevel``, ``kernel.default_message_loglevel``, the minimum
> + console loglevel, and a fourth unused value. The interface is generally
> + considered to quite confusing, doesn't perform checks on the values given,
The seems to be a typo. I would use either of
+ "considered as quite confusing"
+ "considered to be quite confusing"
> + and is unaware of per-console loglevel semantics.
> +
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -4010,6 +4010,9 @@ static void try_enable_default_console(struct console *newcon)
> if (newcon->index < 0)
> newcon->index = 0;
>
> + newcon->level = LOGLEVEL_DEFAULT;
This does not fit in this patch. A better place would be the 3rd patch
which added .level into struct console.
But I am not sure if we need it at all. The LOGLEVEL_DEFAULT will
be set by register_console() as a fallback anyway. It is the same
reason why I suggested to remove a similar code from
try_enable_preferred_console().
> + newcon->classdev = NULL;
This should not be needed. struct console is defined statically
and this field should always be NULL before the console
is registered.
> +
> if (console_call_setup(newcon, NULL) != 0)
> return;
>
> diff --git a/kernel/printk/sysfs.c b/kernel/printk/sysfs.c
> new file mode 100644
> index 000000000000..5252e6e04908
> --- /dev/null
> +++ b/kernel/printk/sysfs.c
> @@ -0,0 +1,213 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/kernel.h>
> +#include <linux/console.h>
> +#include <linux/device.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include "internal.h"
> +
> +static const char *
> +console_effective_loglevel_source_str(const struct console *con)
> +{
> + enum loglevel_source source;
> + const char *str;
> + int con_level;
> + int cookie;
> +
> + cookie = console_srcu_read_lock();
> + con_level = console_srcu_read_loglevel(con);
> + console_srcu_read_unlock(cookie);
I think that this was discussed in v6. But I guess that we did not
reach a consensus.
The SRCU read lock is not need here. In fact, it is wrong because
unregister_console_locked() calls device_unregister() after
synchronize_srcu(&console_srcu).
The sysfs API makes sure that struct console could not get removed
when the interface is accessed. I suggest to create a custom
wrappers for the sysfs interface. Something like:
/**
* console_srcu_read_loglevel - Locklessly read the console specific loglevel
* when accessing the related sysfs interface
* @con: struct console pointer of console to read loglevel from
*
* Locklessly reading @con->level provides a consistent read value because
* there is at most one CPU modifying @con->level and that CPU is using only
* read-modify-write operations to do so.
*
* Only use this function to read flags via the related sysfs interface.
* The sysfs API makes sure that the structure could not disappear while
* the interface is used.
*
* Context: Sysfs interface for the given console.
* Return: The current value of the @con->level field.
*/
static inline
int console_sysfs_read_loglevel(const struct console *con)
{
/*
* The READ_ONCE() matches the WRITE_ONCE() when @level is modified
* for registered consoles.
*/
return data_race(READ_ONCE(con->level));
}
/**
* console_sysfs_write_flags - Write the console specific loglevel via
* sysfs interface.
* @con: struct console pointer of console to write flags to
* @flags: new flags value to write
*
* Only use this function to write flags via the related sysfs interface.
* The sysfs API makes sure that the structure could not disappear while
* the interface is used.
*
* Context: Any context.
*/
static inline
void console_sysfs_write_loglevel(struct console *con, int con_level)
{
/* This matches the READ_ONCE() in console_srcu_read_flags(). */
WRITE_ONCE(con->level, con_level);
}
> + source = console_effective_loglevel_source(con_level);
> +
> + switch (source) {
> + case LLS_IGNORE_LOGLEVEL:
> + str = "ignore_loglevel";
> + break;
> + case LLS_LOCAL:
> + str = "local";
> + break;
> + case LLS_GLOBAL:
> + str = "global";
> + break;
> + default:
> + str = "unknown";
> + break;
> + }
> +
> + return str;
> +}
> +
> +static ssize_t effective_loglevel_source_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct console *con = dev_get_drvdata(dev);
> + const char *str;
> +
> + str = console_effective_loglevel_source_str(con);
> + return sysfs_emit(buf, "%s\n", str);
> +}
> +
> +static DEVICE_ATTR_RO(effective_loglevel_source);
> +
> +static ssize_t effective_loglevel_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct console *con = dev_get_drvdata(dev);
> + int con_level;
> + int cookie;
> +
> + cookie = console_srcu_read_lock();
> + con_level = console_srcu_read_loglevel(con);
> + console_srcu_read_unlock(cookie);
Same here.
> + return sysfs_emit(buf, "%d\n", console_effective_loglevel(con_level));
> +}
> +
> +static DEVICE_ATTR_RO(effective_loglevel);
> +
> +static ssize_t loglevel_show(struct device *dev, struct device_attribute *attr,
> + char *buf)
> +{
> + struct console *con = dev_get_drvdata(dev);
> + int con_level;
> + int cookie;
> +
> + cookie = console_srcu_read_lock();
> + con_level = console_srcu_read_loglevel(con);
> + console_srcu_read_unlock(cookie);
and here.
> + return sysfs_emit(buf, "%d\n", con_level);
> +}
> +
> +static ssize_t loglevel_store(struct device *dev, struct device_attribute *attr,
> + const char *buf, size_t size)
> +{
> + struct console *con = dev_get_drvdata(dev);
> + ssize_t ret;
> + int level;
> + int cookie;
> +
> + ret = kstrtoint(buf, 10, &level);
> + if (ret < 0)
> + return ret;
> +
> + /* -1 means "use global loglevel" */
> + if (level == -1)
> + goto out;
> +
> + /*
> + * Reject level 0 (KERN_EMERG) - per-console loglevel must be > 0.
> + * Emergency messages should go to all consoles, so they cannot be
> + * filtered per-console.
> + */
> + if (level == 0)
> + return -ERANGE;
> +
> + if (console_clamp_loglevel(level) != level)
> + return -ERANGE;
> +
> + /*
> + * If the system has a minimum console loglevel set (via sysctl or
> + * kernel parameter), enforce it. This prevents setting per-console
> + * loglevels below the system minimum.
> + */
> + if (minimum_console_loglevel > CONSOLE_LOGLEVEL_MIN &&
> + level < minimum_console_loglevel)
> + return -ERANGE;
> +
> +out:
> + cookie = console_srcu_read_lock();
> + WRITE_ONCE(con->level, level);
> + console_srcu_read_unlock(cookie);
and here. Note that we need to add the data_race() as discussed
in v6.
> +
> + return size;
> +}
> +
I propose to squash the following changes into this patch:
diff --git a/Documentation/admin-guide/per-console-loglevel.rst b/Documentation/admin-guide/per-console-loglevel.rst
index 69eede12e20f..f621a6785f81 100644
--- a/Documentation/admin-guide/per-console-loglevel.rst
+++ b/Documentation/admin-guide/per-console-loglevel.rst
@@ -255,7 +255,7 @@ Deprecated
* ``kernel.printk`` sysctl: this takes four values, setting
``kernel.console_loglevel``, ``kernel.default_message_loglevel``, the minimum
console loglevel, and a fourth unused value. The interface is generally
- considered to quite confusing, doesn't perform checks on the values given,
+ considered to be quite confusing, doesn't perform checks on the values given,
and is unaware of per-console loglevel semantics.
Chris Down <chris@chrisdown.name>, 18-November-2025
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 170fa8e14ea9..e73276880d51 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -4118,9 +4118,6 @@ static void try_enable_default_console(struct console *newcon)
if (newcon->index < 0)
newcon->index = 0;
- newcon->level = LOGLEVEL_DEFAULT;
- newcon->classdev = NULL;
-
if (console_call_setup(newcon, NULL) != 0)
return;
diff --git a/kernel/printk/sysfs.c b/kernel/printk/sysfs.c
index 5252e6e04908..fc029b425809 100644
--- a/kernel/printk/sysfs.c
+++ b/kernel/printk/sysfs.c
@@ -6,17 +6,57 @@
#include <linux/slab.h>
#include "internal.h"
+/**
+ * console_srcu_read_loglevel - Locklessly read the console specific loglevel
+ * when accessing the related sysfs interface
+ * @con: struct console pointer of console to read loglevel from
+ *
+ * Locklessly reading @con->level provides a consistent read value because
+ * there is at most one CPU modifying @con->level and that CPU is using only
+ * read-modify-write operations to do so.
+ *
+ * Only use this function to read flags via the related sysfs interface.
+ * The sysfs API makes sure that the structure could not disappear while
+ * the interface is used.
+ *
+ * Context: Sysfs interface for the given console.
+ * Return: The current value of the @con->level field.
+ */
+static inline
+int console_sysfs_read_loglevel(const struct console *con)
+{
+ /*
+ * The READ_ONCE() matches the WRITE_ONCE() when @level is modified
+ * for registered consoles.
+ */
+ return data_race(READ_ONCE(con->level));
+}
+
+/**
+ * console_sysfs_write_flags - Write the console specific loglevel via
+ * sysfs interface.
+ * @con: struct console pointer of console to write flags to
+ * @flags: new flags value to write
+ *
+ * Only use this function to write flags via the related sysfs interface.
+ * The sysfs API makes sure that the structure could not disappear while
+ * the interface is used.
+ *
+ * Context: Any context.
+ */
+static inline
+void console_sysfs_write_loglevel(struct console *con, int con_level)
+{
+ /* This matches the READ_ONCE() in console_srcu_read_flags(). */
+ WRITE_ONCE(con->level, con_level);
+}
+
static const char *
-console_effective_loglevel_source_str(const struct console *con)
+console_effective_loglevel_source_str(int con_level)
{
enum loglevel_source source;
const char *str;
- int con_level;
- int cookie;
- cookie = console_srcu_read_lock();
- con_level = console_srcu_read_loglevel(con);
- console_srcu_read_unlock(cookie);
source = console_effective_loglevel_source(con_level);
switch (source) {
@@ -43,8 +83,10 @@ static ssize_t effective_loglevel_source_show(struct device *dev,
{
struct console *con = dev_get_drvdata(dev);
const char *str;
+ int con_level;
- str = console_effective_loglevel_source_str(con);
+ con_level = console_sysfs_read_loglevel(con);
+ str = console_effective_loglevel_source_str(con_level);
return sysfs_emit(buf, "%s\n", str);
}
@@ -55,11 +97,8 @@ static ssize_t effective_loglevel_show(struct device *dev,
{
struct console *con = dev_get_drvdata(dev);
int con_level;
- int cookie;
- cookie = console_srcu_read_lock();
- con_level = console_srcu_read_loglevel(con);
- console_srcu_read_unlock(cookie);
+ con_level = console_sysfs_read_loglevel(con);
return sysfs_emit(buf, "%d\n", console_effective_loglevel(con_level));
}
@@ -70,11 +109,8 @@ static ssize_t loglevel_show(struct device *dev, struct device_attribute *attr,
{
struct console *con = dev_get_drvdata(dev);
int con_level;
- int cookie;
- cookie = console_srcu_read_lock();
- con_level = console_srcu_read_loglevel(con);
- console_srcu_read_unlock(cookie);
+ con_level = console_sysfs_read_loglevel(con);
return sysfs_emit(buf, "%d\n", con_level);
}
@@ -84,7 +120,6 @@ static ssize_t loglevel_store(struct device *dev, struct device_attribute *attr,
struct console *con = dev_get_drvdata(dev);
ssize_t ret;
int level;
- int cookie;
ret = kstrtoint(buf, 10, &level);
if (ret < 0)
@@ -115,9 +150,7 @@ static ssize_t loglevel_store(struct device *dev, struct device_attribute *attr,
return -ERANGE;
out:
- cookie = console_srcu_read_lock();
- WRITE_ONCE(con->level, level);
- console_srcu_read_unlock(cookie);
+ console_sysfs_write_loglevel(con, level);
return size;
}
Best Regards,
Petr
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels
2025-11-18 19:07 ` [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels Chris Down
2025-11-20 16:50 ` Petr Mladek
@ 2025-11-21 11:38 ` Petr Mladek
1 sibling, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-21 11:38 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:27, Chris Down wrote:
> A sysfs interface under /sys/class/console/ is created that permits
> viewing and configuring per-console attributes. This is the main
> interface with which we expect users to interact with and configure
> per-console loglevels.
I made the following test:
1. Enable some debugging (CONFIG_DEBUG_KOBJECT=y, CONFIG_DEBUG_DRIVER=y)
2. Compile ttynull driver as a module (CONFIG_NULL_TTY=m)
3. Add "console=null" on the command line
4. Load the module "modprobe ttynull"
5. Remove the module "rmmod ttynull"
And I got the following warning when the module was removed:
[ 382.299081] printk: legacy console [ttynull0] disabled
[ 382.301829] device: 'ttynull0': device_unregister
[ 382.302057] kobject: 'ttynull0' (000000006a6b229d): kobject_uevent_env
[ 382.302088] kobject: 'ttynull0' (000000006a6b229d): fill_kobj_path: path = '/devices/virtual/console/ttynull0'
[ 382.302250] kobject: 'ttynull0' (000000006a6b229d): kobject_cleanup, parent 0000000000000000
[ 382.302261] kobject: 'ttynull0' (000000006a6b229d): calling ktype release
[ 382.302272] kobject: 'ttynull0': free name
[ 382.302281] ------------[ cut here ]------------
[ 382.302289] refcount_t: underflow; use-after-free.
[ 382.302319] WARNING: CPU: 4 PID: 1857 at lib/refcount.c:28 refcount_warn_saturate+0xbe/0x110
[ 382.302335] Modules linked in: ttynull(E-)
[ 382.302366] CPU: 4 UID: 0 PID: 1857 Comm: rmmod Tainted: G E 6.18.0-rc6-default+ #448 PREEMPT(full) b65a5eeebb0a78c479429a4b06dbf6320bbcd33d
[ 382.302379] Tainted: [E]=UNSIGNED_MODULE
[ 382.302388] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS rel-1.16.3-2-gc13ff2cd-prebuilt.qemu.org 04/01/2014
[ 382.302396] RIP: 0010:refcount_warn_saturate+0xbe/0x110
[ 382.302407] Code: 01 01 e8 05 11 60 ff 0f 0b e9 29 2f 3b ff 80 3d cb 17 b0 01 00 75 85 48 c7 c7 28 d8 00 9d c6 05 bb 17 b0 01 01 e8 e2 10 60 ff <0f> 0b e9 06 2f 3b ff 80 3d a9 17 b0 01 00 0f 85 5e ff ff ff 48 c7
[ 382.302417] RSP: 0018:ffffcbd70189fe60 EFLAGS: 00010282
[ 382.302433] RAX: 0000000000000000 RBX: ffffffffc0459040 RCX: 0000000000000002
[ 382.302441] RDX: ffff8b32f57dff48 RSI: 0000000000000001 RDI: 00000000ffffffff
[ 382.302450] RBP: ffff8b328b4f7000 R08: 00000000fff7ffff R09: ffff8b32fe3fdfa8
[ 382.302458] R10: 0000000000000004 R11: 00000000fff80000 R12: ffffcbd70189ff58
[ 382.302467] R13: 00000000000000b0 R14: 0000000000000000 R15: 0000000000000000
[ 382.302479] FS: 00007f2e97442740(0000) GS:ffff8b3357178000(0000) knlGS:0000000000000000
[ 382.302488] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 382.302497] CR2: 00007fa3088176f8 CR3: 0000000105373000 CR4: 0000000000750ef0
[ 382.302524] PKRU: 55555554
[ 382.302532] Call Trace:
[ 382.302542] <TASK>
[ 382.302551] unregister_console_locked.cold+0x28e/0x2cd
[ 382.302582] unregister_console+0x1f/0x40
[ 382.302599] ttynull_exit+0x10/0xed0 [ttynull 4b641f8e4981ec0e180c1229c097d2bddb0fbe2a]
[ 382.302613] __do_sys_delete_module.isra.0+0x19b/0x270
[ 382.302648] ? srso_alias_return_thunk+0x5/0xfbef5
[ 382.302665] do_syscall_64+0x7a/0x360
[ 382.302692] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 382.302703] RIP: 0033:0x7f2e95d18437
[ 382.302715] Code: 73 01 c3 48 8b 0d 59 0a 2d 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 29 0a 2d 00 f7 d8 64 89 01 48
[ 382.302725] RSP: 002b:00007fff591d8468 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0
[ 382.302740] RAX: ffffffffffffffda RBX: 00007fff591d84c8 RCX: 00007f2e95d18437
[ 382.302748] RDX: 000000000000000a RSI: 0000000000000800 RDI: 000055fe1453b5e8
[ 382.302757] RBP: 000055fe1453b580 R08: 00007fff591d73e1 R09: 0000000000000000
[ 382.302765] R10: 00007f2e95d9d660 R11: 0000000000000206 R12: 00007fff591d86a0
[ 382.302774] R13: 00007fff591da738 R14: 000055fe1453a3c0 R15: 000055fe1453b580
[ 382.302834] </TASK>
[ 382.302843] irq event stamp: 2523
[ 382.302851] hardirqs last enabled at (2529): [<ffffffff9b40932e>] __up_console_sem+0x5e/0x70
[ 382.302863] hardirqs last disabled at (2534): [<ffffffff9b409313>] __up_console_sem+0x43/0x70
[ 382.302873] softirqs last enabled at (1622): [<ffffffff9b35cb2c>] handle_softirqs+0x32c/0x400
[ 382.302885] softirqs last disabled at (1617): [<ffffffff9b35cce9>] __irq_exit_rcu+0xd9/0x150
[ 382.302896] ---[ end trace 0000000000000000 ]---
[ 382.303050] device: 'ttynull': device_unregister
[ 382.303445] PM: Removing info for No Bus:ttynull
[ 382.303454] kobject: 'ttynull' (0000000012b1dfce): kobject_uevent_env
[ 382.303459] kobject: 'ttynull' (0000000012b1dfce): fill_kobj_path: path = '/devices/virtual/tty/ttynull'
[ 382.303494] kobject: 'ttynull' (0000000012b1dfce): kobject_cleanup, parent 0000000000000000
[ 382.303498] kobject: 'ttynull' (0000000012b1dfce): calling ktype release
[ 382.303502] kobject: 'ttynull': free name
[ 382.303506] kobject: '(null)' (00000000eec627ea): kobject_cleanup, parent 0000000000000000
[ 382.303516] kobject: '(null)' (00000000eec627ea): calling ktype release
[ 382.325859] kobject: 'holders' (0000000087b71c70): kobject_cleanup, parent 0000000040844ced
[ 382.325880] kobject: 'holders' (0000000087b71c70): auto cleanup kobject_del
[ 382.325918] kobject: 'holders' (0000000087b71c70): calling ktype release
[ 382.325931] kobject: (0000000087b71c70): dynamic_kobj_release
[ 382.325943] kobject: 'holders': free name
[ 382.326895] kobject: 'ttynull' (0000000040844ced): kobject_cleanup, parent 00000000546c84c0
[ 382.326915] kobject: 'ttynull' (0000000040844ced): auto cleanup kobject_del
[ 382.326928] kobject: 'ttynull' (0000000040844ced): auto cleanup 'remove' event
[ 382.326941] kobject: 'ttynull' (0000000040844ced): kobject_uevent_env
[ 382.326959] kobject: 'ttynull' (0000000040844ced): fill_kobj_path: path = '/module/ttynull'
[ 382.327049] kobject: 'ttynull' (0000000040844ced): calling ktype release
[ 382.327148] kobject: 'ttynull': free name
> The lifecycle of this classdev looks like this on registration:
>
> register_console(con)/printk_late_init()
> console_register_device(con)
> device_initialize(con->classdev) # kref_init: refcount = 1
> device_add(con->classdev) # get_device: refcount++ (to 2)
I think that the problem is here. It is true that device_add() gets
extra refcount at the beginning but it is decremented on success,
see:
int device_add(struct device *dev)
{
[...]
dev = get_device(dev); <---- refcount++ (to 2)
[...]
done:
put_device(dev); <----- refcount-- (back to 1)
return error;
> At stable state, the refcount is two.
>
> Console unregistration looks like this:
>
> unregister_console_locked(con)
> struct device *dev = console->classdev;
> console->classdev = NULL;
> device_unregister(dev)
> device_del(dev)
> device_remove_class_symlinks(dev)
> sysfs_delete_link()
> kernfs_remove_by_name_ns()
> __kernfs_remove()
> kernfs_drain()
> kernfs_drain_open_files() # wait for close()
> kobject_del(&dev->kobj) # removes from sysfs, does NOT change refcount
> put_device(dev) # kref_put: refcount-- (from 2 to 1)
> put_device(dev) # kref_put: refcount-- (from 1 to 0)
This extra put_device() is superfluous and must not be called,
see below.
> kobject_release()
> kobject_cleanup()
> device_release()
> console_classdev_release(dev)
> kfree(dev)
> --- a/kernel/printk/printk.c
> +++ b/kernel/printk/printk.c
> @@ -4379,6 +4383,13 @@ static int unregister_console_locked(struct console *console)
> if (console->flags & CON_NBCON)
> nbcon_free(console);
>
> + if (console->classdev) {
> + struct device *dev = console->classdev;
> + console->classdev = NULL;
> + device_unregister(dev);
> + put_device(dev);
The WARNING has gone after I removed this extra put_device().
And it seems to work well. The sysfs interface gets removed...
Just for record. I tried to load/remove the "ttynull" module
several times and it worked as well.
> + }
> +
> console_sysfs_notify();
>
> if (console->exit)
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 09/13] printk: Support setting initial console loglevel via console= on cmdline
2025-11-18 19:07 ` [PATCH v7 09/13] printk: Support setting initial console loglevel via console= on cmdline Chris Down
@ 2025-11-21 14:05 ` Petr Mladek
0 siblings, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-21 14:05 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:37, Chris Down wrote:
> Extend the console= kernel command line parameter to support specifying
> per-console loglevels at boot time. This is achieved by introducing a
> new loglevel option that can be passed as a loglevel option within a
> console= stanza.
>
> For example, this is an example of how one might configure netconsole
> devices to print messages with a higher priority than loglevel 3
> (KERN_ERR) at startup:
>
> console=netcon0,loglevel:3
>
> Signed-off-by: Chris Down <chris@chrisdown.name>
Looks goot to me and works well:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Tested-by: Petr Mladek <pmladek@suse.com>
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 10/13] printk: Add sysctl interface to set global loglevels
2025-11-18 19:07 ` [PATCH v7 10/13] printk: Add sysctl interface to set global loglevels Chris Down
@ 2025-11-21 14:23 ` Petr Mladek
0 siblings, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-21 14:23 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:07:46, Chris Down wrote:
> Introduce two new sysctl interfaces for configuring global loglevels:
>
> - kernel.console_loglevel: Sets the global console loglevel, determining
> the minimum priority of messages printed to consoles. Messages with a
> loglevel lower than this value will be printed.
> - kernel.default_message_loglevel: Sets the default loglevel for
> messages that do not specify an explicit loglevel.
>
> The kernel.printk sysctl was previously used to set multiple loglevel
> parameters simultaneously, but it was confusing and lacked proper
> validation. By introducing these dedicated sysctl interfaces, we provide
> a clearer and more granular way to configure the loglevels.
>
> Signed-off-by: Petr Mladek <pmladek@suse.com>
> Signed-off-by: Chris Down <chris@chrisdown.name>
Looks good and works well:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Tested-by: Petr Mladek <pmladek@suse.com>
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels
2025-11-18 19:08 ` [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels Chris Down
@ 2025-11-21 15:52 ` Petr Mladek
2025-11-23 21:21 ` John Ogness
1 sibling, 0 replies; 27+ messages in thread
From: Petr Mladek @ 2025-11-21 15:52 UTC (permalink / raw)
To: Chris Down
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, John Ogness, Geert Uytterhoeven, Tony Lindgren,
kernel-team
On Wed 2025-11-19 03:08:01, Chris Down wrote:
> The per-console loglevel feature documentation could use some practical
> guidance. This commit adds:
>
> - Examples section covering runtime configuration, effective loglevel
> checking, and boot-time configuration
> - Common use case demonstrating high-performance netconsole with quiet
> serial console fallback
> - Performance impact section explaining how per-console loglevels reduce
> latency by filtering messages before slow console writes
> - Troubleshooting section addressing common issues like messages not
> appearing, loglevel constraints, and minimum console loglevel
I would remove the section about the "minimum console loglevel",
see below.
> - Edge cases section documenting behavior with concurrent writes,
> console unregistration, and global loglevel changes
>
> The guidance interleaves advice about many parts of this patchset, so
> let's have it in a distinct commit.
>
> This documentation will help users understand how to effectively use and
> debug per-console loglevels.
>
> +Performance Impact
> +------------------
> +
> +When a console has a higher (less verbose) loglevel than the global level,
> +messages that would normally be sent to that console are filtered out before
> +the console write callback is invoked. This eliminates the latency that would
> +be incurred by writing those messages to slow consoles (e.g., serial ports).
The above section is either confusing or wrong ;-) A higher loglevel
number means that more loglevel are allowed and the console is more verbose.
> +For example, setting a serial console to WARN level (4) while keeping
> +netconsole at INFO level (6) prevents INFO and NOTICE messages from being
> +written to the slow serial port, reducing application stalls during verbose
> +logging periods.
> +
> +Serial console writes can take tens of milliseconds per message. During
> +periods of heavy logging (e.g., during network debugging or block I/O tracing),
> +this can cause significant application-level stalls. By setting a higher
> +per-console loglevel for the serial console, you can avoid these stalls while
> +still capturing all messages on faster consoles like netconsole.
This is true for legacy consoles. The drivers converted to nbcon
API offload messages to a kthread when the system is working properly.
It prevents the stalls but the console might be far behind and
the oldest messages can get lost when the log buffer is full.
And the stalls are still possible when the system enters an
emergency, e.g. Oops, still, or WARN().
I think how to update this section. I would write something like:
Performance Impact
------------------
Kernel messages used to be flushed to the consoles immediately even
from a context where the scheduling is not possible. It increases
a chance to see the messages even when the system is in a bad state.
But it might cause significant application-level stalls (e.g., during
network debugging or block I/O tracing). Note that serial console
writes can take tens of milliseconds per message.
The console drivers are being converted to nbcon API (the letter 'N'
in /proc/consoles output). These drivers write the messages in a
dedicated kthreads when the system is working properly. It reduces
the risk of stalls. But the messages are still flushed immediately
when the system detects an emergency situation, for example Oops,
stall, or a warning. Also the messages can get lost when the ring
buffer is full and the console driver is far behind with flushing.
For example, setting a serial console to WARN level (4) while keeping
netconsole at INFO level (6) prevents INFO and NOTICE messages from being
written to the slow serial port. It reduces the risk of application
stalls or message loses during verbose logging periods.
> +Setting below minimum_console_loglevel fails
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +If you get an error when trying to set a loglevel, check the system-wide
> +minimum::
> +
> + cat /proc/sys/kernel/console_loglevel
> +
> +Per-console loglevels cannot be set below this minimum. This is a safety
> +feature to ensure critical messages are always visible.
This section is not valid anymore. I think that it is from an earlier
version of the patchset where the per-console loglevel only allowed
to reduce the verbosity. But this version allows to simply replace
the global setting to any valid value.
Or the section was about the "minimum_console_loglevel" variable.
But it is basically hardcoded to 1. I doubt that anyone
modifies it in practice.
I would just remove this section..
Best Regards,
Petr
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels
2025-11-18 19:08 ` [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels Chris Down
2025-11-21 15:52 ` Petr Mladek
@ 2025-11-23 21:21 ` John Ogness
1 sibling, 0 replies; 27+ messages in thread
From: John Ogness @ 2025-11-23 21:21 UTC (permalink / raw)
To: Chris Down, Petr Mladek
Cc: linux-kernel, Greg Kroah-Hartman, Sergey Senozhatsky,
Steven Rostedt, Geert Uytterhoeven, Tony Lindgren, kernel-team
On 2025-11-19, Chris Down <chris@chrisdown.name> wrote:
> diff --git a/Documentation/admin-guide/per-console-loglevel.rst b/Documentation/admin-guide/per-console-loglevel.rst
> index 4908d5d8ed4f..69eede12e20f 100644
> --- a/Documentation/admin-guide/per-console-loglevel.rst
> +++ b/Documentation/admin-guide/per-console-loglevel.rst
> @@ -97,6 +97,158 @@ are using ``ttyS0``, the console backing it can be viewed at
> This will be in effect if no other global control overrides it. Look at
> ``effective_loglevel`` and ``effective_loglevel_source`` to verify that.
>
> +Examples
> +--------
> +
> +Setting per-console loglevel at runtime
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Set serial console to only show warnings and above (level 4)::
> +
> + echo 4 > /sys/class/console/ttyS0/loglevel
Setting it to 4 would only show "above warnings" ... you will not see
warning messages.
> +
> +Set netconsole to show info and above (level 6)::
> +
> + echo 6 > /sys/class/console/netcon0/loglevel
Setting it to 6 would only show "above info" ... you will not see info
messages.
> +Common use case - high performance with serial fallback
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +A common configuration is to set netconsole to a verbose level for normal
> +debugging, while keeping the serial console quiet to avoid performance impact,
> +but still available for emergencies::
> +
> + # Netconsole gets INFO and above (verbose)
> + echo 6 > /sys/class/console/netcon0/loglevel
6 only shows "above INFO" ... you will not see INFO.
> +
> + # Serial console gets only WARN and above (quiet, for emergencies)
> + echo 4 > /sys/class/console/ttyS0/loglevel
4 only shows "above WARN" ... you will not see WARN.
> +
> +This allows you to see informational messages on the fast netconsole without
> +the latency impact of writing them to the slow serial port.
> +
> +Performance Impact
> +------------------
> +
> +When a console has a higher (less verbose) loglevel than the global level,
> +messages that would normally be sent to that console are filtered out before
> +the console write callback is invoked. This eliminates the latency that would
> +be incurred by writing those messages to slow consoles (e.g., serial ports).
> +
> +For example, setting a serial console to WARN level (4) while keeping
> +netconsole at INFO level (6) prevents INFO and NOTICE messages from being
> +written to the slow serial port, reducing application stalls during verbose
> +logging periods.
Setting serial to 4 will also prevent WARN messages.
> +
> +Serial console writes can take tens of milliseconds per message. During
> +periods of heavy logging (e.g., during network debugging or block I/O tracing),
> +this can cause significant application-level stalls. By setting a higher
> +per-console loglevel for the serial console, you can avoid these stalls while
> +still capturing all messages on faster consoles like netconsole.
> +
> +Troubleshooting
> +---------------
> +
> +Messages not appearing on console despite setting loglevel
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +1. Check effective loglevel source::
> +
> + cat /sys/class/console/<name>/effective_loglevel_source
> +
> + If it shows ``ignore_loglevel``, you have the ``printk.ignore_loglevel``
> + kernel parameter set, which overrides all level controls. Remove it from
> + your kernel command line or set it to N in sysfs::
> +
> + echo N > /sys/module/printk/parameters/ignore_loglevel
> +
> +2. Check if per-console loglevels are being ignored::
> +
> + cat /sys/module/printk/parameters/ignore_per_console_loglevel
> +
> + If it shows ``Y``, per-console settings are disabled. Set it to N::
> +
> + echo N > /sys/module/printk/parameters/ignore_per_console_loglevel
> +
> +3. Verify the message priority is high enough::
> +
> + cat /sys/class/console/<name>/effective_loglevel
> +
> + Messages must have priority less than this value to appear. For example,
> + if effective_loglevel is 4, only messages with priority 0-3 (EMERG, ALERT,
> + CRIT, ERR) will be printed. If you want to see WARN messages (priority 4),
> + you need to increase the effective_loglevel to at least 5.
Yay! Here it is documented correctly. ;-)
> +
> +Cannot set loglevel to 0
> +~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +Per-console loglevels cannot be set to 0 (KERN_EMERG). This is by design, as
> +level 0 is reserved for the most critical system messages that should always
> +go to all consoles. To use the global loglevel, set the per-console loglevel
> +to -1::
> +
> + echo -1 > /sys/class/console/<name>/loglevel
> +
> +Setting below minimum_console_loglevel fails
> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +If you get an error when trying to set a loglevel, check the system-wide
> +minimum::
> +
> + cat /proc/sys/kernel/console_loglevel
> +
> +Per-console loglevels cannot be set below this minimum. This is a safety
> +feature to ensure critical messages are always visible.
> +
> +Edge cases
> +~~~~~~~~~~
> +
> +**Setting all consoles to high loglevels**: If you set all consoles to
> +very high loglevels (e.g., 1 or 2), most messages won't appear anywhere.
> +This is valid but probably not what you want. Keep at least one console
> +at a reasonable level for monitoring.
Keep in mind that all messages (regardless of loglevel) are always
available to userspace via dmesg or syslogd. So saying "messages won't
appear anywhere" is a bit misleading.
John Ogness
^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2025-11-23 21:21 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-18 19:06 [PATCH v7 00/13] printk: console: Per-console loglevels Chris Down
2025-11-18 19:06 ` [PATCH v7 01/13] printk: Avoid delaying messages that aren't solicited by any console Chris Down
2025-11-18 19:33 ` Chris Down
2025-11-19 15:46 ` Petr Mladek
2025-11-18 19:06 ` [PATCH v7 02/13] printk: Use effective loglevel for suppression and extended console state Chris Down
2025-11-18 19:07 ` [PATCH v7 03/13] printk: console: Implement core per-console loglevel infrastructure Chris Down
2025-11-18 19:34 ` Chris Down
2025-11-19 16:49 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 04/13] printk: Ignore per-console loglevel in sysrq Chris Down
2025-11-19 16:51 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 05/13] printk: Add synchronisation for concurrent console state changes Chris Down
2025-11-19 16:58 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 06/13] printk: Support toggling per-console loglevel via syslog() and cmdline Chris Down
2025-11-20 10:05 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 07/13] printk: console: Introduce sysfs interface for per-console loglevels Chris Down
2025-11-20 16:50 ` Petr Mladek
2025-11-21 11:38 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 08/13] printk: Constrain hardware-addressed console checks to name position Chris Down
2025-11-18 19:07 ` [PATCH v7 09/13] printk: Support setting initial console loglevel via console= on cmdline Chris Down
2025-11-21 14:05 ` Petr Mladek
2025-11-18 19:07 ` [PATCH v7 10/13] printk: Add sysctl interface to set global loglevels Chris Down
2025-11-21 14:23 ` Petr Mladek
2025-11-18 19:08 ` [PATCH v7 11/13] printk: docs: Add comprehensive guidance for per-console loglevels Chris Down
2025-11-21 15:52 ` Petr Mladek
2025-11-23 21:21 ` John Ogness
2025-11-18 19:08 ` [PATCH v7 12/13] printk: Deprecate the kernel.printk sysctl interface Chris Down
2025-11-18 19:08 ` [PATCH v7 13/13] printk: Purge default_console_loglevel Chris Down
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox