[PATCH v8 0/4] kunit: Add support for suppressing warning backtraces

2 views
Skip to first unread message

Albert Esteve

unread,
May 4, 2026, 3:41:50 AMMay 4
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Simona Vetter, David Gow
Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

One option to address the problem would be to add messages such as
"expected warning backtraces start/end here" to the kernel log.
However, that would again require filter scripts, might result in
missing real problematic warning backtraces triggered while the test
is running, and the irrelevant backtrace(s) would still clog the
kernel log.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code.
Since each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it, without requiring any
architecture-specific code.

Overview:
Patch#1 Introduces the suppression infrastructure integrated into
KUnit's hook mechanism.
Patch#2 Adds selftests to validate the functionality.
Patch#3 Demonstrates real-world usage in the DRM subsystem.
Patch#4 Documents the new API and usage guidelines.

Design Notes:
Suppression is integrated into the existing KUnit hooks infrastructure,
reusing the kunit_running static branch for zero overhead
when no tests are running. The implementation lives entirely in the
kunit module; only a static-inline wrapper and a function pointer
slot are added to built-in code.

Suppression is checked at three points in the warning path:
- In `warn_slowpath_fmt()` (kernel/panic.c), for architectures without
__WARN_FLAGS. The check runs before any output, fully suppressing
both message and backtrace.
- In `__warn_printk()` (kernel/panic.c), for architectures that define
__WARN_FLAGS but not their own __WARN_printf (arm64, loongarch,
parisc, powerpc, riscv, sh). The check suppresses the warning message
text that is printed before the trap enters __report_bug().
- In `__report_bug()` (lib/bug.c), for architectures that define
__WARN_FLAGS. The check runs before `__warn()` is called, suppressing
the backtrace and stack dump.

To avoid double-counting on architectures where both `__warn_printk()`
and `__report_bug()` run for the same warning, the hook takes a bool
parameter: true to increment the suppression counter, false to suppress
without counting.

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via `kunit_add_action()`, ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Three API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped blocks with automatic
cleanup. The suppression handle is not accessible outside the block,
so warning counts (if needed) must be checked inside. Multiple
suppression blocks are allowed.
- KUNIT_START/END_SUPPRESSED_WARNING(test): manual macros for larger
blocks or post-suppression count checks. Limited to one pair per
scope.
- kunit_start/end_suppress_warning(test): direct functions that return
an explicit handle. Use when the handle needs to be retained, or passed
across helpers. Multiple suppression blocks are allowed.

This series is based on the RFC patch and subsequent discussion at
https://patchwork.kernel.org/project/linux-kselftest/patch/02546e59-1afe-4b08...@moroto.mountain/
and offers a more comprehensive solution of the problem discussed there.

Changes since RFC:
- Introduced CONFIG_KUNIT_SUPPRESS_BACKTRACE
- Minor cleanups and bug fixes
- Added support for all affected architectures
- Added support for counting suppressed warnings
- Added unit tests using those counters
- Added patch to suppress warning backtraces in dev_addr_lists tests

Changes since v1:
- Rebased to v6.9-rc1
- Added Tested-by:, Acked-by:, and Reviewed-by: tags
[I retained those tags since there have been no functional changes]
- Introduced KUNIT_SUPPRESS_BACKTRACE configuration option, enabled by
default.

Changes since v2:
- Rebased to v6.9-rc2
- Added comments to drm warning suppression explaining why it is needed.
- Added patch to move conditional code in arch/sh/include/asm/bug.h
to avoid kerneldoc warning
- Added architecture maintainers to Cc: for architecture specific patches
- No functional changes

Changes since v3:
- Rebased to v6.14-rc6
- Dropped net: "kunit: Suppress lock warning noise at end of dev_addr_lists tests"
since 3db3b62955cd6d73afde05a17d7e8e106695c3b9
- Added __kunit_ and KUNIT_ prefixes.
- Tested on interessed architectures.

Changes since v4:
- Rebased to v6.15-rc7
- Dropped all code in __report_bug()
- Moved all checks in WARN*() macros.
- Dropped all architecture specific code.
- Made __kunit_is_suppressed_warning nice to noinstr functions.

Changes since v5:
- Rebased to v7.0-rc3
- Added RCU protection for the suppressed warnings list.
- Added static key and branching optimization.
- Removed custom `strcmp` implementation and reworked
__kunit_is_suppressed_warning() entrypoint function.

Changes since v6:
- Moved suppression checks from WARN*() macros to warn_slowpath_fmt()
and __report_bug().
- Replaced stack-allocated suppression struct with kunit_kzalloc() heap
allocation tied to the KUnit test lifecycle.
- Changed suppression strategy from function-name matching to task-scoped:
all warnings on the current task are suppressed between START and END,
rather than only warnings originating from a specific named function.
- Simplified macro API: removed KUNIT_DECLARE_SUPPRESSED_WARNING(),
the START macro now takes (test) and handles allocation internally.
- Removed static key and branching optiomization, as by the time it
was executed, callers are already in warn slowpaths.
- Link to v6: https://lore.kernel.org/r/20260317-kunit_add_sup...@redhat.com

Changes since v7:
- Integrated suppression into existing KUnit hooks infrastructure
- Removed CONFIG_KUNIT_SUPPRESS_BACKTRACE
- Added suppression check in __warn_printk()
- Added spinlock for writer-side RCU protection
- Replaced explicit rcu_read_lock/unlock with guard(rcu)()
- Added scoped API (kunit_warning_suppress) using __cleanup attribute
- Updated DRM patch to use scoped API
- Expanded self-tests: incremental counting, cross-kthread isolation
- Rewrote documentation covering all three API forms with examples
- Link to v7: https://lore.kernel.org/r/20260420-kunit_add_sup...@redhat.com

--
2.34.1

---
Alessandro Carminati (1):
bug/kunit: Core support for suppressing warning backtraces

Guenter Roeck (3):
kunit: Add backtrace suppression self-tests
drm: Suppress intentional warning backtraces in scaling unit tests
kunit: Add documentation for warning backtrace suppression API

Documentation/dev-tools/kunit/usage.rst | 63 ++++++++++-
drivers/gpu/drm/tests/drm_rect_test.c | 23 +++-
include/kunit/test-bug.h | 25 +++++
include/kunit/test.h | 138 ++++++++++++++++++++++++
kernel/panic.c | 15 ++-
lib/bug.c | 10 ++
lib/kunit/Makefile | 4 +-
lib/kunit/backtrace-suppression-test.c | 184 ++++++++++++++++++++++++++++++++
lib/kunit/bug.c | 115 ++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
10 files changed, 571 insertions(+), 8 deletions(-)
---
base-commit: 80234b5ab240f52fa45d201e899e207b9265ef91
change-id: 20260312-kunit_add_support-2f35806b19dd

Best regards,
--
Albert Esteve <aes...@redhat.com>

Albert Esteve

unread,
May 4, 2026, 3:41:56 AMMay 4
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve
From: Alessandro Carminati <acar...@redhat.com>

Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code. Since
each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it. Limit changes to generic code
to the absolute minimum.

Implementation details:
Suppression is integrated into the existing KUnit hooks infrastructure
in test-bug.h, reusing the kunit_running static branch for zero
overhead when no tests are running.

Suppression is checked at three points in the warning path:
- In warn_slowpath_fmt(), the check runs before any output, fully
suppressing both message and backtrace. This covers architectures
without __WARN_FLAGS.
- In __warn_printk(), the check suppresses the warning message text.
This covers architectures that define __WARN_FLAGS but not their own
__WARN_printf (arm64, loongarch, parisc, powerpc, riscv, sh), where
the message is printed before the trap enters __report_bug().
- In __report_bug(), the check runs before __warn() is called,
suppressing the backtrace and stack dump.

To avoid double-counting on architectures where both __warn_printk()
and __report_bug() run for the same warning, kunit_is_suppressed_warning()
takes a bool parameter: true to increment the suppression counter
(used in warn_slowpath_fmt and __report_bug), false to check only
(used in __warn_printk).

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via kunit_add_action(), ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Three API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped, uses __cleanup for
automatic teardown on scope exit, kunit_add_action() as safety net
for abnormal exits (e.g. kthread_exit from failed assertions).
Suppression handle is only accessible inside the block.
- KUNIT_START/END_SUPPRESSED_WARNING(test): manual macros for larger
blocks or when warning counts need to be checked after suppression
ends. Limited to one pair per scope.
- kunit_start/end_suppress_warning(test): direct functions returning
an explicit handle, for retaining the handle within the test,
or for cross-function usage.

Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: Kees Cook <ke...@kernel.org>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
include/kunit/test-bug.h | 25 +++++++++
include/kunit/test.h | 138 +++++++++++++++++++++++++++++++++++++++++++++++
kernel/panic.c | 15 +++++-
lib/bug.c | 10 ++++
lib/kunit/Makefile | 3 +-
lib/kunit/bug.c | 115 +++++++++++++++++++++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
7 files changed, 305 insertions(+), 3 deletions(-)

diff --git a/include/kunit/test-bug.h b/include/kunit/test-bug.h
index 47aa8f21ccce8..6237e48ceadfd 100644
--- a/include/kunit/test-bug.h
+++ b/include/kunit/test-bug.h
@@ -23,6 +23,7 @@ DECLARE_STATIC_KEY_FALSE(kunit_running);
extern struct kunit_hooks_table {
__printf(3, 4) void (*fail_current_test)(const char*, int, const char*, ...);
void *(*get_static_stub_address)(struct kunit *test, void *real_fn_addr);
+ bool (*is_suppressed_warning)(bool count);
} kunit_hooks;

/**
@@ -60,9 +61,33 @@ static inline struct kunit *kunit_get_current_test(void)
} \
} while (0)

+/**
+ * kunit_is_suppressed_warning() - Check if warnings are being suppressed
+ * by the current KUnit test.
+ * @count: if true, increment the suppression counter on match.
+ *
+ * Returns true if the current task has active warning suppression.
+ * Uses the kunit_running static branch for zero overhead when no tests run.
+ *
+ * A single WARN*() may traverse multiple call sites in the warning path
+ * (e.g., __warn_printk() and __report_bug()). Pass @count = true at the
+ * primary suppression point to count each warning exactly once, and
+ * @count = false at secondary points to suppress output without
+ * inflating the count.
+ */
+static inline bool kunit_is_suppressed_warning(bool count)
+{
+ if (!static_branch_unlikely(&kunit_running))
+ return false;
+
+ return kunit_hooks.is_suppressed_warning &&
+ kunit_hooks.is_suppressed_warning(count);
+}
+
#else

static inline struct kunit *kunit_get_current_test(void) { return NULL; }
+static inline bool kunit_is_suppressed_warning(bool count) { return false; }

#define kunit_fail_current_test(fmt, ...) do {} while (0)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index 9cd1594ab697d..f278ec028019c 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -1795,4 +1795,142 @@ do { \
// include resource.h themselves if they need it.
#include <kunit/resource.h>

+/*
+ * Warning backtrace suppression API.
+ *
+ * Suppresses WARN*() backtraces on the current task while active. Three forms
+ * are provided, in order of convenience:
+ *
+ * - Scoped: kunit_warning_suppress(test) { ... }
+ * Suppression is active for the duration of the block. On normal exit,
+ * the for-loop increment deactivates suppression. On early exit (break,
+ * return, goto), the __cleanup attribute fires. On kthread_exit() (e.g.,
+ * a failed KUnit assertion), kunit_add_action() cleans up at test
+ * teardown. The suppression handle is only accessible inside the block,
+ * so warning counts must be checked before the block exits.
+ *
+ * - Manual macros: KUNIT_[START|END]_SUPPRESSED_WARNING(test)
+ * Suppression spans an explicit range in the same scope. kunit_add_action()
+ * guarantees cleanup even if KUNIT_END_SUPPRESSED_WARNING() is not reached.
+ * Prefer this form when suppressing warnings across a large block where
+ * extra indentation is undesirable, or when the warning count needs to be
+ * checked after suppression ends. Limited to one pair per scope.
+ *
+ * - Direct: kunit_start_suppress_warning() / kunit_end_suppress_warning()
+ * The underlying functions, returning an explicit handle pointer. Use
+ * when the handle needs to be retained (e.g., for post-suppression
+ * count checks) or passed across helper functions.
+ */
+struct kunit_suppressed_warning;
+
+struct kunit_suppressed_warning *
+kunit_start_suppress_warning(struct kunit *test);
+void kunit_end_suppress_warning(struct kunit *test,
+ struct kunit_suppressed_warning *w);
+int kunit_suppressed_warning_count(struct kunit_suppressed_warning *w);
+void __kunit_suppress_auto_cleanup(struct kunit_suppressed_warning **wp);
+bool kunit_has_active_suppress_warning(void);
+
+/**
+ * kunit_warning_suppress() - Suppress WARN*() backtraces for the duration
+ * of a block.
+ * @test: The test context object.
+ *
+ * Scoped form of the suppression API. Suppression starts when the block is
+ * entered and ends automatically when the block exits through any path. See
+ * the section comment above for the cleanup guarantees on each exit path.
+ * Fails the test if suppression is already active; nesting is not supported.
+ *
+ * The warning count can be checked inside the block via
+ * KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(). The handle is not accessible
+ * after the block exits.
+ *
+ * Example::
+ *
+ * kunit_warning_suppress(test) {
+ * trigger_warning();
+ * KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ * }
+ */
+#define kunit_warning_suppress(test) \
+ for (struct kunit_suppressed_warning *__kunit_suppress \
+ __cleanup(__kunit_suppress_auto_cleanup) = \
+ kunit_start_suppress_warning(test); \
+ __kunit_suppress; \
+ kunit_end_suppress_warning(test, __kunit_suppress), \
+ __kunit_suppress = NULL)
+
+/**
+ * KUNIT_START_SUPPRESSED_WARNING() - Begin suppressing WARN*() backtraces.
+ * @test: The test context object.
+ *
+ * Manual form of the suppression API. Must be paired with
+ * KUNIT_END_SUPPRESSED_WARNING() in the same scope. See the section comment
+ * above for cleanup guarantees. Fails the test if suppression is already
+ * active; nesting is not supported. Limited to one pair per scope; use
+ * sequential kunit_warning_suppress() blocks or the direct function API
+ * when more than one suppression region is needed.
+ *
+ * Example::
+ *
+ * KUNIT_START_SUPPRESSED_WARNING(test);
+ * trigger_code_that_should_warn_once();
+ * KUNIT_END_SUPPRESSED_WARNING(test);
+ * KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ */
+#define KUNIT_START_SUPPRESSED_WARNING(test) \
+ struct kunit_suppressed_warning *__kunit_suppress = \
+ kunit_start_suppress_warning(test)
+
+/**
+ * KUNIT_END_SUPPRESSED_WARNING() - End suppressing WARN*() backtraces.
+ * @test: The test context object.
+ *
+ * Deactivates suppression started by KUNIT_START_SUPPRESSED_WARNING().
+ * The warning count remains readable via KUNIT_SUPPRESSED_WARNING_COUNT()
+ * after this call.
+ */
+#define KUNIT_END_SUPPRESSED_WARNING(test) \
+ kunit_end_suppress_warning(test, __kunit_suppress)
+
+/**
+ * KUNIT_SUPPRESSED_WARNING_COUNT() - Returns the suppressed warning count.
+ *
+ * Returns the number of WARN*() calls suppressed since the current
+ * suppression block started, or 0 if the handle is NULL. Usable inside a
+ * kunit_warning_suppress() block or after KUNIT_END_SUPPRESSED_WARNING().
+ */
+#define KUNIT_SUPPRESSED_WARNING_COUNT() \
+ kunit_suppressed_warning_count(__kunit_suppress)
+
+/**
+ * KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT() - Sets an expectation that the
+ * suppressed warning count equals
+ * @expected.
+ * @test: The test context object.
+ * @expected: an expression that evaluates to the expected warning count.
+ *
+ * Sets an expectation that the number of suppressed WARN*() calls equals
+ * @expected. This is semantically equivalent to
+ * KUNIT_EXPECT_EQ(@test, KUNIT_SUPPRESSED_WARNING_COUNT(), @expected).
+ * See KUNIT_EXPECT_EQ() for more information.
+ */
+#define KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected) \
+ KUNIT_EXPECT_EQ(test, KUNIT_SUPPRESSED_WARNING_COUNT(), expected)
+
+/**
+ * KUNIT_ASSERT_SUPPRESSED_WARNING_COUNT() - Sets an assertion that the
+ * suppressed warning count equals
+ * @expected.
+ * @test: The test context object.
+ * @expected: an expression that evaluates to the expected warning count.
+ *
+ * Sets an assertion that the number of suppressed WARN*() calls equals
+ * @expected. This is the same as KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(),
+ * except it causes an assertion failure (see KUNIT_ASSERT_TRUE()) when the
+ * assertion is not met.
+ */
+#define KUNIT_ASSERT_SUPPRESSED_WARNING_COUNT(test, expected) \
+ KUNIT_ASSERT_EQ(test, KUNIT_SUPPRESSED_WARNING_COUNT(), expected)
+
#endif /* _KUNIT_TEST_H */
diff --git a/kernel/panic.c b/kernel/panic.c
index c78600212b6c1..697d8ca054bef 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -39,6 +39,7 @@
#include <linux/sys_info.h>
#include <trace/events/error_report.h>
#include <asm/sections.h>
+#include <kunit/test-bug.h>

