[PATCH 1/2] bug: Provide WARN_ON.*DEFERRED() macros for console deferred output
From: Sebastian Andrzej Siewior
Date: Tue Jun 23 2026 - 10:28:51 EST
Provide a deferred version of the WARN_ON() macro. It will delay
flushing the console until a later context. It is needed in a context
where the caller holds locks which can lead to a deadlock content is
flushed to the console driver.
An example would from a warning from within the scheduler resulting in a
wake-up of a task.
Deferring the output works by using printk_deferred_enter/ exit() around
the printing output. This must be used in a context where the task can't
migrate to another CPU. This should be the case usually, since the
scheduler would acquire the rq lock whith disabled interrupts, but to be
safe preemption is disabled to guarantee this.
In order not to bloat the code on architectures which provide an
optimized __WARN_FLAGS() define BUGFLAG_DEFERRED which is handled by
__report_bug() and does not increase the code size.
Provide the DEFERRED macros based on __WARN_FLAGS and __WARN_FLAGS
macros. Extend __report_bug() to handle the deferred case.
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx>
---
include/asm-generic/bug.h | 41 +++++++++++++++++++++++++++++++++++++++
lib/bug.c | 16 +++++++++++++--
2 files changed, 55 insertions(+), 2 deletions(-)
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 09e8eccee8ed9..1e3ff00f709b8 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -14,6 +14,7 @@
#define BUGFLAG_DONE (1 << 2)
#define BUGFLAG_NO_CUT_HERE (1 << 3) /* CUT_HERE already sent */
#define BUGFLAG_ARGS (1 << 4)
+#define BUGFLAG_DEFERRED (1 << 5)
#define BUGFLAG_TAINT(taint) ((taint) << 8)
#define BUG_GET_TAINT(bug) ((bug)->flags >> 8)
#endif
@@ -115,6 +116,16 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
})
#endif
+#define WARN_ON_DEFERRED(condition) ({ \
+ int __ret_warn_on = !!(condition); \
+ if (unlikely(__ret_warn_on)) { \
+ __WARN_FLAGS(#condition, \
+ BUGFLAG_DEFERRED | \
+ BUGFLAG_TAINT(TAINT_WARN)); \
+ } \
+ unlikely(__ret_warn_on); \
+})
+
#ifndef WARN_ON_ONCE
#define WARN_ON_ONCE(condition) ({ \
int __ret_warn_on = !!(condition); \
@@ -125,6 +136,16 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
unlikely(__ret_warn_on); \
})
#endif
+
+#define WARN_ON_ONCE_DEFERRED(condition) ({ \
+ int __ret_warn_on = !!(condition); \
+ if (unlikely(__ret_warn_on)) { \
+ __WARN_FLAGS(#condition, \
+ BUGFLAG_ONCE | BUGFLAG_DEFERRED | \
+ BUGFLAG_TAINT(TAINT_WARN)); \
+ } \
+ unlikely(__ret_warn_on); \
+})
#endif /* __WARN_FLAGS */
#if defined(__WARN_FLAGS) && !defined(__WARN_printf)
@@ -159,6 +180,19 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
})
#endif
+#ifndef WARN_ON_DEFERRED
+#define WARN_ON_DEFERRED(condition) ({ \
+ int __ret_warn_on = !!(condition); \
+ if (unlikely(__ret_warn_on)) { \
+ guard(preempt)(); \
+ printk_deferred_enter() \
+ __WARN(); \
+ printk_deferred_exit() \
+ } \
+ unlikely(__ret_warn_on); \
+})
+#endif
+
#ifndef WARN
#define WARN(condition, format...) ({ \
int __ret_warn_on = !!(condition); \
@@ -180,6 +214,11 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
DO_ONCE_LITE_IF(condition, WARN_ON, 1)
#endif
+#ifndef WARN_ON_ONCE_DEFERRED
+#define WARN_ON_ONCE_DEFERRED(condition) \
+ DO_ONCE_LITE_IF(condition, WARN_ON_DEFERRED, 1)
+#endif
+
#ifndef WARN_ONCE
#define WARN_ONCE(condition, format...) \
DO_ONCE_LITE_IF(condition, WARN, 1, format)
@@ -215,7 +254,9 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
})
#endif
+#define WARN_ON_DEFERRED(condition) WARN_ON(condition)
#define WARN_ON_ONCE(condition) WARN_ON(condition)
+#define WARN_ON_ONCE_DEFERRED(condition) WARN_ON(condition)
#define WARN_ONCE(condition, format...) WARN(condition, format)
#define WARN_TAINT(condition, taint, format...) WARN(condition, format)
#define WARN_TAINT_ONCE(condition, taint, format...) WARN(condition, format)
diff --git a/lib/bug.c b/lib/bug.c
index 224f4cfa4aa31..f5768f5d17b47 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -196,7 +196,7 @@ void __warn_printf(const char *fmt, struct pt_regs *regs)
static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long bugaddr, struct pt_regs *regs)
{
- bool warning, once, done, no_cut, has_args;
+ bool warning, once, done, no_cut, has_args, deferred;
const char *file, *fmt;
unsigned line;
@@ -219,6 +219,7 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
done = bug->flags & BUGFLAG_DONE;
no_cut = bug->flags & BUGFLAG_NO_CUT_HERE;
has_args = bug->flags & BUGFLAG_ARGS;
+ deferred = bug->flags & BUGFLAG_DEFERRED;
if (warning && once) {
if (done)
@@ -229,7 +230,10 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
*/
bug->flags |= BUGFLAG_DONE;
}
-
+ if (deferred) {
+ preempt_disable_notrace();
+ printk_deferred_enter();
+ }
/*
* BUG() and WARN_ON() families don't print a custom debug message
* before triggering the exception handler, so we must add the
@@ -245,6 +249,10 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
/* this is a WARN_ON rather than BUG/BUG_ON */
__warn(file, line, (void *)bugaddr, BUG_GET_TAINT(bug), regs,
NULL);
+ if (deferred) {
+ printk_deferred_exit();
+ preempt_enable_notrace();
+ }
return BUG_TRAP_TYPE_WARN;
}
@@ -254,6 +262,10 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
pr_crit("kernel BUG at %pB [verbose debug info unavailable]\n",
(void *)bugaddr);
+ if (deferred) {
+ printk_deferred_exit();
+ preempt_enable_notrace();
+ }
return BUG_TRAP_TYPE_BUG;
}
--
2.53.0