[PATCH 1/2] printk: Introduce LOUD_CON flag

Marcos Paulo de Souza
2024-10-16T17:03:27Z

Introduce LOUD_CON flag to printk. The new flag will make it possible to
create a context where printk messages will never be suppressed. This
new context information will be stored in the already existing
printk_context per-CPU variable. This variable was changed from 'int' to
'unsigned int' to avoid issues with automatic casting.

This mechanism will be used in the next patch to create a loud_console
context on sysrq handling, removing an existing workaround on the
loglevel global variable. The workaround existed to make sure that sysrq
header messages were sent to all consoles.

Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
---
include/linux/printk.h | 3 +++
kernel/printk/internal.h | 3 +++
kernel/printk/printk.c | 8 +++++++-
kernel/printk/printk_safe.c | 27 +++++++++++++++++++++++++--
4 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index eca9bb2ee637..0ac9879d94ac 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -166,6 +166,9 @@ __printf(1, 2) __cold int _printk_deferred(const char *fmt, ...);
extern void __printk_deferred_enter(void);
extern void __printk_deferred_exit(void);

+extern void printk_loud_console_enter(void);
+extern void printk_loud_console_exit(void);
+
/*
* The printk_deferred_enter/exit macros are available only as a hack for
* some code paths that need to defer all printk console printing. Interrupts
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 3fcb48502adb..2649e8a66cef 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -53,6 +53,8 @@ int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write,

/* Flags for a single printk record. */
enum printk_info_flags {
+ /* always show on console, ignore console_loglevel */
+ LOG_LOUD_CON = 1,
LOG_NEWLINE = 2, /* text ended with a newline */
LOG_CONT = 8, /* text is a fragment of a continuation line */
};
@@ -90,6 +92,7 @@ bool printk_percpu_data_ready(void);

void defer_console_output(void);
bool is_printk_legacy_deferred(void);
+bool is_printk_console_loud(void);

u16 printk_parse_prefix(const char *text, int *level,
enum printk_info_flags *flags);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index beb808f4c367..b893825fe21d 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1321,6 +1321,7 @@ static void boot_delay_msec(int level)
unsigned long timeout;

if ((boot_delay == 0 || system_state >= SYSTEM_RUNNING)
+ || is_printk_console_loud()
|| suppress_message_printing(level)) {
return;
}
@@ -2273,6 +2274,9 @@ int vprintk_store(int facility, int level,
if (dev_info)
flags |= LOG_NEWLINE;

+ if (is_printk_console_loud())
+ flags |= LOG_LOUD_CON;
+
if (flags & LOG_CONT) {
prb_rec_init_wr(&r, reserve_size);
if (prb_reserve_in_last(&e, prb, &r, caller_id, PRINTKRB_RECORD_MAX)) {
@@ -2947,6 +2951,7 @@ bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
struct printk_info info;
struct printk_record r;
size_t len = 0;
+ bool loud_con;

/*
* Formatting extended messages requires a separate buffer, so use the
@@ -2965,9 +2970,10 @@ bool printk_get_next_message(struct printk_message *pmsg, u64 seq,

pmsg->seq = r.info->seq;
pmsg->dropped = r.info->seq - seq;
+ loud_con = r.info->flags & LOG_LOUD_CON;

/* Skip record that has level above the console loglevel. */
- if (may_suppress && suppress_message_printing(r.info->level))
+ if (!loud_con && may_suppress && suppress_message_printing(r.info->level))
goto out;

if (is_extended) {
diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c
index 2b35a9d3919d..4618988baeea 100644
--- a/kernel/printk/printk_safe.c
+++ b/kernel/printk/printk_safe.c
@@ -12,7 +12,30 @@

#include "internal.h"

-static DEFINE_PER_CPU(int, printk_context);
+static DEFINE_PER_CPU(unsigned int, printk_context);
+
+#define PRINTK_SAFE_CONTEXT_MASK 0x0000ffffU
+#define PRINTK_LOUD_CONSOLE_CONTEXT_MASK 0xffff0000U
+#define PRINTK_LOUD_CONSOLE_CONTEXT_OFFSET 0x00010000U
+
+void noinstr printk_loud_console_enter(void)
+{
+ cant_migrate();
+ this_cpu_add(printk_context, PRINTK_LOUD_CONSOLE_CONTEXT_OFFSET);
+}
+
+void noinstr printk_loud_console_exit(void)
+{
+ cant_migrate();
+ this_cpu_sub(printk_context, PRINTK_LOUD_CONSOLE_CONTEXT_OFFSET);
+}
+
+/* Safe in any context. CPU migration is always disabled when set. */
+bool is_printk_console_loud(void)
+{
+ return !!(this_cpu_read(printk_context) &
+ PRINTK_LOUD_CONSOLE_CONTEXT_MASK);
+}

/* Can be preempted by NMI. */
void __printk_safe_enter(void)
@@ -45,7 +68,7 @@ bool is_printk_legacy_deferred(void)
* context. CPU migration is always disabled when set.
*/
return (force_legacy_kthread() ||
- this_cpu_read(printk_context) ||
+ !!(this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK) ||
in_nmi());
}

--
2.46.1