#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18
@@ -1080,9 +1081,14 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
void warn_slowpath_fmt(const char *file, int line, unsigned taint,
const char *fmt, ...)
{
- bool rcu = warn_rcu_enter();
+ bool rcu;
struct warn_args args;

+ if (kunit_is_suppressed_warning(true))
+ return;
+
+ rcu = warn_rcu_enter();
+
pr_warn(CUT_HERE);

if (!fmt) {
@@ -1102,9 +1108,14 @@ EXPORT_SYMBOL(warn_slowpath_fmt);
#else
void __warn_printk(const char *fmt, ...)
{
- bool rcu = warn_rcu_enter();
+ bool rcu;
va_list args;

+ if (kunit_is_suppressed_warning(false))
+ return;
+
+ rcu = warn_rcu_enter();
+
pr_warn(CUT_HERE);

va_start(args, fmt);
diff --git a/lib/bug.c b/lib/bug.c
index 623c467a8b76c..a5cebde554ed8 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -48,6 +48,7 @@
#include <linux/rculist.h>
#include <linux/ftrace.h>
#include <linux/context_tracking.h>
+#include <kunit/test-bug.h>

extern struct bug_entry __start___bug_table[], __stop___bug_table[];

@@ -223,6 +224,15 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
no_cut = bug->flags & BUGFLAG_NO_CUT_HERE;
has_args = bug->flags & BUGFLAG_ARGS;

+#ifdef CONFIG_KUNIT
+ /*
+ * Before the once logic so suppressed warnings do not consume
+ * the single-fire budget of WARN_ON_ONCE().
+ */
+ if (warning && kunit_is_suppressed_warning(true))
+ return BUG_TRAP_TYPE_WARN;
+#endif
+
if (warning && once) {
if (done)
return BUG_TRAP_TYPE_WARN;
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 656f1fa35abcc..4592f9d0aa8dd 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -10,7 +10,8 @@ kunit-objs += test.o \
executor.o \
attributes.o \
device.o \
- platform.o
+ platform.o \
+ bug.o

ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
new file mode 100644
index 0000000000000..b0b6778d7399a
--- /dev/null
+++ b/lib/kunit/bug.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit helpers for backtrace suppression
+ *
+ * Copyright (C) 2025 Alessandro Carminati <acar...@redhat.com>
+ * Copyright (C) 2024 Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/resource.h>
+#include <linux/export.h>
+#include <linux/rculist.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+#include "hooks-impl.h"
+
+struct kunit_suppressed_warning {
+ struct list_head node;
+ struct task_struct *task;
+ struct kunit *test;
+ int counter;
+};
+
+static LIST_HEAD(suppressed_warnings);
+static DEFINE_SPINLOCK(suppressed_warnings_lock);
+
+static void kunit_suppress_warning_remove(struct kunit_suppressed_warning *w)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&suppressed_warnings_lock, flags);
+ list_del_rcu(&w->node);
+ spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
+ synchronize_rcu(); /* Wait for readers to finish */
+}
+
+KUNIT_DEFINE_ACTION_WRAPPER(kunit_suppress_warning_cleanup,
+ kunit_suppress_warning_remove,
+ struct kunit_suppressed_warning *);
+
+bool kunit_has_active_suppress_warning(void)
+{
+ return __kunit_is_suppressed_warning_impl(false);
+}
+EXPORT_SYMBOL_GPL(kunit_has_active_suppress_warning);
+
+struct kunit_suppressed_warning *
+kunit_start_suppress_warning(struct kunit *test)
+{
+ struct kunit_suppressed_warning *w;
+ unsigned long flags;
+ int ret;
+
+ if (kunit_has_active_suppress_warning()) {
+ KUNIT_FAIL(test, "Another suppression block is already active");
+ return NULL;
+ }
+
+ w = kunit_kzalloc(test, sizeof(*w), GFP_KERNEL);
+ if (!w)
+ return NULL;
+
+ w->task = current;
+ w->test = test;
+
+ spin_lock_irqsave(&suppressed_warnings_lock, flags);
+ list_add_rcu(&w->node, &suppressed_warnings);
+ spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
+
+ ret = kunit_add_action_or_reset(test,
+ kunit_suppress_warning_cleanup, w);
+ if (ret)
+ return NULL;
+
+ return w;
+}
+EXPORT_SYMBOL_GPL(kunit_start_suppress_warning);
+
+void kunit_end_suppress_warning(struct kunit *test,
+ struct kunit_suppressed_warning *w)
+{
+ if (!w)
+ return;
+ kunit_release_action(test, kunit_suppress_warning_cleanup, w);
+}
+EXPORT_SYMBOL_GPL(kunit_end_suppress_warning);
+
+void __kunit_suppress_auto_cleanup(struct kunit_suppressed_warning **wp)
+{
+ if (*wp)
+ kunit_end_suppress_warning((*wp)->test, *wp);
+}
+EXPORT_SYMBOL_GPL(__kunit_suppress_auto_cleanup);
+
+int kunit_suppressed_warning_count(struct kunit_suppressed_warning *w)
+{
+ return w ? w->counter : 0;
+}
+EXPORT_SYMBOL_GPL(kunit_suppressed_warning_count);
+
+bool __kunit_is_suppressed_warning_impl(bool count)
+{
+ struct kunit_suppressed_warning *w;
+
+ guard(rcu)();
+ list_for_each_entry_rcu(w, &suppressed_warnings, node) {
+ if (w->task == current) {
+ if (count)
+ w->counter++;
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/lib/kunit/hooks-impl.h b/lib/kunit/hooks-impl.h
index 4e71b2d0143ba..d8720f2616925 100644
--- a/lib/kunit/hooks-impl.h
+++ b/lib/kunit/hooks-impl.h
@@ -19,6 +19,7 @@ void __printf(3, 4) __kunit_fail_current_test_impl(const char *file,
int line,
const char *fmt, ...);
void *__kunit_get_static_stub_address_impl(struct kunit *test, void *real_fn_addr);
+bool __kunit_is_suppressed_warning_impl(bool count);

/* Code to set all of the function pointers. */
static inline void kunit_install_hooks(void)
@@ -26,6 +27,7 @@ static inline void kunit_install_hooks(void)
/* Install the KUnit hook functions. */
kunit_hooks.fail_current_test = __kunit_fail_current_test_impl;
kunit_hooks.get_static_stub_address = __kunit_get_static_stub_address_impl;
+ kunit_hooks.is_suppressed_warning = __kunit_is_suppressed_warning_impl;
}

#endif /* _KUNIT_HOOKS_IMPL_H */

--
2.53.0

Albert Esteve

unread,
May 4, 2026, 3:42:05 AMMay 4
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Alessandro Carminati, Albert Esteve, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Add unit tests to verify that warning backtrace suppression works.

Tests cover all three API forms:
- Scoped: kunit_warning_suppress() with in-block count verification
and post-block inactivity check.
- Manual macros: KUNIT_START/END_SUPPRESSED_WARNING() with WARN()
and WARN_ON(), both direct and through helper functions, as well
as multiple warnings in a single block.
- Direct functions: kunit_start/end_suppress_warning() with
sequential independent suppression blocks and per-block counts.

Furthermore, tests verify incremental warning counting, that
kunit_has_active_suppress_warning() transitions correctly around
suppression boundaries, and that suppression active in the test
kthread does not leak to a separate kthread.

If backtrace suppression does _not_ work, the unit tests will likely
trigger unsuppressed backtraces, which should actually help to get
the affected architectures / platforms fixed.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/Makefile | 1 +
lib/kunit/backtrace-suppression-test.c | 184 +++++++++++++++++++++++++++++++++
2 files changed, 185 insertions(+)

diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 4592f9d0aa8dd..2e8a6b71a2ab0 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -22,6 +22,7 @@ obj-$(if $(CONFIG_KUNIT),y) += hooks.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o

# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/backtrace-suppression-test.c b/lib/kunit/backtrace-suppression-test.c
new file mode 100644
index 0000000000000..0e6fb685d2cbb
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for suppressing warning tracebacks.
+ *
+ * Copyright (C) 2024, Guenter Roeck
+ * Author: Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/test.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+
+static void backtrace_suppression_test_warn_direct(struct kunit *test)
+{
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ /*
+ * Count must be checked inside the scope; the handle
+ * is not accessible after the block exits.
+ */
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static void trigger_backtrace_warn(void)
+{
+ WARN(1, "This backtrace should be suppressed");
+}
+
+static void backtrace_suppression_test_warn_indirect(struct kunit *test)
+{
+ KUNIT_START_SUPPRESSED_WARNING(test);
+ trigger_backtrace_warn();
+ KUNIT_END_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+}
+
+static void backtrace_suppression_test_warn_multi(struct kunit *test)
+{
+ KUNIT_START_SUPPRESSED_WARNING(test);
+ WARN(1, "This backtrace should be suppressed");
+ trigger_backtrace_warn();
+ KUNIT_END_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+}
+
+static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
+
+ KUNIT_START_SUPPRESSED_WARNING(test);
+ WARN_ON(1);
+ KUNIT_END_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+}
+
+static void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
+
+ KUNIT_START_SUPPRESSED_WARNING(test);
+ trigger_backtrace_warn_on();
+ KUNIT_END_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+}
+
+static void backtrace_suppression_test_count(struct kunit *test)
+{
+ KUNIT_START_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0);
+
+ WARN(1, "suppressed");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+
+ WARN(1, "suppressed again");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+
+ KUNIT_END_SUPPRESSED_WARNING(test);
+}
+
+static void backtrace_suppression_test_active_state(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ KUNIT_START_SUPPRESSED_WARNING(test);
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ KUNIT_END_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static void backtrace_suppression_test_multi_scope(struct kunit *test)
+{
+ struct kunit_suppressed_warning *sw1, *sw2;
+
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
+
+ sw1 = kunit_start_suppress_warning(test);
+ trigger_backtrace_warn_on();
+ WARN(1, "suppressed by sw1");
+ kunit_end_suppress_warning(test, sw1);
+
+ sw2 = kunit_start_suppress_warning(test);
+ WARN(1, "suppressed by sw2");
+ kunit_end_suppress_warning(test, sw2);
+
+ KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw1), 2);
+ KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(sw2), 1);
+}
+
+struct cross_kthread_data {
+ bool was_active;
+ struct completion done;
+};
+
+static int cross_kthread_fn(void *data)
+{
+ struct cross_kthread_data *d = data;
+
+ d->was_active = kunit_has_active_suppress_warning();
+ complete(&d->done);
+ return 0;
+}
+
+static void backtrace_suppression_test_cross_kthread(struct kunit *test)
+{
+ struct cross_kthread_data data;
+ struct task_struct *task;
+
+ init_completion(&data.done);
+
+ KUNIT_START_SUPPRESSED_WARNING(test);
+
+ task = kthread_run(cross_kthread_fn, &data, "kunit-cross-test");
+ KUNIT_ASSERT_FALSE(test, IS_ERR(task));
+ wait_for_completion(&data.done);
+
+ KUNIT_END_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_FALSE(test, data.was_active);
+}
+
+static struct kunit_case backtrace_suppression_test_cases[] = {
+ KUNIT_CASE(backtrace_suppression_test_warn_direct),
+ KUNIT_CASE(backtrace_suppression_test_warn_indirect),
+ KUNIT_CASE(backtrace_suppression_test_warn_multi),
+ KUNIT_CASE(backtrace_suppression_test_warn_on_direct),
+ KUNIT_CASE(backtrace_suppression_test_warn_on_indirect),
+ KUNIT_CASE(backtrace_suppression_test_count),
+ KUNIT_CASE(backtrace_suppression_test_active_state),
+ KUNIT_CASE(backtrace_suppression_test_multi_scope),
+ KUNIT_CASE(backtrace_suppression_test_cross_kthread),
+ {}
+};
+
+static struct kunit_suite backtrace_suppression_test_suite = {
+ .name = "backtrace-suppression-test",
+ .test_cases = backtrace_suppression_test_cases,
+};
+kunit_test_suites(&backtrace_suppression_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit test to verify warning backtrace suppression");

--
2.53.0

Albert Esteve

unread,
May 4, 2026, 3:42:14 AMMay 4
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Alessandro Carminati, Albert Esteve, Simona Vetter
From: Guenter Roeck <li...@roeck-us.net>

The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
intentionally trigger warning backtraces by providing bad parameters to
the tested functions. What is tested is the return value, not the existence
of a warning backtrace. Suppress the backtraces to avoid clogging the
kernel log and distraction from real problems.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Acked-by: Maíra Canal <mca...@igalia.com>
Cc: Maarten Lankhorst <maarten....@linux.intel.com>
Cc: David Airlie <air...@gmail.com>
Cc: Daniel Vetter <dan...@ffwll.ch>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Acked-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
drivers/gpu/drm/tests/drm_rect_test.c | 23 +++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c
index 17e1f34b76101..818e16e80c8f9 100644
--- a/drivers/gpu/drm/tests/drm_rect_test.c
+++ b/drivers/gpu/drm/tests/drm_rect_test.c
@@ -409,8 +409,16 @@ static void drm_test_rect_calc_hscale(struct kunit *test)
const struct drm_rect_scale_case *params = test->param_value;
int scaling_factor;

- scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * drm_rect_calc_hscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects all unit tests with an
+ * error code in expected_scaling_factor.
+ */
+ kunit_warning_suppress(test) {
+ scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
+ params->min_range,
+ params->max_range);
+ }

KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
@@ -420,8 +428,15 @@ static void drm_test_rect_calc_vscale(struct kunit *test)
const struct drm_rect_scale_case *params = test->param_value;
int scaling_factor;

- scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * drm_rect_calc_vscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects all unit tests with an
+ * error code in expected_scaling_factor.
+ */
+ kunit_warning_suppress(test) {
+ scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
+ params->min_range, params->max_range);
+ }

KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}

--
2.53.0

Albert Esteve

