[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <662c8dbd954f8a52a099805299da93d4c5473a97.1764272407.git.chris@chrisdown.name>
Date: Fri, 28 Nov 2025 03:43:36 +0800
From: Chris Down <chris@...isdown.name>
To: Petr Mladek <pmladek@...e.com>
Cc: linux-kernel@...r.kernel.org,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Sergey Senozhatsky <senozhatsky@...omium.org>,
Steven Rostedt <rostedt@...dmis.org>,
John Ogness <john.ogness@...utronix.de>,
Geert Uytterhoeven <geert@...ux-m68k.org>,
Tony Lindgren <tony.lindgren@...ux.intel.com>, kernel-team@...com
Subject: [PATCH v8 07/21] printk: Introduce per-console loglevel support
Implement the logic to determine which loglevel should be used for
message filtering on each console.
The effective loglevel for a console is determined by a hierarchy of
controls:
1. ignore_loglevel (highest priority), which prints all messages regardless
of level, overriding both global and per-console settings.
2. The per-console loglevel. If set to a valid value (> 0), the console
uses this level instead of the global console_loglevel.
3. Global console_loglevel (fallback). This is used when per-console
loglevel is not set (LOGLEVEL_DEFAULT, i.e. -1).
Reviewed-by: Petr Mladek <pmladek@...e.com>
Signed-off-by: Chris Down <chris@...isdown.name>
---
kernel/printk/internal.h | 10 +++++
kernel/printk/nbcon.c | 10 +++--
kernel/printk/printk.c | 86 +++++++++++++++++++++++++++++++++++++++-
3 files changed, 101 insertions(+), 5 deletions(-)
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 1ed86577896c..f2ebaa2a6aa2 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -15,6 +15,16 @@ 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,
+};
+
+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 493c9e8b2dd5..a61a607a5159 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);
@@ -1509,9 +1511,9 @@ static int __nbcon_atomic_flush_pending_con(struct console *con, u64 stop_seq,
/*
* Match the console_srcu_read_lock()/unlock expectation embedded in
- * console_srcu_read_flags(), which is called from nbcon_emit_next_record().
- * Without this, unregister_console() cannot synchronise against the
- * atomic flusher.
+ * console_srcu_read_loglevel()/console_srcu_read_flags(), both of which
+ * are called from nbcon_emit_next_record(). Without this,
+ * unregister_console() cannot synchronise against the atomic flusher.
*/
cookie = console_srcu_read_lock();
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index c6b56f4d8072..d9c1bc4a32c4 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1280,6 +1280,89 @@ module_param(ignore_loglevel, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_loglevel,
"ignore loglevel setting (prints all kernel messages to the console)");
+/**
+ * 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);
@@ -3090,6 +3173,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,
@@ -3099,7 +3183,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;
--
2.51.2
Powered by blists - more mailing lists