mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] printk: fold consecutive duplicate messages
@ 2026-09-21  5:03 林濬哲
  2026-09-21  7:52 ` John Ogness
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: 林濬哲 @ 2026-09-21  5:03 UTC (permalink / raw)
  To: Petr Mladek
  Cc: Steven Rostedt, John Ogness, Sergey Senozhatsky, linux-kernel,
	m18667909625

A kernel bug can flood the console with thousands of copies of the
same message, drowning out everything else. Fold consecutive
duplicates into a single "last message repeated N times" summary,
flushed when 10 repeats accumulate or a 1s window elapses.

The dedup key is built from facility, level and the format string
address; format parameters are not part of the key since va_list can
only be consumed once and hashing rendered text would put string
comparisons on the printk fast path.

Messages at LOGLEVEL_ERR and above are never folded, and dedup is
skipped after suppress_printk / panic take effect since the call site
sits behind those checks. The dedup state is protected by a raw
spinlock, and the summary is printed only after dropping the lock to
avoid recursive self-deadlock.

Disable with printk_dedup=0 on the kernel command line or via the
printk_dedup module parameter.

Known limitation: dedup_lock is not NMI safe; a message from NMI
context while another CPU holds the lock will spin.

Signed-off-by: 林濬哲 <m18667909625@163.com>
Assisted-by: AI coding assistant (disclosed per kernel AI guidelines)
---
 kernel/printk/printk.c | 89 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 6d3d18a50da7..0c93767d041f 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -20,6 +20,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/kernel.h>
+#include <linux/hash.h>
 #include <linux/mm.h>
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
@@ -104,6 +105,91 @@ DEFINE_STATIC_SRCU(console_srcu);
  */
 int __read_mostly suppress_printk;
 
+static u32 dedup_last_key;
+static u32 dedup_repeat;
+static bool dedup_active;
+static u64 dedup_window_start;
+#define PRINTK_DEDUP_WINDOW_NS	1000000000ULL	/* 1s */
+static bool printk_dedup = true;
+module_param(printk_dedup, bool, 0644);
+MODULE_PARM_DESC(printk_dedup, "fold consecutive duplicate printk messages");
+/* protects the dedup state above; never held while printing */
+static DEFINE_RAW_SPINLOCK(dedup_lock);
+
+static int __init printk_dedup_setup(char *str)
+{
+	return kstrtobool(str, &printk_dedup);
+}
+early_param("printk_dedup", printk_dedup_setup);
+
+/*
+ * Detect consecutive duplicate printk messages and fold them away.
+ * Returns true if this message should be dropped.
+ *
+ * The dedup key is built from facility, level and the format string.
+ * Format parameters are intentionally not part of the key: va_list
+ * can only be consumed once, and hashing rendered text would put
+ * string comparisons on the printk fast path.
+ */
+static bool outputs_dedupe(int facility, int level, const char *fmt)
+{
+	unsigned long flags;
+	bool drop;
+	u64 now;
+	u32 key;
+	u32 n;
+
+	/* error and above must never be folded */
+	if (level <= LOGLEVEL_ERR || !printk_dedup)
+		return false;
+
+	key = hash_64((unsigned long)(fmt ? : ""), 32) ^
+	      hash_32(facility ^ level, 32);
+	now = ktime_get_ns();
+
+	raw_spin_lock_irqsave(&dedup_lock, flags);
+
+	if (dedup_active && key == dedup_last_key) {
+		dedup_repeat++;
+		/*
+		 * Force a flush when 10 repeats have accumulated or
+		 * the 1s window has elapsed, so the summary shows up.
+		 */
+		if (dedup_repeat >= 10 ||
+		    now - dedup_window_start > PRINTK_DEDUP_WINDOW_NS) {
+			n = dedup_repeat;
+			dedup_repeat = 0;
+			dedup_window_start = now;
+			drop = false;
+		} else {
+			drop = true;
+		}
+	} else {
+		if (dedup_active && dedup_repeat) {
+			n = dedup_repeat;
+			drop = false;
+		} else {
+			n = 0;
+			drop = false;
+		}
+		dedup_last_key = key;
+		dedup_repeat = 0;
+		dedup_window_start = now;
+		dedup_active = true;
+	}
+
+	raw_spin_unlock_irqrestore(&dedup_lock, flags);
+
+	/*
+	 * Print only after dropping the lock: pr_info() re-enters
+	 * vprintk_emit() and would deadlock on dedup_lock otherwise.
+	 */
+	if (n)
+		pr_info("last message repeated %u times\n", n);
+
+	return drop;
+}
+
 #ifdef CONFIG_LOCKDEP
 static struct lockdep_map console_lock_dep_map = {
 	.name = "console_lock"
@@ -2441,6 +2527,9 @@ asmlinkage int vprintk_emit(int facility, int level,
 	    !panic_triggering_all_cpu_backtrace)
 		return 0;
 
+	if (outputs_dedupe(facility, level, fmt))
+		return 0;
+
 	printk_get_console_flush_type(&ft);
 
 	/* If called from the scheduler, we can not call up(). */
-- 
2.53.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-09-23 21:34 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21  5:03 [PATCH] printk: fold consecutive duplicate messages 林濬哲
2026-09-21  7:52 ` John Ogness
2026-09-21 10:30   ` Lin Junzhe
2026-09-21 12:13     ` John Ogness
2026-09-21 14:40       ` Lin Junzhe
2026-09-23  9:43       ` Petr Mladek
2026-09-23 20:59 ` kernel test robot
2026-09-23 21:34 ` kernel test robot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®