unread,
May 4, 2026, 3:42:23 AMMay 4
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Alessandro Carminati, Albert Esteve, Kees Cook, David Gow
From: Guenter Roeck <li...@roeck-us.net>

Document API functions for suppressing warning backtraces.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Reviewed-by: David Gow <davi...@google.com>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
Documentation/dev-tools/kunit/usage.rst | 63 ++++++++++++++++++++++++++++++++-
1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index ebd06f5ea4550..25724f7e72969 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -157,6 +157,67 @@ Alternatively, one can take full control over the error message by using
if (some_setup_function())
KUNIT_FAIL(test, "Failed to setup thing for testing");

+Suppressing warning backtraces
+------------------------------
+
+Some unit tests trigger warning backtraces either intentionally or as a side
+effect. Such backtraces are normally undesirable since they distract from
+the actual test and may result in the impression that there is a problem.
+
+Backtraces can be suppressed with **task-scoped suppression**: while
+suppression is active on the current task, the backtrace and stack dump from
+``WARN*()``, ``WARN_ON*()``, and related macros on that task are suppressed.
+Three API forms are available, in order of convenience.
+
+- Scoped suppression is the simplest form. Wrap the code that triggers
+ warnings in a ``kunit_warning_suppress()`` block:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {
+ kunit_warning_suppress(test) {
+ trigger_backtrace();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ }
+
+.. note::
+ The warning count must be checked inside the block; the suppression handle
+ is not accessible after the block exits.
+
+- Manual macros are useful when the suppressed region is large enough that
+ extra indentation is undesirable, or when the warning count needs to be
+ checked after suppression ends. ``KUNIT_START_SUPPRESSED_WARNING()`` must
+ appear before ``KUNIT_END_SUPPRESSED_WARNING()`` in the same scope.
+ Limited to one pair per scope.
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {
+ KUNIT_START_SUPPRESSED_WARNING(test);
+ trigger_backtrace();
+ KUNIT_END_SUPPRESSED_WARNING(test);
+
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+
+- Direct functions return an explicit handle pointer. Use them when the handle
+ needs to be retained or passed across helper functions:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {
+ struct kunit_suppressed_warning *w;
+
+ w = kunit_start_suppress_warning(test);
+ trigger_backtrace();
+ kunit_end_suppress_warning(test, w);
+
+ KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(w), 1);
+ }

Test Suites
~~~~~~~~~~~
@@ -1211,4 +1272,4 @@ For example:
dev_managed_string = devm_kstrdup(fake_device, "Hello, World!");

// Everything is cleaned up automatically when the test ends.
- }
\ No newline at end of file
+ }

--
2.53.0

Maxime Ripard

unread,
May 4, 2026, 6:04:04 AMMay 4
to Albert Esteve, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Alessandro Carminati, Simona Vetter
Hi,
For both I think we should add KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT calls, no?

Maxime
signature.asc

Albert Esteve

unread,
May 6, 2026, 4:37:54 AMMay 6
to Maxime Ripard, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Alessandro Carminati, Simona Vetter
Hi Maxime,

It may indeed add verification value by ensuring the call actually
generates the expected warning.

The original patch did not include count checks on the version I
inherited it from (i.e.,
https://lore.kernel.org/all/20250526132755.1...@redhat.com/),
so I kept the same approach while adapting to the API changes. That
said, the check is simple to add, so I could include it in the next
version.

BR,
Albert

>
> Maxime

David Gow

unread,
May 6, 2026, 5:39:22 AMMay 6
to Albert Esteve, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook
This looks pretty good to me, thanks.

Reviewed-by: David Gow <da...@davidgow.net>

It's maybe slightly over-the-top to now have three different ways of
enabling warning suppression: I'd probably personally get rid of
KUNIT_START/END_SUPPRESSED_WARNING() if we had to lose one. But if
there's a real reason to prefer keeping all three, it's not actually a
problem to do so.

Regardless, this series is looking pretty ready to me. Let me know if
you're planning a v9, otherwise we'll take this when you're ready.

Cheers,
-- David

David Gow

unread,
May 6, 2026, 5:40:23 AMMay 6
to Albert Esteve, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Alessandro Carminati, Kees Cook
Le 04/05/2026 à 3:41 PM, Albert Esteve a écrit :
Looks good to me, thanks!

This is still:

Reviewed-by: David Gow <da...@davidgow.net>

Cheers,
-- David

David Gow

unread,
May 6, 2026, 5:41:36 AMMay 6
to Albert Esteve, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Alessandro Carminati, Simona Vetter
Le 04/05/2026 à 3:41 PM, Albert Esteve a écrit :
> From: Guenter Roeck <li...@roeck-us.net>
>
> The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
> intentionally trigger warning backtraces by providing bad parameters to
> the tested functions. What is tested is the return value, not the existence
> of a warning backtrace. Suppress the backtraces to avoid clogging the
> kernel log and distraction from real problems.
>
> Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
> Acked-by: Dan Carpenter <dan.ca...@linaro.org>
> Acked-by: Maíra Canal <mca...@igalia.com>
> Cc: Maarten Lankhorst <maarten....@linux.intel.com>
> Cc: David Airlie <air...@gmail.com>
> Cc: Daniel Vetter <dan...@ffwll.ch>
> Signed-off-by: Guenter Roeck <li...@roeck-us.net>
> Signed-off-by: Alessandro Carminati <acar...@redhat.com>
> Acked-by: David Gow <da...@davidgow.net>
> Signed-off-by: Albert Esteve <aes...@redhat.com>
> ---

I'm happy with this either with or without the extra check for the
warning count.

Acked-by: David Gow <da...@davidgow.net>

We'll take this patch in the kunit tree along with the rest of the
series once everyone's happy.

Cheers,
-- David

David Gow

unread,
May 6, 2026, 5:42:40 AMMay 6
to Albert Esteve, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Alessandro Carminati, Kees Cook
Le 04/05/2026 à 3:41 PM, Albert Esteve a écrit :
> From: Guenter Roeck <li...@roeck-us.net>
>
> Document API functions for suppressing warning backtraces.
>
> Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
> Acked-by: Dan Carpenter <dan.ca...@linaro.org>
> Reviewed-by: Kees Cook <kees...@chromium.org>
> Signed-off-by: Guenter Roeck <li...@roeck-us.net>
> Reviewed-by: David Gow <davi...@google.com>
> Signed-off-by: Alessandro Carminati <acar...@redhat.com>
> Reviewed-by: David Gow <da...@davidgow.net>
> Signed-off-by: Albert Esteve <aes...@redhat.com>
> ---

This is great, thanks!

Reviewed-by: David Gow <da...@davidgow.net>

Cheers,
-- David

Maxime Ripard

unread,
May 6, 2026, 5:48:08 AMMay 6
to David Gow, Albert Esteve, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Alessandro Carminati, Simona Vetter
On Wed, May 06, 2026 at 05:38:50PM +0800, David Gow wrote:
> Le 04/05/2026 à 3:41 PM, Albert Esteve a écrit :
> > From: Guenter Roeck <li...@roeck-us.net>
> >
> > The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
> > intentionally trigger warning backtraces by providing bad parameters to
> > the tested functions. What is tested is the return value, not the existence
> > of a warning backtrace. Suppress the backtraces to avoid clogging the
> > kernel log and distraction from real problems.
> >
> > Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
> > Acked-by: Dan Carpenter <dan.ca...@linaro.org>
> > Acked-by: Maíra Canal <mca...@igalia.com>
> > Cc: Maarten Lankhorst <maarten....@linux.intel.com>
> > Cc: David Airlie <air...@gmail.com>
> > Cc: Daniel Vetter <dan...@ffwll.ch>
> > Signed-off-by: Guenter Roeck <li...@roeck-us.net>
> > Signed-off-by: Alessandro Carminati <acar...@redhat.com>
> > Acked-by: David Gow <da...@davidgow.net>
> > Signed-off-by: Albert Esteve <aes...@redhat.com>
> > ---
>
> I'm happy with this either with or without the extra check for the warning
> count.
>
> Acked-by: David Gow <da...@davidgow.net>
>
> We'll take this patch in the kunit tree along with the rest of the series
> once everyone's happy.

If there's no need for a new version, feel free to merge these patches,
I'm totally fine with it being addressed as a follow-up.

Maxime
signature.asc

Albert Esteve

unread,
May 6, 2026, 6:11:37 AMMay 6
to David Gow, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook
Thanks for the review!

I think the three forms earn their keep: the scoped form is the go-to
for most cases, but the macros avoid indentation without requiring
users to manage a raw pointer. I initially removed the macros and
added them back later. Direct calls to the functions will be less
frequent, used only when you need the handle.

That said, if it becomes a maintenance burden, the macros are the
easiest to drop since they're thin wrappers. Let me know if you prefer
them to be dropped, and I will send a v9 with that and the
`KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT` additions to the drm test
patch.

BR,
Albert.

Albert Esteve

unread,
May 8, 2026, 2:52:17 AMMay 8
to David Gow, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook
I have been thinking, and despite my last response arguing against it,
I think I will send that v9 and remove
KUNIT_START/END_SUPPRESSED_WARNING(). Since the scoped approach with a
reduced extent should be the default style for most cases, the direct
calls can cover any other use cases without needing macros. Plus,
since macros set the handler name for you, it seems odd not to be able
to use them more than once per test.

I hope that's ok. After that I think it should be ready (at least from my side).

BR,
Albert

David Gow

unread,
May 8, 2026, 6:10:51 AMMay 8
to Albert Esteve, Arnd Bergmann, Brendan Higgins, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook
Yeah, that was a part of my thought: all of the possible uses of
KUNIT_START/END_SUPPRESSED_WARNING() can be replaced with the scoped
version except for the one case where you want to check the number of
warnings specifically after the scope. I'm not sure the latter would be
common, and in the few places it's useful, the direct calls are not
_that_ much more difficult to use.

> I hope that's ok. After that I think it should be ready (at least from my side).
>
Sounds good: assuming no last-minute disasters, we'll take v9 then.

Cheers,
-- David

Albert Esteve

unread,
May 8, 2026, 11:03:14 AMMay 8
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

One option to address the problem would be to add messages such as
"expected warning backtraces start/end here" to the kernel log.
However, that would again require filter scripts, might result in
missing real problematic warning backtraces triggered while the test
is running, and the irrelevant backtrace(s) would still clog the
kernel log.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code.
Since each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it, without requiring any
architecture-specific code.

Overview:
Patch#1 Introduces the suppression infrastructure integrated into
KUnit's hook mechanism.
Patch#2 Adds selftests to validate the functionality.
Patch#3 Demonstrates real-world usage in the DRM subsystem.
Patch#4 Documents the new API and usage guidelines.

Design Notes:
Suppression is integrated into the existing KUnit hooks infrastructure,
reusing the kunit_running static branch for zero overhead
when no tests are running. The implementation lives entirely in the
kunit module; only a static-inline wrapper and a function pointer
slot are added to built-in code.

Suppression is checked at three points in the warning path:
- In `warn_slowpath_fmt()` (kernel/panic.c), for architectures without
__WARN_FLAGS. The check runs before any output, fully suppressing
both message and backtrace.
- In `__warn_printk()` (kernel/panic.c), for architectures that define
__WARN_FLAGS but not their own __WARN_printf (arm64, loongarch,
parisc, powerpc, riscv, sh). The check suppresses the warning message
text that is printed before the trap enters __report_bug().
- In `__report_bug()` (lib/bug.c), for architectures that define
__WARN_FLAGS. The check runs before `__warn()` is called, suppressing
the backtrace and stack dump.

To avoid double-counting on architectures where both `__warn_printk()`
and `__report_bug()` run for the same warning, the hook takes a bool
parameter: true to increment the suppression counter, false to suppress
without counting.

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via `kunit_add_action()`, ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Two API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped blocks with automatic
cleanup. The suppression handle is not accessible outside the block,
so warning counts (if needed) must be checked inside. Multiple
suppression blocks are allowed.
Changes since v8:
- Rebased to v7.1-rc2
- Remove KUNIT_START/END_SUPPRESSED_WARNING() macros
- Add KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT checks to drm tests
- Link to v8: https://lore.kernel.org/r/20260504-kunit_add_sup...@redhat.com

--
2.34.1

---
Alessandro Carminati (1):
bug/kunit: Core support for suppressing warning backtraces

Guenter Roeck (3):
kunit: Add backtrace suppression self-tests
drm: Suppress intentional warning backtraces in scaling unit tests
kunit: Add documentation for warning backtrace suppression API

Documentation/dev-tools/kunit/usage.rst | 46 ++++++++-
drivers/gpu/drm/tests/drm_rect_test.c | 27 ++++-
include/kunit/test-bug.h | 25 +++++
include/kunit/test.h | 98 ++++++++++++++++++
kernel/panic.c | 15 ++-
lib/bug.c | 10 ++
lib/kunit/Makefile | 4 +-
lib/kunit/backtrace-suppression-test.c | 176 ++++++++++++++++++++++++++++++++
lib/kunit/bug.c | 115 +++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
10 files changed, 510 insertions(+), 8 deletions(-)
---
base-commit: 74fe02ce122a6103f207d29fafc8b3a53de6abaf

Albert Esteve

unread,
May 8, 2026, 11:03:23 AMMay 8
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve
From: Alessandro Carminati <acar...@redhat.com>

Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code. Since
each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it. Limit changes to generic code
to the absolute minimum.

Implementation details:
Suppression is integrated into the existing KUnit hooks infrastructure
in test-bug.h, reusing the kunit_running static branch for zero
overhead when no tests are running.

Suppression is checked at three points in the warning path:
- In warn_slowpath_fmt(), the check runs before any output, fully
suppressing both message and backtrace. This covers architectures
without __WARN_FLAGS.
- In __warn_printk(), the check suppresses the warning message text.
This covers architectures that define __WARN_FLAGS but not their own
__WARN_printf (arm64, loongarch, parisc, powerpc, riscv, sh), where
the message is printed before the trap enters __report_bug().
- In __report_bug(), the check runs before __warn() is called,
suppressing the backtrace and stack dump.

To avoid double-counting on architectures where both __warn_printk()
and __report_bug() run for the same warning, kunit_is_suppressed_warning()
takes a bool parameter: true to increment the suppression counter
(used in warn_slowpath_fmt and __report_bug), false to check only
(used in __warn_printk).

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via kunit_add_action(), ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Two API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped, uses __cleanup for
automatic teardown on scope exit, kunit_add_action() as safety net
for abnormal exits (e.g. kthread_exit from failed assertions).
Suppression handle is only accessible inside the block.
- kunit_start/end_suppress_warning(test): direct functions returning
an explicit handle, for retaining the handle within the test,
or for cross-function usage.

Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: Kees Cook <ke...@kernel.org>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
include/kunit/test-bug.h | 25 +++++++++++
include/kunit/test.h | 98 ++++++++++++++++++++++++++++++++++++++++
kernel/panic.c | 15 ++++++-
lib/bug.c | 10 +++++
lib/kunit/Makefile | 3 +-
lib/kunit/bug.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
7 files changed, 265 insertions(+), 3 deletions(-)
index 9cd1594ab697d..be71612f61655 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -1795,4 +1795,102 @@ do { \
// include resource.h themselves if they need it.
#include <kunit/resource.h>

+/*
+ * Warning backtrace suppression API.
+ *
+ * Suppresses WARN*() backtraces on the current task while active. Two forms
+ * are provided:
+ *
+ * - Scoped: kunit_warning_suppress(test) { ... }
+ * Suppression is active for the duration of the block. On normal exit,
+ * the for-loop increment deactivates suppression. On early exit (break,
+ * return, goto), the __cleanup attribute fires. On kthread_exit() (e.g.,
+ * a failed KUnit assertion), kunit_add_action() cleans up at test
+ * teardown. The suppression handle is only accessible inside the block,
+ * so warning counts must be checked before the block exits.
+ *
+ * KUNIT_SUPPRESSED_WARNING_COUNT() - Returns the suppressed warning count.
+ *
+ * Returns the number of WARN*() calls suppressed since the current
+ * suppression block started, or 0 if the handle is NULL. Usable inside a
+ * kunit_warning_suppress() block.
index 20feada5319d4..41c530d60efbf 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -39,6 +39,7 @@
#include <linux/sys_info.h>
#include <trace/events/error_report.h>
#include <asm/sections.h>
+#include <kunit/test-bug.h>

#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18
@@ -1121,9 +1122,14 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
void warn_slowpath_fmt(const char *file, int line, unsigned taint,
const char *fmt, ...)
{
- bool rcu = warn_rcu_enter();
+ bool rcu;
struct warn_args args;

+ if (kunit_is_suppressed_warning(true))
+ return;
+
+ rcu = warn_rcu_enter();
+
pr_warn(CUT_HERE);

if (!fmt) {
@@ -1143,9 +1149,14 @@ EXPORT_SYMBOL(warn_slowpath_fmt);
#else
void __warn_printk(const char *fmt, ...)
{
- bool rcu = warn_rcu_enter();
+ bool rcu;
va_list args;

+ if (kunit_is_suppressed_warning(false))
+ return;
+
+ rcu = warn_rcu_enter();
+
pr_warn(CUT_HERE);

va_start(args, fmt);
diff --git a/lib/bug.c b/lib/bug.c
index 224f4cfa4aa31..255530284502a 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -48,6 +48,7 @@
#include <linux/rculist.h>
#include <linux/ftrace.h>
#include <linux/context_tracking.h>
+#include <kunit/test-bug.h>

extern struct bug_entry __start___bug_table[], __stop___bug_table[];

@@ -220,6 +221,15 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
--
2.53.0

Albert Esteve

unread,
May 8, 2026, 11:03:29 AMMay 8
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Add unit tests to verify that warning backtrace suppression works.

Tests cover both API forms:
- Scoped: kunit_warning_suppress() with in-block count verification
and post-block inactivity check.
- Direct functions: kunit_start/end_suppress_warning() with
sequential independent suppression blocks and per-block counts.

Furthermore, tests verify incremental warning counting, that
kunit_has_active_suppress_warning() transitions correctly around
suppression boundaries, and that suppression active in the test
kthread does not leak to a separate kthread.

If backtrace suppression does _not_ work, the unit tests will likely
trigger unsuppressed backtraces, which should actually help to get
the affected architectures / platforms fixed.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/Makefile | 1 +
lib/kunit/backtrace-suppression-test.c | 176 +++++++++++++++++++++++++++++++++
2 files changed, 177 insertions(+)

diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 4592f9d0aa8dd..2e8a6b71a2ab0 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -22,6 +22,7 @@ obj-$(if $(CONFIG_KUNIT),y) += hooks.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o

# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/backtrace-suppression-test.c b/lib/kunit/backtrace-suppression-test.c
new file mode 100644
index 0000000000000..3f0b2ae83312f
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for suppressing warning tracebacks.
+ *
+ * Copyright (C) 2024, Guenter Roeck
+ * Author: Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/test.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+
+static void backtrace_suppression_test_warn_direct(struct kunit *test)
+{
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ /*
+ * Count must be checked inside the scope; the handle
+ * is not accessible after the block exits.
+ */
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static void trigger_backtrace_warn(void)
+{
+ WARN(1, "This backtrace should be suppressed");
+}
+
+static void backtrace_suppression_test_warn_indirect(struct kunit *test)
+{
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_warn_multi(struct kunit *test)
+{
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
+
+ kunit_warning_suppress(test) {
+ WARN_ON(1);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
+
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn_on();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_count(struct kunit *test)
+{
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0);
+
+ WARN(1, "suppressed");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+
+ WARN(1, "suppressed again");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_active_state(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static void backtrace_suppression_test_multi_scope(struct kunit *test)
+{
+static void backtrace_suppression_test_cross_kthread(struct kunit *test)
+{
+ struct cross_kthread_data data;
+ struct task_struct *task;
+
+ init_completion(&data.done);
+
+ kunit_warning_suppress(test) {
+ task = kthread_run(cross_kthread_fn, &data, "kunit-cross-test");
+ KUNIT_ASSERT_FALSE(test, IS_ERR(task));
+ wait_for_completion(&data.done);
+ }
+
+ KUNIT_EXPECT_FALSE(test, data.was_active);
+}
+
+static struct kunit_case backtrace_suppression_test_cases[] = {
+ KUNIT_CASE(backtrace_suppression_test_warn_direct),
+ KUNIT_CASE(backtrace_suppression_test_warn_indirect),
+ KUNIT_CASE(backtrace_suppression_test_warn_multi),
+ KUNIT_CASE(backtrace_suppression_test_warn_on_direct),
+ KUNIT_CASE(backtrace_suppression_test_warn_on_indirect),
+ KUNIT_CASE(backtrace_suppression_test_count),
+ KUNIT_CASE(backtrace_suppression_test_active_state),
+ KUNIT_CASE(backtrace_suppression_test_multi_scope),
+ KUNIT_CASE(backtrace_suppression_test_cross_kthread),
+ {}
+};
+
+static struct kunit_suite backtrace_suppression_test_suite = {
+ .name = "backtrace-suppression-test",
+ .test_cases = backtrace_suppression_test_cases,
+};
+kunit_test_suites(&backtrace_suppression_test_suite);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("KUnit test to verify warning backtrace suppression");

--
2.53.0

Albert Esteve

unread,
May 8, 2026, 11:03:42 AMMay 8
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Maíra Canal, Alessandro Carminati, Albert Esteve, Dan Carpenter, Simona Vetter
From: Guenter Roeck <li...@roeck-us.net>

The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
intentionally trigger warning backtraces by providing bad parameters to
the tested functions. What is tested is the return value, not the existence
of a warning backtrace. Suppress the backtraces to avoid clogging the
kernel log and distraction from real problems. Additionally, the
suppression API allows to actually ensure a warning was triggered,
without parsing any kernel logs and keeping them clean.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Acked-by: Maíra Canal <mca...@igalia.com>
Cc: Maarten Lankhorst <maarten....@linux.intel.com>
Cc: David Airlie <air...@gmail.com>
Cc: Daniel Vetter <dan...@ffwll.ch>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Acked-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
drivers/gpu/drm/tests/drm_rect_test.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c
index 17e1f34b76101..809e73cb74498 100644
--- a/drivers/gpu/drm/tests/drm_rect_test.c
+++ b/drivers/gpu/drm/tests/drm_rect_test.c
@@ -407,10 +407,20 @@ KUNIT_ARRAY_PARAM(drm_rect_scale, drm_rect_scale_cases, drm_rect_scale_case_desc
static void drm_test_rect_calc_hscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
+ int expected_warnings = params->expected_scaling_factor == -EINVAL;
int scaling_factor;

- scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * drm_rect_calc_hscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects unit tests with -EINVAL
+ * error code in expected_scaling_factor.
+ */
+ kunit_warning_suppress(test) {
+ scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
+ params->min_range,
+ params->max_range);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings);
+ }

KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
@@ -418,10 +428,19 @@ static void drm_test_rect_calc_hscale(struct kunit *test)
static void drm_test_rect_calc_vscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
+ int expected_warnings = params->expected_scaling_factor == -EINVAL;
int scaling_factor;

- scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * drm_rect_calc_vscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects unit tests with -EINVAL
+ * error code in expected_scaling_factor.
+ */
+ kunit_warning_suppress(test) {
+ scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
+ params->min_range, params->max_range);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings);
+ }

KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}

--
2.53.0

Albert Esteve

unread,
May 8, 2026, 11:03:48 AMMay 8
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Document API functions for suppressing warning backtraces.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
Documentation/dev-tools/kunit/usage.rst | 46 ++++++++++++++++++++++++++++++++-
1 file changed, 45 insertions(+), 1 deletion(-)

diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index ebd06f5ea4550..1c78dfff94e8a 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -157,6 +157,50 @@ Alternatively, one can take full control over the error message by using
if (some_setup_function())
KUNIT_FAIL(test, "Failed to setup thing for testing");

+Suppressing warning backtraces
+------------------------------
+
+Some unit tests trigger warning backtraces either intentionally or as a side
+effect. Such backtraces are normally undesirable since they distract from
+the actual test and may result in the impression that there is a problem.
+
+Backtraces can be suppressed with **task-scoped suppression**: while
+suppression is active on the current task, the backtrace and stack dump from
+``WARN*()``, ``WARN_ON*()``, and related macros on that task are suppressed.
+Two API forms are available.
+
+- Scoped suppression is the simplest form. Wrap the code that triggers
+ warnings in a ``kunit_warning_suppress()`` block:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {
+ kunit_warning_suppress(test) {
+ trigger_backtrace();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ }
+
+.. note::
+ The warning count must be checked inside the block; the suppression handle
+ is not accessible after the block exits.
+
+- Direct functions return an explicit handle pointer. Use them when the handle
+ needs to be retained or passed across helper functions:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {
+ struct kunit_suppressed_warning *w;
+
+ w = kunit_start_suppress_warning(test);
+ trigger_backtrace();
+ kunit_end_suppress_warning(test, w);
+
+ KUNIT_EXPECT_EQ(test, kunit_suppressed_warning_count(w), 1);
+ }

Test Suites
~~~~~~~~~~~
@@ -1211,4 +1255,4 @@ For example:
dev_managed_string = devm_kstrdup(fake_device, "Hello, World!");

// Everything is cleaned up automatically when the test ends.
- }
\ No newline at end of file
+ }

--
2.53.0

Andrew Morton

unread,
May 8, 2026, 7:52:07 PMMay 8
to Albert Esteve, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
On Fri, 08 May 2026 17:02:44 +0200 Albert Esteve <aes...@redhat.com> wrote:

> Some unit tests intentionally trigger warning backtraces by passing bad
> parameters to kernel API functions. Such unit tests typically check the
> return value from such calls, not the existence of the warning backtrace.
>
> ...
>
> Solve the problem by providing a means to suppress warning backtraces
> originating from the current kthread while executing test code.
> Since each KUnit test runs in its own kthread, this effectively scopes
> suppression to the test that enabled it, without requiring any
> architecture-specific code.

Thanks. AI review has a bunch of questions:
https://sashiko.dev/#/patchset/20260508-kunit_add_sup...@redhat.com

Albert Esteve

unread,
May 12, 2026, 11:05:38 AM (13 days ago) May 12
to Andrew Morton, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
Hi! Most look like valid concerns/issues. I will send a new version
addressing them then, and monitor sashiko to ensure I clear
everything.

Thanks for the link.

BR,
Albert.

kernel test robot

unread,
May 13, 2026, 2:12:49 AM (12 days ago) May 13
to Albert Esteve, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, oe-kbu...@lists.linux.dev, Linux Memory Management List, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Dan Carpenter, Alessandro Carminati
Hi Alessandro,

kernel test robot noticed the following build errors:

[auto build test ERROR on 80234b5ab240f52fa45d201e899e207b9265ef91]

url: https://github.com/intel-lab-lkp/linux/commits/Albert-Esteve/bug-kunit-Core-support-for-suppressing-warning-backtraces/20260513-043807
base: 80234b5ab240f52fa45d201e899e207b9265ef91
patch link: https://lore.kernel.org/r/20260504-kunit_add_support-v8-4-3e5957cdd235%40redhat.com
patch subject: [PATCH v8 4/4] kunit: Add documentation for warning backtrace suppression API
config: x86_64-rhel-9.4-ltp (https://download.01.org/0day-ci/archive/20260513/202605130826...@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260513/202605130826...@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <l...@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202605130826...@intel.com/

All errors (new ones prefixed by >>):

In file included from drivers/gpu/drm/drm_buddy.c:6:
>> include/kunit/test-bug.h:90:15: error: unknown type name 'bool'
90 | static inline bool kunit_is_suppressed_warning(bool count) { return false; }
| ^~~~
include/kunit/test-bug.h:13:1: note: 'bool' is defined in header '<stdbool.h>'; this is probably fixable by adding '#include <stdbool.h>'
12 | #include <linux/stddef.h> /* for NULL */
+++ |+#include <stdbool.h>
13 |
include/kunit/test-bug.h:90:48: error: unknown type name 'bool'
90 | static inline bool kunit_is_suppressed_warning(bool count) { return false; }
| ^~~~
include/kunit/test-bug.h:90:48: note: 'bool' is defined in header '<stdbool.h>'; this is probably fixable by adding '#include <stdbool.h>'


vim +/bool +90 include/kunit/test-bug.h

88
89 static inline struct kunit *kunit_get_current_test(void) { return NULL; }
> 90 static inline bool kunit_is_suppressed_warning(bool count) { return false; }
91

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

Albert Esteve

unread,
May 13, 2026, 3:31:21 AM (12 days ago) May 13
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

One option to address the problem would be to add messages such as
"expected warning backtraces start/end here" to the kernel log.
However, that would again require filter scripts, might result in
missing real problematic warning backtraces triggered while the test
is running, and the irrelevant backtrace(s) would still clog the
kernel log.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code.
Since each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it, without requiring any
architecture-specific code.

Overview:
Patch#1 Introduces the suppression infrastructure integrated into
KUnit's hook mechanism.
Patch#2 Adds selftests to validate the functionality.
Patch#3 Demonstrates real-world usage in the DRM subsystem.
Patch#4 Documents the new API and usage guidelines.

Design Notes:
Suppression is integrated into the existing KUnit hooks infrastructure,
reusing the kunit_running static branch for zero overhead
when no tests are running. The implementation lives entirely in the
kunit module; only a static-inline wrapper and a function pointer
slot are added to built-in code.

Suppression is checked at three points in the warning path:
- In `warn_slowpath_fmt()` (kernel/panic.c), for architectures without
__WARN_FLAGS. The check runs before any output, fully suppressing
both message and backtrace.
- In `__warn_printk()` (kernel/panic.c), for architectures that define
__WARN_FLAGS but not their own __WARN_printf (arm64, loongarch,
parisc, powerpc, riscv, sh). The check suppresses the warning message
text that is printed before the trap enters __report_bug().
- In `__report_bug()` (lib/bug.c), for architectures that define
__WARN_FLAGS. The check runs before `__warn()` is called, suppressing
the backtrace and stack dump.

To avoid double-counting on architectures where both `__warn_printk()`
and `__report_bug()` run for the same warning, the hook takes a bool
parameter: true to increment the suppression counter, false to suppress
without counting.

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via `kunit_add_action()`, ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Two API forms are provided:
Changes since v9:
- Fix silent false-pass when kunit_start_suppress_warning() returns NULL
- Fix RCU lockdep splat for kunit_is_suppressed_warning() calls
- Move disable_trace_on_warning() in __report_bug()
- Make suppress counter atomic
- Mark helper warn functions in selftest as noinline
- Add kunit_skip() for CONFIG_BUG=n in selftests
- Fix potentially uninitialized data.was_active in kthread seltest
- Add kthread_stop() in kthread selftest early exit
- Initialize scaling_factor to INT_MIN in DRM scaling tests
- Add include for bool in test-bug.h to fix CONFIG_KUNIT=n case
- Link to v9: https://lore.kernel.org/r/20260508-kunit_add_sup...@redhat.com

--
2.34.1

---
To: Brendan Higgins <brendan...@linux.dev>
To: David Gow <da...@davidgow.net>
To: Rae Moar <raem...@gmail.com>
To: Andrew Morton <ak...@linux-foundation.org>
To: Paul Walmsley <p...@kernel.org>
To: Palmer Dabbelt <pal...@dabbelt.com>
To: Albert Ou <a...@eecs.berkeley.edu>
To: Alexandre Ghiti <al...@ghiti.fr>
To: Maarten Lankhorst <maarten....@linux.intel.com>
To: Maxime Ripard <mri...@kernel.org>
To: Thomas Zimmermann <tzimm...@suse.de>
To: David Airlie <air...@gmail.com>
To: Simona Vetter <sim...@ffwll.ch>
To: Jonathan Corbet <cor...@lwn.net>
To: Shuah Khan <sk...@linuxfoundation.org>
Cc: linux-...@vger.kernel.org
Cc: linux-k...@vger.kernel.org
Cc: kuni...@googlegroups.com
Cc: linux...@lists.infradead.org
Cc: dri-...@lists.freedesktop.org
Cc: work...@vger.kernel.org
Cc: linu...@vger.kernel.org

---
Alessandro Carminati (1):
bug/kunit: Core support for suppressing warning backtraces

Guenter Roeck (3):
kunit: Add backtrace suppression self-tests
drm: Suppress intentional warning backtraces in scaling unit tests
kunit: Add documentation for warning backtrace suppression API

Documentation/dev-tools/kunit/usage.rst | 46 +++++++-
drivers/gpu/drm/tests/drm_rect_test.c | 32 +++++-
include/kunit/test-bug.h | 26 +++++
include/kunit/test.h | 98 ++++++++++++++++
kernel/panic.c | 11 ++
lib/bug.c | 14 ++-
lib/kunit/Makefile | 4 +-
lib/kunit/backtrace-suppression-test.c | 196 ++++++++++++++++++++++++++++++++
lib/kunit/bug.c | 119 +++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
10 files changed, 538 insertions(+), 10 deletions(-)

Albert Esteve

unread,
May 13, 2026, 3:31:28 AM (12 days ago) May 13
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve
From: Alessandro Carminati <acar...@redhat.com>

Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code. Since
each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it. Limit changes to generic code
to the absolute minimum.

Implementation details:
Suppression is integrated into the existing KUnit hooks infrastructure
in test-bug.h, reusing the kunit_running static branch for zero
overhead when no tests are running.

Suppression is checked at three points in the warning path:
- In warn_slowpath_fmt(), the check runs before any output, fully
suppressing both message and backtrace. This covers architectures
without __WARN_FLAGS.
- In __warn_printk(), the check suppresses the warning message text.
This covers architectures that define __WARN_FLAGS but not their own
__WARN_printf (arm64, loongarch, parisc, powerpc, riscv, sh), where
the message is printed before the trap enters __report_bug().
- In __report_bug(), the check runs before __warn() is called,
suppressing the backtrace and stack dump.

To avoid double-counting on architectures where both __warn_printk()
and __report_bug() run for the same warning, kunit_is_suppressed_warning()
takes a bool parameter: true to increment the suppression counter
(used in warn_slowpath_fmt and __report_bug), false to check only
(used in __warn_printk).

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via kunit_add_action(), ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Two API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped, uses __cleanup for
automatic teardown on scope exit, kunit_add_action() as safety net
for abnormal exits (e.g. kthread_exit from failed assertions).
Suppression handle is only accessible inside the block.
- kunit_start/end_suppress_warning(test): direct functions returning
an explicit handle, for retaining the handle within the test,
or for cross-function usage.

Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: Kees Cook <ke...@kernel.org>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
include/kunit/test-bug.h | 26 +++++++++++
include/kunit/test.h | 98 ++++++++++++++++++++++++++++++++++++++
kernel/panic.c | 11 +++++
lib/bug.c | 14 +++++-
lib/kunit/Makefile | 3 +-
lib/kunit/bug.c | 119 +++++++++++++++++++++++++++++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
7 files changed, 270 insertions(+), 3 deletions(-)

diff --git a/include/kunit/test-bug.h b/include/kunit/test-bug.h
index 47aa8f21ccce8..99869029fc686 100644
--- a/include/kunit/test-bug.h
+++ b/include/kunit/test-bug.h
@@ -10,6 +10,7 @@
#define _KUNIT_TEST_BUG_H

#include <linux/stddef.h> /* for NULL */
+#include <linux/types.h> /* for bool */

#if IS_ENABLED(CONFIG_KUNIT)

@@ -23,6 +24,7 @@ DECLARE_STATIC_KEY_FALSE(kunit_running);
extern struct kunit_hooks_table {
__printf(3, 4) void (*fail_current_test)(const char*, int, const char*, ...);
void *(*get_static_stub_address)(struct kunit *test, void *real_fn_addr);
+ bool (*is_suppressed_warning)(bool count);
} kunit_hooks;

/**
@@ -60,9 +62,33 @@ static inline struct kunit *kunit_get_current_test(void)
static inline struct kunit *kunit_get_current_test(void) { return NULL; }
index 20feada5319d4..213725b612aa1 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -39,6 +39,7 @@
#include <linux/sys_info.h>
#include <trace/events/error_report.h>
#include <asm/sections.h>
+#include <kunit/test-bug.h>

#define PANIC_TIMER_STEP 100
#define PANIC_BLINK_SPD 18
@@ -1124,6 +1125,11 @@ void warn_slowpath_fmt(const char *file, int line, unsigned taint,
bool rcu = warn_rcu_enter();
struct warn_args args;

+ if (kunit_is_suppressed_warning(true)) {
+ warn_rcu_exit(rcu);
+ return;
+ }
+
pr_warn(CUT_HERE);

if (!fmt) {
@@ -1146,6 +1152,11 @@ void __warn_printk(const char *fmt, ...)
bool rcu = warn_rcu_enter();
va_list args;

+ if (kunit_is_suppressed_warning(false)) {
+ warn_rcu_exit(rcu);
+ return;
+ }
+
pr_warn(CUT_HERE);

va_start(args, fmt);
diff --git a/lib/bug.c b/lib/bug.c
index 224f4cfa4aa31..d99e369bc1103 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -48,6 +48,7 @@
#include <linux/rculist.h>
#include <linux/ftrace.h>
#include <linux/context_tracking.h>
+#include <kunit/test-bug.h>

extern struct bug_entry __start___bug_table[], __stop___bug_table[];

@@ -209,8 +210,6 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
return BUG_TRAP_TYPE_NONE;
}

- disable_trace_on_warning();
-
bug_get_file_line(bug, &file, &line);
fmt = bug_get_format(bug);

@@ -220,6 +219,17 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
no_cut = bug->flags & BUGFLAG_NO_CUT_HERE;
has_args = bug->flags & BUGFLAG_ARGS;

+#ifdef CONFIG_KUNIT
+ /*
+ * Before the once logic so suppressed warnings do not consume
+ * the single-fire budget of WARN_ON_ONCE().
+ */
+ if (warning && kunit_is_suppressed_warning(true))
+ return BUG_TRAP_TYPE_WARN;
+#endif
+
+ disable_trace_on_warning();
+
if (warning && once) {
if (done)
return BUG_TRAP_TYPE_WARN;
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 656f1fa35abcc..4592f9d0aa8dd 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -10,7 +10,8 @@ kunit-objs += test.o \
executor.o \
attributes.o \
device.o \
- platform.o
+ platform.o \
+ bug.o

ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
new file mode 100644
index 0000000000000..cdfcbfe80b5df
--- /dev/null
+++ b/lib/kunit/bug.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit helpers for backtrace suppression
+ *
+ * Copyright (C) 2025 Alessandro Carminati <acar...@redhat.com>
+ * Copyright (C) 2024 Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/resource.h>
+#include <linux/export.h>
+#include <linux/rculist.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+#include "hooks-impl.h"
+
+struct kunit_suppressed_warning {
+ struct list_head node;
+ struct task_struct *task;
+ struct kunit *test;
+ atomic_t counter;
+kunit_start_suppress_warning(struct kunit *test)
+{
+ struct kunit_suppressed_warning *w;
+ unsigned long flags;
+ int ret;
+
+ if (kunit_has_active_suppress_warning()) {
+ KUNIT_FAIL(test, "Another suppression block is already active");
+ return NULL;
+ }
+
+ w = kunit_kzalloc(test, sizeof(*w), GFP_KERNEL);
+ if (!w) {
+ KUNIT_FAIL(test, "Failed to allocate suppression handle.");
+ return NULL;
+ }
+
+ w->task = current;
+ w->test = test;
+
+ spin_lock_irqsave(&suppressed_warnings_lock, flags);
+ list_add_rcu(&w->node, &suppressed_warnings);
+ spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
+
+ ret = kunit_add_action_or_reset(test,
+ kunit_suppress_warning_cleanup, w);
+ if (ret) {
+ KUNIT_FAIL(test, "Failed to add suppression cleanup action.");
+ return NULL;
+ }
+
+ return w;
+}
+EXPORT_SYMBOL_GPL(kunit_start_suppress_warning);
+
+void kunit_end_suppress_warning(struct kunit *test,
+ struct kunit_suppressed_warning *w)
+{
+ if (!w)
+ return;
+ kunit_release_action(test, kunit_suppress_warning_cleanup, w);
+}
+EXPORT_SYMBOL_GPL(kunit_end_suppress_warning);
+
+void __kunit_suppress_auto_cleanup(struct kunit_suppressed_warning **wp)
+{
+ if (*wp)
+ kunit_end_suppress_warning((*wp)->test, *wp);
+}
+EXPORT_SYMBOL_GPL(__kunit_suppress_auto_cleanup);
+
+int kunit_suppressed_warning_count(struct kunit_suppressed_warning *w)
+{
+ return w ? atomic_read(&w->counter) : 0;
+}
+EXPORT_SYMBOL_GPL(kunit_suppressed_warning_count);
+
+bool __kunit_is_suppressed_warning_impl(bool count)
+{
+ struct kunit_suppressed_warning *w;
+
+ guard(rcu)();
+ list_for_each_entry_rcu(w, &suppressed_warnings, node) {
+ if (w->task == current) {
+ if (count)
+ atomic_inc(&w->counter);

Albert Esteve

unread,
May 13, 2026, 3:31:35 AM (12 days ago) May 13
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Add unit tests to verify that warning backtrace suppression works.

Tests cover both API forms:
- Scoped: kunit_warning_suppress() with in-block count verification
and post-block inactivity check.
- Direct functions: kunit_start/end_suppress_warning() with
sequential independent suppression blocks and per-block counts.

Furthermore, tests verify incremental warning counting, that
kunit_has_active_suppress_warning() transitions correctly around
suppression boundaries, and that suppression active in the test
kthread does not leak to a separate kthread.

If backtrace suppression does _not_ work, the unit tests will likely
trigger unsuppressed backtraces, which should actually help to get
the affected architectures / platforms fixed.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/Makefile | 1 +
lib/kunit/backtrace-suppression-test.c | 196 +++++++++++++++++++++++++++++++++
2 files changed, 197 insertions(+)

diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 4592f9d0aa8dd..2e8a6b71a2ab0 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -22,6 +22,7 @@ obj-$(if $(CONFIG_KUNIT),y) += hooks.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o

# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/backtrace-suppression-test.c b/lib/kunit/backtrace-suppression-test.c
new file mode 100644
index 0000000000000..831a60f3521fa
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for suppressing warning tracebacks.
+ *
+ * Copyright (C) 2024, Guenter Roeck
+ * Author: Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/test.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+
+static void backtrace_suppression_test_warn_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ /*
+ * Count must be checked inside the scope; the handle
+ * is not accessible after the block exits.
+ */
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static noinline void trigger_backtrace_warn(void)
+{
+ WARN(1, "This backtrace should be suppressed");
+}
+
+static void backtrace_suppression_test_warn_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_warn_multi(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
+
+ kunit_warning_suppress(test) {
+ WARN_ON(1);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static noinline void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
+
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn_on();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_count(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0);
+
+ WARN(1, "suppressed");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+
+ WARN(1, "suppressed again");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_active_state(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static void backtrace_suppression_test_multi_scope(struct kunit *test)
+{
+ struct kunit_suppressed_warning *sw1, *sw2;
+
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+static void backtrace_suppression_test_cross_kthread(struct kunit *test)
+{
+ struct cross_kthread_data data;
+ struct task_struct *task;
+
+ data.was_active = false;
+ init_completion(&data.done);
+
+ kunit_warning_suppress(test) {
+ task = kthread_run(cross_kthread_fn, &data, "kunit-cross-test");
+ KUNIT_ASSERT_FALSE(test, IS_ERR(task));
+ wait_for_completion(&data.done);
+ kthread_stop(task);

Albert Esteve

unread,
May 13, 2026, 3:31:43 AM (12 days ago) May 13
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Maíra Canal, Alessandro Carminati, Albert Esteve, Dan Carpenter, Simona Vetter
From: Guenter Roeck <li...@roeck-us.net>

The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
intentionally trigger warning backtraces by providing bad parameters to
the tested functions. What is tested is the return value, not the existence
of a warning backtrace. Suppress the backtraces to avoid clogging the
kernel log and distraction from real problems. Additionally, the
suppression API allows to actually ensure a warning was triggered,
without parsing any kernel logs and keeping them clean.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Acked-by: Maíra Canal <mca...@igalia.com>
Cc: Maarten Lankhorst <maarten....@linux.intel.com>
Cc: David Airlie <air...@gmail.com>
Cc: Daniel Vetter <dan...@ffwll.ch>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Acked-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
drivers/gpu/drm/tests/drm_rect_test.c | 32 ++++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c
index 17e1f34b76101..ccc741b6191ff 100644
--- a/drivers/gpu/drm/tests/drm_rect_test.c
+++ b/drivers/gpu/drm/tests/drm_rect_test.c
@@ -10,6 +10,7 @@
#include <drm/drm_rect.h>
#include <drm/drm_mode.h>

+#include <linux/limits.h>
#include <linux/string_helpers.h>
#include <linux/errno.h>

@@ -407,10 +408,20 @@ KUNIT_ARRAY_PARAM(drm_rect_scale, drm_rect_scale_cases, drm_rect_scale_case_desc
static void drm_test_rect_calc_hscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
- int scaling_factor;
+ int expected_warnings = params->expected_scaling_factor == -EINVAL;
+ int scaling_factor = INT_MIN;

- scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * drm_rect_calc_hscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects unit tests with -EINVAL
+ * error code in expected_scaling_factor.
+ */
+ kunit_warning_suppress(test) {
+ scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
+ params->min_range,
+ params->max_range);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings);
+ }

KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
@@ -418,10 +429,19 @@ static void drm_test_rect_calc_hscale(struct kunit *test)
static void drm_test_rect_calc_vscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
- int scaling_factor;
+ int expected_warnings = params->expected_scaling_factor == -EINVAL;
+ int scaling_factor = INT_MIN;

Albert Esteve

unread,
May 13, 2026, 3:31:54 AM (12 days ago) May 13
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Document API functions for suppressing warning backtraces.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ }
+
+.. note::
+ The warning count must be checked inside the block; the suppression handle
+ is not accessible after the block exits.
+
+- Direct functions return an explicit handle pointer. Use them when the handle
+ needs to be retained or passed across helper functions:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {

Albert Esteve

unread,
May 14, 2026, 4:38:31 AM (11 days ago) May 14
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
These were mostly sashiko + kernel test robot comments.

The current version is available at
https://sashiko.dev/#/patchset/20260513-kunit_add_support-v10-0-e379d206c8cd%40redhat.com,
which again has a few comments that I'd consider potential real
issues.

I think `synchronize_rcu()` can be removed in patch 1, to avoid one of
the mentioned issues. The struct kunit_suppressed_warning task would
be safer if we increased its refcount. I'll take a reference on
task_struct via get_task_struct() when starting suppression and
release it on cleanup.

We can address the kthread use-after-free problem with the warn
suppression selftest by actively looping while waiting for
kthread_stop().

Finally, I am thinking of adding something like this to the drm test
(similar to what we do with the suppression selftest):
```
if (!IS_ENABLED(CONFIG_BUG))
kunit_skip(test, "requires CONFIG_BUG");
```
While sashiko is right about this, it is a bit annoying having to add
this to tests that want to use the suppression API, especially since
we are setting the drm test as a "real-life example". But I'd say that
it is better to warn that the configuration is required rather than
fail an assertion without knowing the reason.

Overall, these comments were slightly nitpicky already. So I hope I
can clear them all with a new version. Sorry for the added noise, but
it's better to fix these before merging!

Albert Esteve

unread,
May 14, 2026, 7:07:22 AM (11 days ago) May 14
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
Changes since v10:
- Remove synchronize_rcu() to avoid sleeping in atomic context
- Pin task_struct refcount to prevent ABA false-positive matches
- Loop in suppression selftest to prevent use-after-free on kthread exit
- Skip DRM rect tests on CONFIG_BUG=n
- Link to v10: https://lore.kernel.org/r/20260513-kunit_add_sup...@redhat.com
drivers/gpu/drm/tests/drm_rect_test.c | 46 +++++++-
include/kunit/test-bug.h | 26 +++++
include/kunit/test.h | 98 ++++++++++++++++
kernel/panic.c | 11 ++
lib/bug.c | 14 ++-
lib/kunit/Makefile | 4 +-
lib/kunit/backtrace-suppression-test.c | 198 ++++++++++++++++++++++++++++++++
lib/kunit/bug.c | 120 +++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
10 files changed, 555 insertions(+), 10 deletions(-)

Albert Esteve

unread,
May 14, 2026, 7:07:27 AM (11 days ago) May 14
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve
From: Alessandro Carminati <acar...@redhat.com>

Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code. Since
each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it. Limit changes to generic code
to the absolute minimum.

Implementation details:
Suppression is integrated into the existing KUnit hooks infrastructure
in test-bug.h, reusing the kunit_running static branch for zero
overhead when no tests are running.

Suppression is checked at three points in the warning path:
- In warn_slowpath_fmt(), the check runs before any output, fully
suppressing both message and backtrace. This covers architectures
without __WARN_FLAGS.
- In __warn_printk(), the check suppresses the warning message text.
This covers architectures that define __WARN_FLAGS but not their own
__WARN_printf (arm64, loongarch, parisc, powerpc, riscv, sh), where
the message is printed before the trap enters __report_bug().
- In __report_bug(), the check runs before __warn() is called,
suppressing the backtrace and stack dump.

To avoid double-counting on architectures where both __warn_printk()
and __report_bug() run for the same warning, kunit_is_suppressed_warning()
takes a bool parameter: true to increment the suppression counter
(used in warn_slowpath_fmt and __report_bug), false to check only
(used in __warn_printk).

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via kunit_add_action(), ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Two API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped, uses __cleanup for
automatic teardown on scope exit, kunit_add_action() as safety net
for abnormal exits (e.g. kthread_exit from failed assertions).
Suppression handle is only accessible inside the block.
- kunit_start/end_suppress_warning(test): direct functions returning
an explicit handle, for retaining the handle within the test,
or for cross-function usage.

Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: Kees Cook <ke...@kernel.org>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
include/kunit/test-bug.h | 26 ++++++++++
include/kunit/test.h | 98 ++++++++++++++++++++++++++++++++++++++
kernel/panic.c | 11 +++++
lib/bug.c | 14 +++++-
lib/kunit/Makefile | 3 +-
lib/kunit/bug.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
7 files changed, 271 insertions(+), 3 deletions(-)
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 656f1fa35abcc..4592f9d0aa8dd 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -10,7 +10,8 @@ kunit-objs += test.o \
executor.o \
attributes.o \
device.o \
- platform.o
+ platform.o \
+ bug.o

ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
new file mode 100644
index 0000000000000..8579235c9ca68
--- /dev/null
+++ b/lib/kunit/bug.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit helpers for backtrace suppression
+ *
+ * Copyright (C) 2025 Alessandro Carminati <acar...@redhat.com>
+ * Copyright (C) 2024 Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/resource.h>
+#include <linux/export.h>
+#include <linux/rculist.h>
+#include <linux/sched.h>
+#include <linux/sched/task.h>
+#include <linux/spinlock.h>
+
+#include "hooks-impl.h"
+
+struct kunit_suppressed_warning {
+ struct list_head node;
+ struct task_struct *task;
+ struct kunit *test;
+ atomic_t counter;
+};
+
+static LIST_HEAD(suppressed_warnings);
+static DEFINE_SPINLOCK(suppressed_warnings_lock);
+
+static void kunit_suppress_warning_remove(struct kunit_suppressed_warning *w)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&suppressed_warnings_lock, flags);
+ list_del_rcu(&w->node);
+ spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
+ put_task_struct(w->task);
+}
+
+KUNIT_DEFINE_ACTION_WRAPPER(kunit_suppress_warning_cleanup,
+ kunit_suppress_warning_remove,
+ struct kunit_suppressed_warning *);
+
+bool kunit_has_active_suppress_warning(void)
+{
+ return __kunit_is_suppressed_warning_impl(false);
+}
+EXPORT_SYMBOL_GPL(kunit_has_active_suppress_warning);
+
+struct kunit_suppressed_warning *
+kunit_start_suppress_warning(struct kunit *test)
+{
+ struct kunit_suppressed_warning *w;
+ unsigned long flags;
+ int ret;
+
+ if (kunit_has_active_suppress_warning()) {
+ KUNIT_FAIL(test, "Another suppression block is already active");
+ return NULL;
+ }
+
+ w = kunit_kzalloc(test, sizeof(*w), GFP_KERNEL);
+ if (!w) {
+ KUNIT_FAIL(test, "Failed to allocate suppression handle.");
+ return NULL;
+ }
+
+ w->task = get_task_struct(current);
+{
+ struct kunit_suppressed_warning *w;
+

Albert Esteve

unread,
May 14, 2026, 7:07:37 AM (11 days ago) May 14
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Add unit tests to verify that warning backtrace suppression works.

Tests cover both API forms:
- Scoped: kunit_warning_suppress() with in-block count verification
and post-block inactivity check.
- Direct functions: kunit_start/end_suppress_warning() with
sequential independent suppression blocks and per-block counts.

Furthermore, tests verify incremental warning counting, that
kunit_has_active_suppress_warning() transitions correctly around
suppression boundaries, and that suppression active in the test
kthread does not leak to a separate kthread.

If backtrace suppression does _not_ work, the unit tests will likely
trigger unsuppressed backtraces, which should actually help to get
the affected architectures / platforms fixed.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/Makefile | 1 +
lib/kunit/backtrace-suppression-test.c | 198 +++++++++++++++++++++++++++++++++
2 files changed, 199 insertions(+)

diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 4592f9d0aa8dd..2e8a6b71a2ab0 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -22,6 +22,7 @@ obj-$(if $(CONFIG_KUNIT),y) += hooks.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o

# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/backtrace-suppression-test.c b/lib/kunit/backtrace-suppression-test.c
new file mode 100644
index 0000000000000..7a2a59c6a780d
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for suppressing warning tracebacks.
+ *
+ * Copyright (C) 2024, Guenter Roeck
+ * Author: Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/test.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+
+static void backtrace_suppression_test_warn_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ /*
+ * Count must be checked inside the scope; the handle
+ * is not accessible after the block exits.
+ */
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static noinline void trigger_backtrace_warn(void)
+{
+ WARN(1, "This backtrace should be suppressed");
+}
+
+static void backtrace_suppression_test_warn_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_warn_multi(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
+
+ kunit_warning_suppress(test) {
+ WARN_ON(1);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static noinline void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
+
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn_on();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_count(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0);
+
+ WARN(1, "suppressed");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+
+ WARN(1, "suppressed again");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_active_state(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static void backtrace_suppression_test_multi_scope(struct kunit *test)
+{
+ while (!kthread_should_stop())
+ schedule();
+ return 0;
+}
+
+static void backtrace_suppression_test_cross_kthread(struct kunit *test)
+{
+ struct cross_kthread_data data;
+ struct task_struct *task;
+

Albert Esteve

unread,
May 14, 2026, 7:07:44 AM (11 days ago) May 14
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Maíra Canal, Alessandro Carminati, Albert Esteve, Dan Carpenter, Simona Vetter
From: Guenter Roeck <li...@roeck-us.net>

The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
intentionally trigger warning backtraces by providing bad parameters to
the tested functions. What is tested is the return value, not the existence
of a warning backtrace. Suppress the backtraces to avoid clogging the
kernel log and distraction from real problems. Additionally, the
suppression API allows to actually ensure a warning was triggered,
without parsing any kernel logs and keeping them clean.
The suppression check requires CONFIG_BUG enabled.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Acked-by: Maíra Canal <mca...@igalia.com>
Cc: Maarten Lankhorst <maarten....@linux.intel.com>
Cc: David Airlie <air...@gmail.com>
Cc: Daniel Vetter <dan...@ffwll.ch>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Acked-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
drivers/gpu/drm/tests/drm_rect_test.c | 46 ++++++++++++++++++++++++++++++-----
1 file changed, 40 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c
index 17e1f34b76101..3402f993d7d33 100644
--- a/drivers/gpu/drm/tests/drm_rect_test.c
+++ b/drivers/gpu/drm/tests/drm_rect_test.c
@@ -10,6 +10,7 @@
#include <drm/drm_rect.h>
#include <drm/drm_mode.h>

+#include <linux/limits.h>
#include <linux/string_helpers.h>
#include <linux/errno.h>

@@ -407,10 +408,27 @@ KUNIT_ARRAY_PARAM(drm_rect_scale, drm_rect_scale_cases, drm_rect_scale_case_desc
static void drm_test_rect_calc_hscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
- int scaling_factor;
+ int expected_warnings = params->expected_scaling_factor == -EINVAL;
+ int scaling_factor = INT_MIN;

- scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * Without CONFIG_BUG, WARN_ON() is a no-op and the suppressed warning
+ * count stays zero, failing the assertion.
+ */
+ if (expected_warnings && !IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ /*
+ * drm_rect_calc_hscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects unit tests with -EINVAL
+ * error code in expected_scaling_factor.
+ */
+ kunit_warning_suppress(test) {
+ scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
+ params->min_range,
+ params->max_range);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings);
+ }

KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
@@ -418,10 +436,26 @@ static void drm_test_rect_calc_hscale(struct kunit *test)
static void drm_test_rect_calc_vscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
- int scaling_factor;
+ int expected_warnings = params->expected_scaling_factor == -EINVAL;
+ int scaling_factor = INT_MIN;

- scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * Without CONFIG_BUG, WARN_ON() is a no-op and the suppressed warning
+ * count stays zero, failing the assertion.
+ */
+ if (expected_warnings && !IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+

Albert Esteve

unread,
May 14, 2026, 7:07:49 AM (11 days ago) May 14
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Document API functions for suppressing warning backtraces.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ }
+
+.. note::
+ The warning count must be checked inside the block; the suppression handle
+ is not accessible after the block exits.
+
+- Direct functions return an explicit handle pointer. Use them when the handle
+ needs to be retained or passed across helper functions:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {

Albert Esteve

unread,
May 15, 2026, 3:58:56 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook
Sashiko says:
"""
Is the CONFIG_KUNIT check sufficient here?
CONFIG_KUNIT is a tristate configuration option. When KUnit is built as a
module, the preprocessor macro CONFIG_KUNIT_MODULE is defined instead,
leaving CONFIG_KUNIT undefined.
Because lib/bug.c is compiled into the core kernel, this block will be
silently stripped out during a module build. This prevents warning
suppression from working on all architectures that rely on __report_bug().
Could this use IS_ENABLED(CONFIG_KUNIT) instead, or be dropped completely
since include/kunit/test-bug.h provides a safe stub?
"""
Ugh, it is right. I did not consider the module case. Not only is it
safe now as it says, but iirc we added it for performance, however,
since we now have static_branch, it is not really needed. I think
removing it is the right thing to do here.
Sashiko says:
"""
Does this code introduce a use-after-free regression for concurrent RCU
readers?
Because the suppression handle is allocated using kunit_kzalloc() below,
the KUnit framework will automatically free it with a synchronous kfree()
at the end of the test.
Since the handle is unlinked using list_del_rcu() here, but there is no
synchronize_rcu() or kfree_rcu() between the list removal and the memory
free, a concurrent task evaluating warnings under rcu_read_lock() could
dereference the pointer after it has been freed.
Would it be safer to allocate the handle with kzalloc() and explicitly free
it using kfree_rcu() inside this cleanup action?
"""
It is taking a few iterations to get this right...
In the previous version we ruled out synchronize_rcu() because it is a
blocking call that can deadlock if exited while holding the RCU lock.
On the other hand, the suggested kfree_rcu(), only frees memory, but
we also need to release the task reference in the w struct after the
grace period. Reading
`Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst`,
a solution could be to hold an `rcu_head` in the suppressed warning
struct and invoke call_rcu directly (and explicitly free as suggested
by Sashiko, so I'd need to change kunit_kzalloc() too).
I hope that clears all races.

Albert Esteve

unread,
May 15, 2026, 4:30:24 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Dan Carpenter, Kees Cook
On Thu, May 14, 2026 at 1:07 PM Albert Esteve <aes...@redhat.com> wrote:
>
Sashiko says:
"""
Is there a reason why backtrace_suppression_test_warn_on_direct() falls back
to checking CONFIG_KALLSYMS while
backtrace_suppression_test_warn_on_indirect() does not?
The core warning suppression logic matches on the task, so it seems like
CONFIG_KALLSYMS should be sufficient for both.
Could this cause tests to be unnecessarily skipped on systems with
CONFIG_KALLSYMS enabled but CONFIG_DEBUG_BUGVERBOSE disabled?
"""
This is interesting. I am not sure why they were different; it was
probably an oversight. But after looking at it, this is probably a
leftover from when suppression occurred at the macros. They do not
seem necessary anymore. So I will remove them.

Albert Esteve

unread,
May 15, 2026, 4:53:18 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

One option to address the problem would be to add messages such as
"expected warning backtraces start/end here" to the kernel log.
However, that would again require filter scripts, might result in
missing real problematic warning backtraces triggered while the test
is running, and the irrelevant backtrace(s) would still clog the
kernel log.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code.
Since each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it, without requiring any
architecture-specific code.

Overview:
Patch#1 Introduces the suppression infrastructure integrated into
KUnit's hook mechanism.
Patch#2 Adds selftests to validate the functionality.
Patch#3 Demonstrates real-world usage in the DRM subsystem.
Patch#4 Documents the new API and usage guidelines.

Design Notes:
Suppression is integrated into the existing KUnit hooks infrastructure,
reusing the kunit_running static branch for zero overhead
when no tests are running. The implementation lives entirely in the
kunit module; only a static-inline wrapper and a function pointer
slot are added to built-in code.

Suppression is checked at three points in the warning path:
- In `warn_slowpath_fmt()` (kernel/panic.c), for architectures without
__WARN_FLAGS. The check runs before any output, fully suppressing
both message and backtrace.
- In `__warn_printk()` (kernel/panic.c), for architectures that define
__WARN_FLAGS but not their own __WARN_printf (arm64, loongarch,
parisc, powerpc, riscv, sh). The check suppresses the warning message
text that is printed before the trap enters __report_bug().
- In `__report_bug()` (lib/bug.c), for architectures that define
__WARN_FLAGS. The check runs before `__warn()` is called, suppressing
the backtrace and stack dump.

To avoid double-counting on architectures where both `__warn_printk()`
and `__report_bug()` run for the same warning, the hook takes a bool
parameter: true to increment the suppression counter, false to suppress
without counting.

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via `kunit_add_action()`, ensuring
automatic cleanup at test exit. Writer-side access to the global
suppression list is serialized with a spinlock; readers use RCU.

Two API forms are provided:
Changes since v11:
- Use call_rcu() to defer free without blocking
- Remove #ifdef CONFIG_KUNIT guard in lib/bug.c
- Remove stale config checks from selftest
- Replace skip on DRM rect tests with conditional expectation
- Link to v11: https://lore.kernel.org/r/20260514-kunit_add_sup...@redhat.com
drivers/gpu/drm/tests/drm_rect_test.c | 36 +++++-
include/kunit/test-bug.h | 26 +++++
include/kunit/test.h | 98 ++++++++++++++++
kernel/panic.c | 11 ++
lib/bug.c | 12 +-
lib/kunit/Makefile | 4 +-
lib/kunit/backtrace-suppression-test.c | 192 ++++++++++++++++++++++++++++++++
lib/kunit/bug.c | 131 ++++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
10 files changed, 548 insertions(+), 10 deletions(-)

Albert Esteve

unread,
May 15, 2026, 4:53:29 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve
From: Alessandro Carminati <acar...@redhat.com>

Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code. Since
each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it. Limit changes to generic code
to the absolute minimum.

Implementation details:
Suppression is integrated into the existing KUnit hooks infrastructure
in test-bug.h, reusing the kunit_running static branch for zero
overhead when no tests are running.

Suppression is checked at three points in the warning path:
- In warn_slowpath_fmt(), the check runs before any output, fully
suppressing both message and backtrace. This covers architectures
without __WARN_FLAGS.
- In __warn_printk(), the check suppresses the warning message text.
This covers architectures that define __WARN_FLAGS but not their own
__WARN_printf (arm64, loongarch, parisc, powerpc, riscv, sh), where
the message is printed before the trap enters __report_bug().
- In __report_bug(), the check runs before __warn() is called,
suppressing the backtrace and stack dump.

To avoid double-counting on architectures where both __warn_printk()
and __report_bug() run for the same warning, kunit_is_suppressed_warning()
takes a bool parameter: true to increment the suppression counter
(used in warn_slowpath_fmt and __report_bug), false to check only
(used in __warn_printk).

The suppression state is dynamically allocated via kzalloc() and its
lifetime is managed via kunit_add_action(). On cleanup, the node is
removed with list_del_rcu() and the object is freed asynchronously via
call_rcu(), ensuring that any concurrent RCU reader that obtained a
pointer to the handle before removal can finish safely. Writer-side
access to the global suppression list is serialized with a spinlock;
readers use RCU.

Two API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped, uses __cleanup for
automatic teardown on scope exit, kunit_add_action() as safety net
for abnormal exits (e.g. kthread_exit from failed assertions).
Suppression handle is only accessible inside the block.
- kunit_start/end_suppress_warning(test): direct functions returning
an explicit handle, for retaining the handle within the test,
or for cross-function usage.

Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: Kees Cook <ke...@kernel.org>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
include/kunit/test-bug.h | 26 ++++++++++
include/kunit/test.h | 98 +++++++++++++++++++++++++++++++++++
kernel/panic.c | 11 ++++
lib/bug.c | 12 ++++-
lib/kunit/Makefile | 3 +-
lib/kunit/bug.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
7 files changed, 280 insertions(+), 3 deletions(-)
index 224f4cfa4aa31..874cb4ae4d047 100644
--- a/lib/bug.c
+++ b/lib/bug.c
@@ -48,6 +48,7 @@
#include <linux/rculist.h>
#include <linux/ftrace.h>
#include <linux/context_tracking.h>
+#include <kunit/test-bug.h>

extern struct bug_entry __start___bug_table[], __stop___bug_table[];

@@ -209,8 +210,6 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
return BUG_TRAP_TYPE_NONE;
}

- disable_trace_on_warning();
-
bug_get_file_line(bug, &file, &line);
fmt = bug_get_format(bug);

@@ -220,6 +219,15 @@ static enum bug_trap_type __report_bug(struct bug_entry *bug, unsigned long buga
no_cut = bug->flags & BUGFLAG_NO_CUT_HERE;
has_args = bug->flags & BUGFLAG_ARGS;

+ /*
+ * Before the once logic so suppressed warnings do not consume
+ * the single-fire budget of WARN_ON_ONCE().
+ */
+ if (warning && kunit_is_suppressed_warning(true))
+ return BUG_TRAP_TYPE_WARN;
+
+ disable_trace_on_warning();
+
if (warning && once) {
if (done)
return BUG_TRAP_TYPE_WARN;
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 656f1fa35abcc..4592f9d0aa8dd 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -10,7 +10,8 @@ kunit-objs += test.o \
executor.o \
attributes.o \
device.o \
- platform.o
+ platform.o \
+ bug.o

ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
new file mode 100644
index 0000000000000..8a5725aebcaa4
--- /dev/null
+++ b/lib/kunit/bug.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit helpers for backtrace suppression
+ *
+ * Copyright (C) 2025 Alessandro Carminati <acar...@redhat.com>
+ * Copyright (C) 2024 Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/resource.h>
+#include <linux/export.h>
+#include <linux/rculist.h>
+#include <linux/sched.h>
+#include <linux/sched/task.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "hooks-impl.h"
+
+struct kunit_suppressed_warning {
+ struct list_head node;
+ struct rcu_head rcu;
+ struct task_struct *task;
+ struct kunit *test;
+ atomic_t counter;
+};
+
+static LIST_HEAD(suppressed_warnings);
+static DEFINE_SPINLOCK(suppressed_warnings_lock);
+
+static void kunit_suppress_warning_free(struct rcu_head *rcu)
+{
+ struct kunit_suppressed_warning *w =
+ container_of(rcu, struct kunit_suppressed_warning, rcu);
+
+ put_task_struct(w->task);
+ kfree(w);
+}
+
+static void kunit_suppress_warning_remove(struct kunit_suppressed_warning *w)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&suppressed_warnings_lock, flags);
+ list_del_rcu(&w->node);
+ spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
+ call_rcu(&w->rcu, kunit_suppress_warning_free);
+}
+
+KUNIT_DEFINE_ACTION_WRAPPER(kunit_suppress_warning_cleanup,
+ kunit_suppress_warning_remove,
+ struct kunit_suppressed_warning *);
+
+bool kunit_has_active_suppress_warning(void)
+{
+ return __kunit_is_suppressed_warning_impl(false);
+}
+EXPORT_SYMBOL_GPL(kunit_has_active_suppress_warning);
+
+struct kunit_suppressed_warning *
+kunit_start_suppress_warning(struct kunit *test)
+{
+ struct kunit_suppressed_warning *w;
+ unsigned long flags;
+ int ret;
+
+ if (kunit_has_active_suppress_warning()) {
+ KUNIT_FAIL(test, "Another suppression block is already active");
+ return NULL;
+ }
+
+ w = kzalloc(sizeof(*w), GFP_KERNEL);

Albert Esteve

unread,
May 15, 2026, 4:53:35 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Add unit tests to verify that warning backtrace suppression works.

Tests cover both API forms:
- Scoped: kunit_warning_suppress() with in-block count verification
and post-block inactivity check.
- Direct functions: kunit_start/end_suppress_warning() with
sequential independent suppression blocks and per-block counts.

Furthermore, tests verify incremental warning counting, that
kunit_has_active_suppress_warning() transitions correctly around
suppression boundaries, and that suppression active in the test
kthread does not leak to a separate kthread.

If backtrace suppression does _not_ work, the unit tests will likely
trigger unsuppressed backtraces, which should actually help to get
the affected architectures / platforms fixed.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/Makefile | 1 +
lib/kunit/backtrace-suppression-test.c | 192 +++++++++++++++++++++++++++++++++
2 files changed, 193 insertions(+)

diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 4592f9d0aa8dd..2e8a6b71a2ab0 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -22,6 +22,7 @@ obj-$(if $(CONFIG_KUNIT),y) += hooks.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o

# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/backtrace-suppression-test.c b/lib/kunit/backtrace-suppression-test.c
new file mode 100644
index 0000000000000..59a038b2739f5
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for suppressing warning tracebacks.
+ *
+ * Copyright (C) 2024, Guenter Roeck
+ * Author: Guenter Roeck <li...@roeck-us.net>
+ */
+
+ kunit_warning_suppress(test) {
+ WARN_ON(1);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static noinline void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+static void backtrace_suppression_test_multi_scope(struct kunit *test)
+{
+ struct kunit_suppressed_warning *sw1, *sw2;
+
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+static void backtrace_suppression_test_cross_kthread(struct kunit *test)
+{
+ struct cross_kthread_data data;
+ struct task_struct *task;
+

Albert Esteve

unread,
May 15, 2026, 4:53:42 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Maíra Canal, Alessandro Carminati, Albert Esteve, Dan Carpenter, Simona Vetter
From: Guenter Roeck <li...@roeck-us.net>

The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
intentionally trigger warning backtraces by providing invalid parameters
the tested functions. Suppress the backtraces to avoid clogging the
kernel log and distracting from real problems.

The suppression API also exposes a warning counter, which is used
to assert that the expected warning was actually triggered. On
CONFIG_BUG=n, WARN_ON() is a no-op and the counter stays zero;
the expected count is adjusted accordingly, preserving the return
value check on all configurations.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Acked-by: Maíra Canal <mca...@igalia.com>
Cc: Maarten Lankhorst <maarten....@linux.intel.com>
Cc: David Airlie <air...@gmail.com>
Cc: Daniel Vetter <dan...@ffwll.ch>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Acked-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
drivers/gpu/drm/tests/drm_rect_test.c | 36 +++++++++++++++++++++++++++++------
1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c
index 17e1f34b76101..5aa8ec5fc4d64 100644
--- a/drivers/gpu/drm/tests/drm_rect_test.c
+++ b/drivers/gpu/drm/tests/drm_rect_test.c
@@ -10,6 +10,7 @@
#include <drm/drm_rect.h>
#include <drm/drm_mode.h>

+#include <linux/limits.h>
#include <linux/string_helpers.h>
#include <linux/errno.h>

@@ -407,10 +408,22 @@ KUNIT_ARRAY_PARAM(drm_rect_scale, drm_rect_scale_cases, drm_rect_scale_case_desc
static void drm_test_rect_calc_hscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
- int scaling_factor;
+ /* With CONFIG_BUG=n, WARN_ON() is a no-op so no warning fires. */
+ int expected_warnings = IS_ENABLED(CONFIG_BUG) ?
+ (params->expected_scaling_factor == -EINVAL) : 0;
+ int scaling_factor = INT_MIN;

- scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*
+ * drm_rect_calc_hscale() generates a warning backtrace whenever bad
+ * parameters are passed to it. This affects unit tests with -EINVAL
+ * error code in expected_scaling_factor.
+ */
+ kunit_warning_suppress(test) {
+ scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
+ params->min_range,
+ params->max_range);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, expected_warnings);
+ }

KUNIT_EXPECT_EQ(test, scaling_factor, params->expected_scaling_factor);
}
@@ -418,10 +431,21 @@ static void drm_test_rect_calc_hscale(struct kunit *test)
static void drm_test_rect_calc_vscale(struct kunit *test)
{
const struct drm_rect_scale_case *params = test->param_value;
- int scaling_factor;
+ /* With CONFIG_BUG=n, WARN_ON() is a no-op so no warning fires. */
+ int expected_warnings = IS_ENABLED(CONFIG_BUG) ?
+ (params->expected_scaling_factor == -EINVAL) : 0;
+ int scaling_factor = INT_MIN;

- scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
- params->min_range, params->max_range);
+ /*

Albert Esteve

unread,
May 15, 2026, 4:53:48 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Document API functions for suppressing warning backtraces.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ }
+
+.. note::
+ The warning count must be checked inside the block; the suppression handle
+ is not accessible after the block exits.
+
+- Direct functions return an explicit handle pointer. Use them when the handle
+ needs to be retained or passed across helper functions:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {

Albert Esteve

unread,
May 15, 2026, 7:46:35 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
A regression occurred in this version because `kunit_kzalloc` was
removed, following sashiko's suggestions in the last version. The
handle cannot be used after the kunit_end_suppress_warning() call
because we explicitly kfree(), resulting in a use-after-free. I
should've seen it, but the tests passed so it did not click (not sure
why exactly, probably the memory was still mapped during the grace
period so the access succeeded spuriously). This back and forth is not
going well, so I will revert lib/kunit/bug.c to v9, before removing
`synchronize_rcu()`, which was working fine. It used `kunit_kzalloc`
so KUnit correctly handled the memory lifetime. The problem is that
`synchronize_rcu()` requires process context, which is always the case
here, it should never be called under a spinlock or RCU read lock.
Re-adding may trigger a comment from the automatic review, but I think
it is the safest option.

This time sashiko found another issue and suggested `in_task()`, which
sounds like a nice addition to avoid suppressing real warnings within
interrupt handlers. So one more version it is...

Albert Esteve

unread,
May 15, 2026, 8:30:00 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
sequential suppression blocks are allowed.
- kunit_start/end_suppress_warning(test): direct functions that return
an explicit handle. Use when the handle needs to be retained, or passed
across helpers. Multiple sequential suppression blocks are allowed.
Changes since v12:
- Reverted to the v9 synchronize_rcu() approach
- Add in_task() check at the top of __kunit_is_suppressed_warning_impl()
- Link to v12: https://lore.kernel.org/r/20260515-kunit_add_sup...@redhat.com
lib/kunit/bug.c | 127 +++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
10 files changed, 544 insertions(+), 10 deletions(-)

Albert Esteve

unread,
May 15, 2026, 8:30:03 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Albert Esteve
From: Alessandro Carminati <acar...@redhat.com>

Some unit tests intentionally trigger warning backtraces by passing bad
parameters to kernel API functions. Such unit tests typically check the
return value from such calls, not the existence of the warning backtrace.

Such intentionally generated warning backtraces are neither desirable
nor useful for a number of reasons:
- They can result in overlooked real problems.
- A warning that suddenly starts to show up in unit tests needs to be
investigated and has to be marked to be ignored, for example by
adjusting filter scripts. Such filters are ad hoc because there is
no real standard format for warnings. On top of that, such filter
scripts would require constant maintenance.

Solve the problem by providing a means to suppress warning backtraces
originating from the current kthread while executing test code. Since
each KUnit test runs in its own kthread, this effectively scopes
suppression to the test that enabled it. Limit changes to generic code
to the absolute minimum.

Implementation details:
Suppression is integrated into the existing KUnit hooks infrastructure
in test-bug.h, reusing the kunit_running static branch for zero
overhead when no tests are running.

Suppression is checked at three points in the warning path:
- In warn_slowpath_fmt(), the check runs before any output, fully
suppressing both message and backtrace. This covers architectures
without __WARN_FLAGS.
- In __warn_printk(), the check suppresses the warning message text.
This covers architectures that define __WARN_FLAGS but not their own
__WARN_printf (arm64, loongarch, parisc, powerpc, riscv, sh), where
the message is printed before the trap enters __report_bug().
- In __report_bug(), the check runs before __warn() is called,
suppressing the backtrace and stack dump.

To avoid double-counting on architectures where both __warn_printk()
and __report_bug() run for the same warning, kunit_is_suppressed_warning()
takes a bool parameter: true to increment the suppression counter
(used in warn_slowpath_fmt and __report_bug), false to check only
(used in __warn_printk).

The suppression state is dynamically allocated via kunit_kzalloc() and
tied to the KUnit test lifecycle via kunit_add_action(), ensuring
automatic cleanup at test exit. On cleanup, the node is removed with
list_del_rcu() followed by synchronize_rcu() to wait for any concurrent
RCU readers to finish. Because kunit_end_suppress_warning() (and the
__cleanup wrapper) always runs from process context, synchronize_rcu()
is safe. The handle memory remains valid until the test exits, so the
suppression count can be read after the scope closes. Writer-side
access to the global suppression list is serialized with a spinlock;
readers use RCU. To avoid false suppression of warnings fired from
hardware interrupt handlers (where current still points to the test
task), the check exits early when not in task context.

Two API forms are provided:
- kunit_warning_suppress(test) { ... }: scoped, uses __cleanup for
automatic teardown on scope exit, kunit_add_action() as safety net
for abnormal exits (e.g. kthread_exit from failed assertions).
Suppression handle is only accessible inside the block.
- kunit_start/end_suppress_warning(test): direct functions returning
an explicit handle, for retaining the handle within the test,
or for cross-function usage.

Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: Kees Cook <ke...@kernel.org>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
include/kunit/test-bug.h | 26 ++++++++++
include/kunit/test.h | 98 ++++++++++++++++++++++++++++++++++++
kernel/panic.c | 11 ++++
lib/bug.c | 12 ++++-
lib/kunit/Makefile | 3 +-
lib/kunit/bug.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++
lib/kunit/hooks-impl.h | 2 +
7 files changed, 276 insertions(+), 3 deletions(-)
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 656f1fa35abcc..4592f9d0aa8dd 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -10,7 +10,8 @@ kunit-objs += test.o \
executor.o \
attributes.o \
device.o \
- platform.o
+ platform.o \
+ bug.o

ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
new file mode 100644
index 0000000000000..6752b497aeefe
--- /dev/null
+++ b/lib/kunit/bug.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit helpers for backtrace suppression
+ *
+ * Copyright (C) 2025 Alessandro Carminati <acar...@redhat.com>
+ * Copyright (C) 2024 Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/resource.h>
+#include <linux/export.h>
+#include <linux/rculist.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+#include "hooks-impl.h"
+
+struct kunit_suppressed_warning {
+ struct list_head node;
+ struct task_struct *task;
+ struct kunit *test;
+ atomic_t counter;
+};
+
+static LIST_HEAD(suppressed_warnings);
+static DEFINE_SPINLOCK(suppressed_warnings_lock);
+
+static void kunit_suppress_warning_remove(struct kunit_suppressed_warning *w)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&suppressed_warnings_lock, flags);
+ list_del_rcu(&w->node);
+ spin_unlock_irqrestore(&suppressed_warnings_lock, flags);
+ synchronize_rcu(); /* Wait for readers to finish */
+}
+
+KUNIT_DEFINE_ACTION_WRAPPER(kunit_suppress_warning_cleanup,
+ kunit_suppress_warning_remove,
+ struct kunit_suppressed_warning *);
+
+bool kunit_has_active_suppress_warning(void)
+{
+ return __kunit_is_suppressed_warning_impl(false);
+}
+EXPORT_SYMBOL_GPL(kunit_has_active_suppress_warning);
+
+struct kunit_suppressed_warning *
+kunit_start_suppress_warning(struct kunit *test)
+{
+ struct kunit_suppressed_warning *w;
+ unsigned long flags;
+ int ret;
+
+ if (kunit_has_active_suppress_warning()) {
+ KUNIT_FAIL(test, "Another suppression block is already active");
+ return NULL;
+ }
+
+ w = kunit_kzalloc(test, sizeof(*w), GFP_KERNEL);
+ if (!w) {
+ KUNIT_FAIL(test, "Failed to allocate suppression handle.");
+ return NULL;
+ }
+
+ /*
+ * Store current without taking a reference. The test task cannot
+ * exit before kunit tears down the test, so the pointer is stable
+ * for the lifetime of this handle.
+ */
+ w->task = current;
+{
+ struct kunit_suppressed_warning *w;
+
+ if (!in_task())
+ return false;
+

Albert Esteve

unread,
May 15, 2026, 8:30:13 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Add unit tests to verify that warning backtrace suppression works.

Tests cover both API forms:
- Scoped: kunit_warning_suppress() with in-block count verification
and post-block inactivity check.
- Direct functions: kunit_start/end_suppress_warning() with
sequential independent suppression blocks and per-block counts.

Furthermore, tests verify incremental warning counting, that
kunit_has_active_suppress_warning() transitions correctly around
suppression boundaries, and that suppression active in the test
kthread does not leak to a separate kthread.

If backtrace suppression does _not_ work, the unit tests will likely
trigger unsuppressed backtraces, which should actually help to get
the affected architectures / platforms fixed.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/Makefile | 1 +
lib/kunit/backtrace-suppression-test.c | 192 +++++++++++++++++++++++++++++++++
2 files changed, 193 insertions(+)

diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 4592f9d0aa8dd..2e8a6b71a2ab0 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -22,6 +22,7 @@ obj-$(if $(CONFIG_KUNIT),y) += hooks.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o

# string-stream-test compiles built-in only.
ifeq ($(CONFIG_KUNIT_TEST),y)
diff --git a/lib/kunit/backtrace-suppression-test.c b/lib/kunit/backtrace-suppression-test.c
new file mode 100644
index 0000000000000..59a038b2739f5
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit test for suppressing warning tracebacks.
+ *
+ * Copyright (C) 2024, Guenter Roeck
+ * Author: Guenter Roeck <li...@roeck-us.net>
+ */
+
+#include <kunit/test.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+
+static void backtrace_suppression_test_warn_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ /*
+ * Count must be checked inside the scope; the handle
+ * is not accessible after the block exits.
+ */
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static noinline void trigger_backtrace_warn(void)
+{
+ WARN(1, "This backtrace should be suppressed");
+}
+
+static void backtrace_suppression_test_warn_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_warn_multi(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ WARN(1, "This backtrace should be suppressed");
+ trigger_backtrace_warn();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ WARN_ON(1);
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static noinline void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ trigger_backtrace_warn_on();
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+}
+
+static void backtrace_suppression_test_count(struct kunit *test)
+{
+ if (!IS_ENABLED(CONFIG_BUG))
+ kunit_skip(test, "requires CONFIG_BUG");
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 0);
+
+ WARN(1, "suppressed");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+
+ WARN(1, "suppressed again");
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 2);
+ }
+}
+
+static void backtrace_suppression_test_active_state(struct kunit *test)
+{
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+
+ kunit_warning_suppress(test) {
+ KUNIT_EXPECT_TRUE(test, kunit_has_active_suppress_warning());
+ }
+
+ KUNIT_EXPECT_FALSE(test, kunit_has_active_suppress_warning());
+}
+
+static void backtrace_suppression_test_multi_scope(struct kunit *test)
+{
+static void backtrace_suppression_test_cross_kthread(struct kunit *test)
+{
+ struct cross_kthread_data data;
+ struct task_struct *task;
+

Albert Esteve

unread,
May 15, 2026, 8:30:22 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Maíra Canal, Alessandro Carminati, Albert Esteve, Dan Carpenter, Simona Vetter
From: Guenter Roeck <li...@roeck-us.net>

The drm_test_rect_calc_hscale and drm_test_rect_calc_vscale unit tests
intentionally trigger warning backtraces by providing invalid parameters
the tested functions. Suppress the backtraces to avoid clogging the
kernel log and distracting from real problems.

The suppression API also exposes a warning counter, which is used
to assert that the expected warning was actually triggered. On
CONFIG_BUG=n, WARN_ON() is a no-op and the counter stays zero;
the expected count is adjusted accordingly, preserving the return
value check on all configurations.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Acked-by: Maíra Canal <mca...@igalia.com>
Cc: Maarten Lankhorst <maarten....@linux.intel.com>
Cc: David Airlie <air...@gmail.com>
Cc: Daniel Vetter <dan...@ffwll.ch>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Acked-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---

Albert Esteve

unread,
May 15, 2026, 8:30:26 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Albert Esteve, Dan Carpenter, Kees Cook
From: Guenter Roeck <li...@roeck-us.net>

Document API functions for suppressing warning backtraces.

Tested-by: Linux Kernel Functional Testing <lk...@linaro.org>
Acked-by: Dan Carpenter <dan.ca...@linaro.org>
Reviewed-by: Kees Cook <kees...@chromium.org>
Signed-off-by: Guenter Roeck <li...@roeck-us.net>
Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Reviewed-by: David Gow <da...@davidgow.net>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
+ KUNIT_EXPECT_SUPPRESSED_WARNING_COUNT(test, 1);
+ }
+ }
+
+.. note::
+ The warning count must be checked inside the block; the suppression handle
+ is not accessible after the block exits.
+
+- Direct functions return an explicit handle pointer. Use them when the handle
+ needs to be retained or passed across helper functions:
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {

Albert Esteve

unread,
May 15, 2026, 9:36:21 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Guenter Roeck, Kees Cook
Let me address sashiko's comments for
https://sashiko.dev/#/patchset/20260515-kunit_add_support-v13-0-18ee42f96e7b%40redhat.com?part=1
here:

1. "Is this assumption always accurate? Tests frequently acquire
spinlocks or RCU read locks."
The assumption is accurate because kunit_end_suppress_warning() and
the __cleanup wrapper fire at the closing brace of the
kunit_warning_suppress() scope. If a developer holds a spinlock or RCU
read lock when that scope closes, their test is structurally incorrect
regardless of this API. The API documentation notes that process
context is required.

2. "If kunit_start_suppress_warning() fails and returns NULL, will
this skip the entire loop body?"
Yes, intentionally. KUNIT_FAIL() is called before returning NULL, so
the test is already marked as failed at that point. Skipping the body
of a failed test is expected KUnit behavior. In patch 3,
scaling_factor is initialized to INT_MIN precisely for this reason.

3. "Does this mean the single-fire budget is consumed anyway on
non-CONFIG_GENERIC_BUG architectures?"
Yes, true, but it should only affect non-__WARN_FLAGS architectures.
If there is demand, it can be addressed in a follow-up series. It does
not affect current API users.

4. "Would GFP_KERNEL / synchronize_rcu() cause a sleep-in-atomic bug
if used in atomic context?"
Yes, and that would be a test design error. kunit_warning_suppress()
is a KUnit test API; KUnit tests run in process context by design. As
stated in `Documentation/core-api/memory-allocation.rst`, GFP_KERNEL
implies GFP_RECLAIM, which requires the calling context to be allowed
to sleep. Using it in an atomic context is incorrect regardless of
which API calls it. `kunit_kzalloc(test, ..., GFP_KERNEL)` is the
standard allocation pattern throughout KUnit itself (e.g.,
lib/kunit/assert_test.c, platform-test.c, ...), so this API follows
the same convention.

5. "Could a child kthread's task_struct be freed and reused, causing
false suppression?"
The API is designed to be called from the test task only. w->task =
current stores the caller's task_struct, and the inline comment
explains the stability guarantee: the test task cannot exit before
KUnit tears down the test. The correct pattern for child kthread is
demonstrated in backtrace_suppression_test_cross_kthread: the test
task opens and closes the suppression scope; child threads only read
the suppression state. Otherwise it is an API misuse.

Guenter Roeck

unread,
May 15, 2026, 9:51:15 AM (10 days ago) May 15
to Albert Esteve, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
Hi Albert,

On 5/15/26 05:29, Albert Esteve wrote:
...

> Guenter Roeck (3):
> kunit: Add backtrace suppression self-tests
> drm: Suppress intentional warning backtraces in scaling unit tests
> kunit: Add documentation for warning backtrace suppression API
>


How much of that is from me at this point ? Wouldn't it make sense to drop me
as "author" of those patches ?

I would not mind. I had the idea, but others like you are doing the hard work
of pushing it through.

Thanks,
Guenter

Albert Esteve

unread,
May 15, 2026, 10:14:23 AM (10 days ago) May 15
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Guenter Roeck, Linux Kernel Functional Testing, Alessandro Carminati, Dan Carpenter, Kees Cook
On Fri, May 15, 2026 at 2:30 PM Albert Esteve <aes...@redhat.com> wrote:
>
> From: Guenter Roeck <li...@roeck-us.net>
>
> Add unit tests to verify that warning backtrace suppression works.
>
> Tests cover both API forms:
> - Scoped: kunit_warning_suppress() with in-block count verification
> and post-block inactivity check.
> - Direct functions: kunit_start/end_suppress_warning() with
> sequential independent suppression blocks and per-block counts.
>
> Furthermore, tests verify incremental warning counting, that
> kunit_has_active_suppress_warning() transitions correctly around
> suppression boundaries, and that suppression active in the test
> kthread does not leak to a separate kthread.
>
> If backtrace suppression does _not_ work, the unit tests will likely
> trigger unsuppressed backtraces, which should actually help to get
> the affected architectures / platforms fixed.
>

Another set of sashiko comments for this patch
https://sashiko.dev/#/patchset/20260515-kunit_add_support-v13-0-18ee42f96e7b%40redhat.com?part=2
here:

1. CPU spike from while (!kthread_should_stop()) schedule()
Ha! I expected this one because I saw it in a previous review from the
bot. schedule() from TASK_RUNNING yields the CPU; it does not
spin-wait. The thread is rescheduled only when the scheduler gives it
time, not in a tight loop. But the important thing is that the window
where this loop actually runs is negligible: the parent calls
kthread_stop() immediately after wait_for_completion() returns. Using
set_current_state(TASK_INTERRUPTIBLE) would be slightly more
CPU-friendly, but for a test that probably runs and exits in
microseconds, it makes no practical difference. And it unnecessarily
adds complexity.

2. Orphaned kthread on early abort
This cannot happen in this test. The only KUNIT_ASSERT_* that could
abort early is KUNIT_ASSERT_FALSE(test, IS_ERR(task)). If that
assertion fails, it means kthread_run() itself returned an error,
therefore, the kthread was never started and there is nothing to
orphan. If kthread_run() succeeds, the assertion passes, and execution
continues sequentially to kthread_stop(). No code path allows a live
kthread to exist while bypassing kthread_stop().

Albert Esteve

unread,
May 15, 2026, 10:25:28 AM (10 days ago) May 15
to Guenter Roeck, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, Andrew Morton, Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linux...@lists.infradead.org, linu...@vger.kernel.org, pet...@infradead.org, Alessandro Carminati, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Dan Carpenter, Simona Vetter
On Fri, May 15, 2026 at 3:51 PM Guenter Roeck <li...@roeck-us.net> wrote:
>
> Hi Albert,
>
> On 5/15/26 05:29, Albert Esteve wrote:
> ...
>
> > Guenter Roeck (3):
> > kunit: Add backtrace suppression self-tests
> > drm: Suppress intentional warning backtraces in scaling unit tests
> > kunit: Add documentation for warning backtrace suppression API
> >
>
>
> How much of that is from me at this point ? Wouldn't it make sense to drop me
> as "author" of those patches ?

Hi Guenter,

I do not mind authorship either. Signed-off-by lines already attest to
everyone's contribution.

In principle I am done with Sashiko's reviews. I addressed all
comments in their respective patches. Some lead to the rabbit holes I
put myself into for previous versions.

I do not plan to send a new version unless a human review requires a change.

If that happens, I may update the authorship. Otherwise, I'd keep it
as is, since you said you do not mind :)

Thanks!

BR,
Albert.
Reply all
Reply to author
Forward
0 new messages