[PATCH v6 0/5] kunit: Add support for suppressing warning backtraces

0 views
Skip to first unread message

Albert Esteve

unread,
Mar 17, 2026, 5:25:07 AM (4 days ago) Mar 17
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.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 identify and suppress specific
warning backtraces while executing test code. Support suppressing multiple
backtraces while at the same time limiting changes to generic code to the
absolute minimum.

Overview:
Patch#1 Introduces the suppression infrastructure.
Patch#2 Mitigate the impact at WARN*() sites.
Patch#3 Adds selftests to validate the functionality.
Patch#4 Demonstrates real-world usage in the DRM subsystem.
Patch#5 Documents the new API and usage guidelines.

Design Notes:
The objective is to suppress unwanted WARN*() generated messages.

Although most major architectures share common bug handling via `lib/bug.c`
and `report_bug()`, some minor or legacy architectures still rely on their
own platform-specific handling. This divergence must be considered in any
such feature. Additionally, a key challenge in implementing this feature is
the fragmentation of `WARN*()` messages emission: specific part in the
macro, common with BUG*() part in the exception handler.
As a result, any intervention to suppress the message must occur before the
illegal instruction.

Lessons from the Previous Attempt
In earlier iterations, suppression logic was added inside the
`__report_bug()` function to intercept WARN*() messages not producing
messages in the macro.
To implement the check in the bug handler code, two strategies were
considered:

* Strategy #1: Use `kallsyms` to infer the originating functionid, namely
a pointer to the function. Since in any case, the user interface relies
on function names, they must be translated in addresses at suppression-
time or at check-time.
Assuming to translate at suppression-time, the `kallsyms` subsystem needs
to be used to determine the symbol address from the name, and again to
produce the functionid from `bugaddr`. This approach proved unreliable
due to compiler-induced transformations such as inlining, cloning, and
code fragmentation. Attempts to preventing them is also unconvenient
because several `WARN()` sites are in functions intentionally declared
as `__always_inline`.

* Strategy #2: Store function name `__func__` in `struct bug_entry` in
the `__bug_table`. This implementation was used in the previous version.
However, `__func__` is a compiler-generated symbol, which complicates
relocation and linking in position-independent code. Workarounds such as
storing offsets from `.rodata` or embedding string literals directly into
the table would have significantly either increased complexity or
increase the __bug_table size.
Additionally, architectures not using the unified `BUG()` path would
still require ad-hoc handling. Because current WARN*() message production
strategy, a few WARN*() macros still need a check to suppress the part of
the message produced in the macro itself.

As a result, previous version proposed a per-macro solution, which offers
better control on where the suppression logic is applied.

For this iteration, the `__report_bug()` centralized approach was
revisited after the discussion in the previous version [1].
However, again this approach did not work because:
- Some warning output is generated directly in the macros before calling
the centralized functions (e.g., `__warn_printk()` in `__WARN_printf()`)
- Functions in the warning path like `warn_slowpath_fmt()` are marked
`__always_inline`, making it difficult to intercept early enough
- So, by the time `__report_bug()` is called, output has already been written
to the console, making suppression ineffective

Current Proposal: Check Directly in the `WARN()` Macros.
This avoids the need for function symbol resolution or ELF section
modification.
Suppression is implemented directly in the `WARN*()` macros.

A helper function, `__kunit_is_suppressed_warning()`, is used to determine
whether suppression applies. It is marked as `noinstr`, since some `WARN*()`
sites reside in non-instrumentable sections. The function uses a static
branch for the fast path check (which is `noinstr`-safe), and only calls
the actual suppression check (which uses `strcmp`, and is surrounded by
instrumentation_begin()/end() calls) when suppressions are active.
The list of suppressed warnings is protected with RCU (Read-Copy-Update)
to allow concurrent read access without locks.

To minimize runtime impact when no suppressions are active, static branching
is used via a static key (`kunit_suppress_warnings_key`). The branch is
automatically enabled when the first suppression starts and disabled when the
last suppression ends, tracked via an atomic counter. This keeps the suppression
check code out-of-line, reducing code bloat at each `WARN*()` site while
maintaining minimal runtime overhead in the common case (no active suppressions).

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.

[1] https://lore.kernel.org/all/CAGegRW76X8Fk_5qqOBw_aqBw...@mail.gmail.com/

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.

Alessandro Carminati (2):
bug/kunit: Core support for suppressing warning backtraces
bug/kunit: Suppressing warning backtraces reduced impact on WARN*()
sites

Guenter Roeck (3):
Add unit tests to verify that warning backtrace suppression works.
drm: Suppress intentional warning backtraces in scaling unit tests
kunit: Add documentation for warning backtrace suppression API

Documentation/dev-tools/kunit/usage.rst | 30 ++++++-
drivers/gpu/drm/tests/drm_rect_test.c | 16 ++++
include/asm-generic/bug.h | 48 +++++++----
include/kunit/bug.h | 62 ++++++++++++++
include/kunit/test.h | 1 +
lib/kunit/Kconfig | 9 ++
lib/kunit/Makefile | 9 +-
lib/kunit/backtrace-suppression-test.c | 105 ++++++++++++++++++++++++
lib/kunit/bug.c | 54 ++++++++++++
9 files changed, 316 insertions(+), 18 deletions(-)
create mode 100644 include/kunit/bug.h
create mode 100644 lib/kunit/backtrace-suppression-test.c
create mode 100644 lib/kunit/bug.c

--
2.34.1

---
Alessandro Carminati (2):
bug/kunit: Core support for suppressing warning backtraces
bug/kunit: Suppressing warning backtraces reduced impact on WARN*() sites

Guenter Roeck (3):
Add unit tests to verify that warning backtrace suppression works.
drm: Suppress intentional warning backtraces in scaling unit tests
kunit: Add documentation for warning backtrace suppression API

Documentation/dev-tools/kunit/usage.rst | 30 ++++++++-
drivers/gpu/drm/tests/drm_rect_test.c | 16 +++++
include/asm-generic/bug.h | 44 ++++++++-----
include/kunit/bug.h | 61 ++++++++++++++++++
include/kunit/test.h | 1 +
lib/kunit/Kconfig | 9 +++
lib/kunit/Makefile | 9 ++-
lib/kunit/backtrace-suppression-test.c | 109 ++++++++++++++++++++++++++++++++
lib/kunit/bug.c | 71 +++++++++++++++++++++
9 files changed, 332 insertions(+), 18 deletions(-)
---
base-commit: 80234b5ab240f52fa45d201e899e207b9265ef91
change-id: 20260312-kunit_add_support-2f35806b19dd

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

Albert Esteve

unread,
Mar 17, 2026, 5:25:12 AM (4 days ago) Mar 17
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.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 identify and suppress specific
warning backtraces while executing test code. Support suppressing multiple
backtraces while at the same time limiting changes to generic code to the
absolute minimum.

Implementation details:
Check suppression directly in the `WARN()` Macros.
This avoids the need for function symbol resolution or ELF section
modification.
Use unlikely() with KUNIT_IS_SUPPRESSED_WARNING() in the Macros
so the common case (no suppression) stays on the hot path,
improving performance and keeping the critical path smaller.

A helper function, `__kunit_is_suppressed_warning()`, is used to determine
whether suppression applies. It is marked as `noinstr`, since some
`WARN*()` sites reside in non-instrumentable sections. As it uses `strcmp`,
which is not `noinstr`-safe, its use is wrapped within
`__kunit_check_suppress()` and surounded by instrumentation_begin()/end()
calls.

The list of supressed warnings includes RCU protection.

The implementation is deliberately simple and avoids architecture-specific
optimizations to preserve portability.

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/asm-generic/bug.h | 44 ++++++++++++++++++++++------------
include/kunit/bug.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++
include/kunit/test.h | 1 +
lib/kunit/Kconfig | 9 +++++++
lib/kunit/Makefile | 6 +++--
lib/kunit/bug.c | 59 +++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 163 insertions(+), 17 deletions(-)

diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index 09e8eccee8ed9..7c766fde49a90 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -27,6 +27,7 @@
#endif /* WARN_CONDITION_STR */

#ifndef __ASSEMBLY__
+#include <kunit/bug.h>
#include <linux/panic.h>
#include <linux/printk.h>

@@ -71,9 +72,13 @@ struct bug_entry {
*/
#ifndef HAVE_ARCH_BUG
#define BUG() do { \
- printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
- barrier_before_unreachable(); \
- panic("BUG!"); \
+ if (!unlikely(KUNIT_IS_SUPPRESSED_WARNING(__func__))) { \
+ printk("BUG: failure at %s:%d/%s()!\n", __FILE__, \
+ __LINE__, __func__); \
+ barrier_before_unreachable(); \
+ panic("BUG!"); \
+ } \
+ __builtin_unreachable(); \
} while (0)
#endif

@@ -129,18 +134,23 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);

#if defined(__WARN_FLAGS) && !defined(__WARN_printf)
#define __WARN_printf(taint, arg...) do { \
- instrumentation_begin(); \
- __warn_printk(arg); \
- __WARN_FLAGS("", BUGFLAG_NO_CUT_HERE | BUGFLAG_TAINT(taint));\
- instrumentation_end(); \
+ if (!unlikely(KUNIT_IS_SUPPRESSED_WARNING(__func__))) { \
+ instrumentation_begin(); \
+ __warn_printk(arg); \
+ __WARN_FLAGS("", BUGFLAG_NO_CUT_HERE | \
+ BUGFLAG_TAINT(taint)); \
+ instrumentation_end(); \
+ } \
} while (0)
#endif

#ifndef __WARN_printf
-#define __WARN_printf(taint, arg...) do { \
- instrumentation_begin(); \
- warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \
- instrumentation_end(); \
+#define __WARN_printf(taint, arg...) do { \
+ if (!unlikely(KUNIT_IS_SUPPRESSED_WARNING(__func__))) { \
+ instrumentation_begin(); \
+ warn_slowpath_fmt(__FILE__, __LINE__, taint, arg); \
+ instrumentation_end(); \
+ } \
} while (0)
#endif

@@ -153,7 +163,8 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
#ifndef WARN_ON
#define WARN_ON(condition) ({ \
int __ret_warn_on = !!(condition); \
- if (unlikely(__ret_warn_on)) \
+ if (unlikely(__ret_warn_on) && \
+ !unlikely(KUNIT_IS_SUPPRESSED_WARNING(__func__))) \
__WARN(); \
unlikely(__ret_warn_on); \
})
@@ -170,7 +181,8 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);

#define WARN_TAINT(condition, taint, format...) ({ \
int __ret_warn_on = !!(condition); \
- if (unlikely(__ret_warn_on)) \
+ if (unlikely(__ret_warn_on) && \
+ !unlikely(KUNIT_IS_SUPPRESSED_WARNING(__func__))) \
__WARN_printf(taint, format); \
unlikely(__ret_warn_on); \
})
@@ -191,8 +203,10 @@ extern __printf(1, 2) void __warn_printk(const char *fmt, ...);
#else /* !CONFIG_BUG */
#ifndef HAVE_ARCH_BUG
#define BUG() do { \
- do {} while (1); \
- unreachable(); \
+ if (!unlikely(KUNIT_IS_SUPPRESSED_WARNING(__func__))) { \
+ do {} while (1); \
+ } \
+ unreachable(); \
} while (0)
#endif

diff --git a/include/kunit/bug.h b/include/kunit/bug.h
new file mode 100644
index 0000000000000..9a5cd226c2139
--- /dev/null
+++ b/include/kunit/bug.h
@@ -0,0 +1,61 @@
+/* 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>
+ */
+
+#ifndef _KUNIT_BUG_H
+#define _KUNIT_BUG_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+
+struct __suppressed_warning {
+ struct list_head node;
+ const char *function;
+ int counter;
+};
+
+void __kunit_start_suppress_warning(struct __suppressed_warning *warning);
+void __kunit_end_suppress_warning(struct __suppressed_warning *warning);
+bool __kunit_is_suppressed_warning(const char *function);
+
+#define KUNIT_DEFINE_SUPPRESSED_WARNING(func) \
+ struct __suppressed_warning __kunit_suppress_##func = \
+ { .function = __stringify(func), .counter = 0 }
+
+#define KUNIT_START_SUPPRESSED_WARNING(func) \
+ __kunit_start_suppress_warning(&__kunit_suppress_##func)
+
+#define KUNIT_END_SUPPRESSED_WARNING(func) \
+ __kunit_end_suppress_warning(&__kunit_suppress_##func)
+
+#define KUNIT_IS_SUPPRESSED_WARNING(func) \
+ __kunit_is_suppressed_warning(func)
+
+#define KUNIT_SUPPRESSED_WARNING_COUNT(func) \
+ (__kunit_suppress_##func.counter)
+
+#define KUNIT_SUPPRESSED_WARNING_COUNT_RESET(func) \
+ __kunit_suppress_##func.counter = 0
+
+#else /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+
+#define KUNIT_DEFINE_SUPPRESSED_WARNING(func)
+#define KUNIT_START_SUPPRESSED_WARNING(func)
+#define KUNIT_END_SUPPRESSED_WARNING(func)
+#define KUNIT_IS_SUPPRESSED_WARNING(func) ((void)(func), false)
+#define KUNIT_SUPPRESSED_WARNING_COUNT(func) ((void)(func), 0)
+#define KUNIT_SUPPRESSED_WARNING_COUNT_RESET(func)
+
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
+#endif /* __ASSEMBLY__ */
+#endif /* _KUNIT_BUG_H */
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 9cd1594ab697d..4ec07b3fa0204 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -10,6 +10,7 @@
#define _KUNIT_TEST_H

#include <kunit/assert.h>
+#include <kunit/bug.h>
#include <kunit/try-catch.h>

#include <linux/args.h>
diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig
index 498cc51e493dc..57527418fcf09 100644
--- a/lib/kunit/Kconfig
+++ b/lib/kunit/Kconfig
@@ -15,6 +15,15 @@ menuconfig KUNIT

if KUNIT

+config KUNIT_SUPPRESS_BACKTRACE
+ bool "KUnit - Enable backtrace suppression"
+ default y
+ help
+ Enable backtrace suppression for KUnit. If enabled, backtraces
+ generated intentionally by KUnit tests are suppressed. Disable
+ to reduce kernel image size if image size is more important than
+ suppression of backtraces generated by KUnit tests.
+
config KUNIT_DEBUGFS
bool "KUnit - Enable /sys/kernel/debug/kunit debugfs representation" if !KUNIT_ALL_TESTS
default KUNIT_ALL_TESTS
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 656f1fa35abcc..fe177ff3ebdef 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -16,8 +16,10 @@ ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
endif

-# KUnit 'hooks' are built-in even when KUnit is built as a module.
-obj-$(if $(CONFIG_KUNIT),y) += hooks.o
+# KUnit 'hooks' and bug handling are built-in even when KUnit is built
+# as a module.
+obj-$(if $(CONFIG_KUNIT),y) += hooks.o \
+ bug.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
new file mode 100644
index 0000000000000..53c98e225a895
--- /dev/null
+++ b/lib/kunit/bug.c
@@ -0,0 +1,59 @@
+// 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/bug.h>
+#include <linux/export.h>
+#include <linux/instrumentation.h>
+#include <linux/rculist.h>
+#include <linux/string.h>
+
+#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
+
+static LIST_HEAD(suppressed_warnings);
+
+void __kunit_start_suppress_warning(struct __suppressed_warning *warning)
+{
+ list_add_rcu(&warning->node, &suppressed_warnings);
+}
+EXPORT_SYMBOL_GPL(__kunit_start_suppress_warning);
+
+void __kunit_end_suppress_warning(struct __suppressed_warning *warning)
+{
+ list_del_rcu(&warning->node);
+ synchronize_rcu(); /* Wait for readers to finish */
+}
+EXPORT_SYMBOL_GPL(__kunit_end_suppress_warning);
+
+static bool __kunit_check_suppress(const char *function)
+{
+ struct __suppressed_warning *warning;
+
+ if (!function)
+ return false;
+
+ list_for_each_entry(warning, &suppressed_warnings, node) {
+ if (!strcmp(function, warning->function)) {
+ warning->counter++;
+ return true;
+ }
+ }
+ return false;
+}
+
+noinstr bool __kunit_is_suppressed_warning(const char *function)
+{
+ bool ret;
+
+ instrumentation_begin();
+ ret = __kunit_check_suppress(function);
+ instrumentation_end();
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__kunit_is_suppressed_warning);
+
+#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */

--
2.52.0

Albert Esteve

unread,
Mar 17, 2026, 5:25:20 AM (4 days ago) Mar 17
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Albert Esteve
From: Alessandro Carminati <acar...@redhat.com>

KUnit support is not consistently present across distributions, some
include it in their stock kernels, while others do not.
While both KUNIT and KUNIT_SUPPRESS_BACKTRACE can be considered debug
features, the fact that some distros ship with KUnit enabled means it's
important to minimize the runtime impact of this patch.

To that end, this patch uses static branching to minimize code size
and runtime overhead when no suppressions are active. In that case,
the static branch compiles to a single no-op instruction (5 bytes on
x86), avoiding any memory loads or branch prediction overhead. The
branch is automatically enabled when the first suppression starts and
disabled when the last suppression ends.

Signed-off-by: Alessandro Carminati <acar...@redhat.com>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/bug.c | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
index 53c98e225a895..9c2c4ee013d92 100644
--- a/lib/kunit/bug.c
+++ b/lib/kunit/bug.c
@@ -7,17 +7,25 @@
*/

#include <kunit/bug.h>
+#include <linux/atomic.h>
#include <linux/export.h>
#include <linux/instrumentation.h>
+#include <linux/jump_label.h>
#include <linux/rculist.h>
#include <linux/string.h>

#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE

static LIST_HEAD(suppressed_warnings);
+static atomic_t suppressed_symbols_cnt = ATOMIC_INIT(0);
+
+DEFINE_STATIC_KEY_FALSE(kunit_suppress_warnings_key);
+EXPORT_SYMBOL_GPL(kunit_suppress_warnings_key);

void __kunit_start_suppress_warning(struct __suppressed_warning *warning)
{
+ if (atomic_inc_return(&suppressed_symbols_cnt) == 1)
+ static_branch_enable(&kunit_suppress_warnings_key);
list_add_rcu(&warning->node, &suppressed_warnings);
}
EXPORT_SYMBOL_GPL(__kunit_start_suppress_warning);
@@ -26,6 +34,8 @@ void __kunit_end_suppress_warning(struct __suppressed_warning *warning)
{
list_del_rcu(&warning->node);
synchronize_rcu(); /* Wait for readers to finish */
+ if (atomic_dec_return(&suppressed_symbols_cnt) == 0)
+ static_branch_disable(&kunit_suppress_warnings_key);
}
EXPORT_SYMBOL_GPL(__kunit_end_suppress_warning);

@@ -49,6 +59,8 @@ noinstr bool __kunit_is_suppressed_warning(const char *function)
{
bool ret;

+ if (!static_branch_unlikely(&kunit_suppress_warnings_key))
+ return false;
instrumentation_begin();
ret = __kunit_check_suppress(function);
instrumentation_end();

--
2.52.0

Albert Esteve

unread,
Mar 17, 2026, 5:25:27 AM (4 days ago) Mar 17
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.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.

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>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
lib/kunit/Makefile | 3 +
lib/kunit/backtrace-suppression-test.c | 109 +++++++++++++++++++++++++++++++++
2 files changed, 112 insertions(+)

diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index fe177ff3ebdef..b2f2b8ada7b71 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -23,6 +23,9 @@ obj-$(if $(CONFIG_KUNIT),y) += hooks.o \

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
+ifeq ($(CONFIG_KUNIT_SUPPRESS_BACKTRACE),y)
+obj-$(CONFIG_KUNIT_TEST) += backtrace-suppression-test.o
+endif

# 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..524aecee0ee23
--- /dev/null
+++ b/lib/kunit/backtrace-suppression-test.c
@@ -0,0 +1,109 @@
+// 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>
+
+static void backtrace_suppression_test_warn_direct(struct kunit *test)
+{
+ KUNIT_DEFINE_SUPPRESSED_WARNING(backtrace_suppression_test_warn_direct);
+
+ KUNIT_START_SUPPRESSED_WARNING(backtrace_suppression_test_warn_direct);
+ WARN(1, "This backtrace should be suppressed");
+ KUNIT_END_SUPPRESSED_WARNING(backtrace_suppression_test_warn_direct);
+
+ KUNIT_EXPECT_EQ(test,
+ KUNIT_SUPPRESSED_WARNING_COUNT(backtrace_suppression_test_warn_direct), 1);
+}
+
+static void trigger_backtrace_warn(void)
+{
+ WARN(1, "This backtrace should be suppressed");
+}
+
+static void backtrace_suppression_test_warn_indirect(struct kunit *test)
+{
+ KUNIT_DEFINE_SUPPRESSED_WARNING(trigger_backtrace_warn);
+
+ KUNIT_START_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ trigger_backtrace_warn();
+ KUNIT_END_SUPPRESSED_WARNING(trigger_backtrace_warn);
+
+ KUNIT_EXPECT_EQ(test, KUNIT_SUPPRESSED_WARNING_COUNT(trigger_backtrace_warn), 1);
+}
+
+static void backtrace_suppression_test_warn_multi(struct kunit *test)
+{
+ KUNIT_DEFINE_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ KUNIT_DEFINE_SUPPRESSED_WARNING(backtrace_suppression_test_warn_multi);
+
+ KUNIT_START_SUPPRESSED_WARNING(backtrace_suppression_test_warn_multi);
+ KUNIT_START_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ WARN(1, "This backtrace should be suppressed");
+ trigger_backtrace_warn();
+ KUNIT_END_SUPPRESSED_WARNING(trigger_backtrace_warn);
+ KUNIT_END_SUPPRESSED_WARNING(backtrace_suppression_test_warn_multi);
+
+ KUNIT_EXPECT_EQ(test,
+ KUNIT_SUPPRESSED_WARNING_COUNT(backtrace_suppression_test_warn_multi), 1);
+ KUNIT_EXPECT_EQ(test,
+ KUNIT_SUPPRESSED_WARNING_COUNT(trigger_backtrace_warn), 1);
+}
+
+static void backtrace_suppression_test_warn_on_direct(struct kunit *test)
+{
+ KUNIT_DEFINE_SUPPRESSED_WARNING(backtrace_suppression_test_warn_on_direct);
+
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE) && !IS_ENABLED(CONFIG_KALLSYMS))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE or CONFIG_KALLSYMS");
+
+ KUNIT_START_SUPPRESSED_WARNING(backtrace_suppression_test_warn_on_direct);
+ WARN_ON(1);
+ KUNIT_END_SUPPRESSED_WARNING(backtrace_suppression_test_warn_on_direct);
+
+ KUNIT_EXPECT_EQ(test,
+ KUNIT_SUPPRESSED_WARNING_COUNT(
+ backtrace_suppression_test_warn_on_direct), 1);
+}
+
+static void trigger_backtrace_warn_on(void)
+{
+ WARN_ON(1);
+}
+
+static void backtrace_suppression_test_warn_on_indirect(struct kunit *test)
+{
+ KUNIT_DEFINE_SUPPRESSED_WARNING(trigger_backtrace_warn_on);
+
+ if (!IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE))
+ kunit_skip(test, "requires CONFIG_DEBUG_BUGVERBOSE");
+
+ KUNIT_START_SUPPRESSED_WARNING(trigger_backtrace_warn_on);
+ trigger_backtrace_warn_on();
+ KUNIT_END_SUPPRESSED_WARNING(trigger_backtrace_warn_on);
+
+ KUNIT_EXPECT_EQ(test, KUNIT_SUPPRESSED_WARNING_COUNT(trigger_backtrace_warn_on), 1);
+}
+
+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),
+ {}
+};
+
+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.52.0

Albert Esteve

unread,
Mar 17, 2026, 5:25:37 AM (4 days ago) Mar 17
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.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>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
drivers/gpu/drm/tests/drm_rect_test.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/drivers/gpu/drm/tests/drm_rect_test.c b/drivers/gpu/drm/tests/drm_rect_test.c
index 17e1f34b76101..867845e7d5ab6 100644
--- a/drivers/gpu/drm/tests/drm_rect_test.c
+++ b/drivers/gpu/drm/tests/drm_rect_test.c
@@ -406,22 +406,38 @@ 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)
{
+ KUNIT_DEFINE_SUPPRESSED_WARNING(drm_calc_scale);
const struct drm_rect_scale_case *params = test->param_value;
int scaling_factor;

+ /*
+ * 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_START_SUPPRESSED_WARNING(drm_calc_scale);
scaling_factor = drm_rect_calc_hscale(&params->src, &params->dst,
params->min_range, params->max_range);
+ KUNIT_END_SUPPRESSED_WARNING(drm_calc_scale);

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

static void drm_test_rect_calc_vscale(struct kunit *test)
{
+ KUNIT_DEFINE_SUPPRESSED_WARNING(drm_calc_scale);
const struct drm_rect_scale_case *params = test->param_value;
int scaling_factor;

+ /*
+ * 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_START_SUPPRESSED_WARNING(drm_calc_scale);
scaling_factor = drm_rect_calc_vscale(&params->src, &params->dst,
params->min_range, params->max_range);
+ KUNIT_END_SUPPRESSED_WARNING(drm_calc_scale);

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

--
2.52.0

Albert Esteve

unread,
Mar 17, 2026, 5:25:48 AM (4 days ago) Mar 17
to Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.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>
Signed-off-by: Albert Esteve <aes...@redhat.com>
---
Documentation/dev-tools/kunit/usage.rst | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index ebd06f5ea4550..f1f8e2619d2d6 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -157,6 +157,34 @@ 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 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.
+
+Such backtraces can be suppressed. To suppress a backtrace in some_function(),
+use the following code.
+
+.. code-block:: c
+
+ static void some_test(struct kunit *test)
+ {
+ DEFINE_SUPPRESSED_WARNING(some_function);
+
+ KUNIT_START_SUPPRESSED_WARNING(some_function);
+ trigger_backtrace();
+ KUNIT_END_SUPPRESSED_WARNING(some_function);
+ }
+
+SUPPRESSED_WARNING_COUNT() returns the number of suppressed backtraces. If the
+suppressed backtrace was triggered on purpose, this can be used to check if
+the backtrace was actually triggered.
+
+.. code-block:: c
+
+ KUNIT_EXPECT_EQ(test, SUPPRESSED_WARNING_COUNT(some_function), 1);

Test Suites
~~~~~~~~~~~
@@ -1211,4 +1239,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.52.0

Dan Carpenter

unread,
Mar 17, 2026, 6:03:22 AM (4 days ago) Mar 17
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, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Simona Vetter
I think this is great to suppress some warnings, and I already ACKed
this patchset. But we're still going to have some warnings where the
warning is the whole point of the test.

It would be great if marked these somehow:
1) At minimum we should mark them so people seeing the warning know it's
intentional. "Intentional Stack Trace". I've sent at least one patch
to add that printk before the stack trace but it was ignored. We could
do this piecemeal.

2) It would be nice if the print was standardized enough so CI systems
could automatically filter it out.

regards,
dan carpenter

Vlastimil Babka (SUSE)

unread,
Mar 17, 2026, 7:20:35 AM (4 days ago) Mar 17
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, Peter Zijlstra, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Simona Vetter
Discussion with PeterZ, who is not CC'd here? (did it now for my reply).

> However, again this approach did not work because:
> - Some warning output is generated directly in the macros before calling
> the centralized functions (e.g., `__warn_printk()` in `__WARN_printf()`)
> - Functions in the warning path like `warn_slowpath_fmt()` are marked
> `__always_inline`, making it difficult to intercept early enough
> - So, by the time `__report_bug()` is called, output has already been written
> to the console, making suppression ineffective
>
> Current Proposal: Check Directly in the `WARN()` Macros.
> This avoids the need for function symbol resolution or ELF section
> modification.
> Suppression is implemented directly in the `WARN*()` macros.

So does that bloat every warn/bug site (as Peter objected to) or not?
And is it compatible with x86? I see you modify include/asm-generic/bug.h
but x86 has its own version of e.g. __WARN_printf ?

Peter Zijlstra

unread,
Mar 17, 2026, 7:30:33 AM (4 days ago) Mar 17
to Vlastimil Babka (SUSE), Albert Esteve, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Simona Vetter
On Tue, Mar 17, 2026 at 12:20:26PM +0100, Vlastimil Babka (SUSE) wrote:
> > For this iteration, the `__report_bug()` centralized approach was
> > revisited after the discussion in the previous version [1].
>
> Discussion with PeterZ, who is not CC'd here? (did it now for my reply).
>
> > However, again this approach did not work because:
> > - Some warning output is generated directly in the macros before calling
> > the centralized functions (e.g., `__warn_printk()` in `__WARN_printf()`)
> > - Functions in the warning path like `warn_slowpath_fmt()` are marked
> > `__always_inline`, making it difficult to intercept early enough
> > - So, by the time `__report_bug()` is called, output has already been written
> > to the console, making suppression ineffective
> >
> > Current Proposal: Check Directly in the `WARN()` Macros.
> > This avoids the need for function symbol resolution or ELF section
> > modification.
> > Suppression is implemented directly in the `WARN*()` macros.
>
> So does that bloat every warn/bug site (as Peter objected to) or not?
> And is it compatible with x86? I see you modify include/asm-generic/bug.h
> but x86 has its own version of e.g. __WARN_printf ?

Yeah, they done it all wrong again :-(

This should be pushed inside __report_bug() through __WARN_printf with a
new BUGFLAG thing.

So NAK from me on this -- again!

Guenter Roeck

unread,
Mar 17, 2026, 10:55:45 AM (4 days ago) Mar 17
to Dan Carpenter, Albert Esteve, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Kees Cook, Linux Kernel Functional Testing, Maíra Canal, Simona Vetter
On 3/17/26 03:03, Dan Carpenter wrote:
> I think this is great to suppress some warnings, and I already ACKed
> this patchset. But we're still going to have some warnings where the
> warning is the whole point of the test.
>

Maybe my memory defeats me, but if I recall correctly the original patch
set counted the skipped warnings. I don't see why a to-be-parsed message
would be necessary or add value over that.

Guenter

Guenter Roeck

unread,
Mar 17, 2026, 11:02:48 AM (4 days ago) Mar 17
to Peter Zijlstra, Vlastimil Babka (SUSE), Albert Esteve, Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Kees Cook, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Simona Vetter
That would require another set of WARN macros, and after (or if) accepted
negotiations with each of the owners of the WARNing code to use the new
macros. Trying to do this would just trigger another set of objections.

Given that, but for other reasons,

> So NAK from me on this -- again!

I agree.

Guenter

Albert Esteve

unread,
Mar 18, 2026, 5:25:18 AM (3 days ago) Mar 18
to Peter Zijlstra, Vlastimil Babka (SUSE), Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Simona Vetter
Hi Peter,

On Tue, Mar 17, 2026 at 12:33 PM Peter Zijlstra <pet...@infradead.org> wrote:
>
> On Tue, Mar 17, 2026 at 12:20:26PM +0100, Vlastimil Babka (SUSE) wrote:
> > > For this iteration, the `__report_bug()` centralized approach was
> > > revisited after the discussion in the previous version [1].
> >
> > Discussion with PeterZ, who is not CC'd here? (did it now for my reply).

(Sorry not to put you on CC, I thought auto-cc would pick you up already)

> >
> > > However, again this approach did not work because:
> > > - Some warning output is generated directly in the macros before calling
> > > the centralized functions (e.g., `__warn_printk()` in `__WARN_printf()`)
> > > - Functions in the warning path like `warn_slowpath_fmt()` are marked
> > > `__always_inline`, making it difficult to intercept early enough
> > > - So, by the time `__report_bug()` is called, output has already been written
> > > to the console, making suppression ineffective
> > >
> > > Current Proposal: Check Directly in the `WARN()` Macros.
> > > This avoids the need for function symbol resolution or ELF section
> > > modification.
> > > Suppression is implemented directly in the `WARN*()` macros.
> >
> > So does that bloat every warn/bug site (as Peter objected to) or not?
> > And is it compatible with x86? I see you modify include/asm-generic/bug.h
> > but x86 has its own version of e.g. __WARN_printf ?
>
> Yeah, they done it all wrong again :-(
>
> This should be pushed inside __report_bug() through __WARN_printf with a
> new BUGFLAG thing.

Thanks for the specific suggestion to use BUGFLAG, but I do not think
this is what we need. These flags seem to be used statically, but the
goal is to enable and disable these warning suppressions dynamically
in the tests. Happy to be corrected if I missed something.

I want to highlight that I did not bluntly ignore your point in the
last version. I thoroughly read the discussions there, and as I
mentioned in the cover letter, I tried to move away from the per-macro
approach. However, while doing that, I encountered the same issues
Alessandro found in the last iteration. I’ll address that below, but I
decided to give this approach one more try and tackle the raised
drawbacks.

The main concern was the increased size of the code emitted by the
WARN*() macros, which was a fair point. I tried to alleviate this with
a static key and branching, and addressed other concerns by using RCU
protection on the list for thread safety. I think it was worth a try,
as all options seemed to have their own tradeoffs. However, if this
option remains unacceptable even after trying to diminish its
drawbacks, I am happy to focus on finding the best solution for the
centralised approach, and keeping the discussion constructive.

So back to my test on this. Alessandro detailed two strategies in the
last version, one of them (storing the function name in `struct
bug_entry`) was already used and discarded in older iterations of this
series. So let's focus on the other strategy, using 'kallsyms`. Let's
assume we still store the function name when registering a new symbol
to suppress. Otherwise, we might need to check address ranges to
ensure bugaddr is within the function's scope, which sounds trickier?
With `kallsyms` we can infer the originating functionid. But this
approach works unreliably with compiler-induced transformations (e.g.,
inlining, cloning, code fragmentation). And we still cannot prevent
all output. Additionally, we would need to prevent prints in
`warn_slowpath_fmt()`. There may be other `printk`s embedded in the
macros, but let's focus on suppressing all warnings as a best effort.
It would already improve the quality of life for testers.

Considering these remaining issues, I managed to create a centralised
proposal. Please find the main changes at the bottom of this message.

But again, even with these, the solution remains unreliable. We can
mitigate this by registering the test name on the suppression list (at
least, I can make the new test in this series pass with that). Not
ideal, but we could mention it in the documentation. Something like
"Suppression is matched by the function where the warning is reported.
If the warning is triggered from a helper (or the compiler inlines it
into the test), that function name may differ. In that case, register
and start suppression for both the test and the helper so the test
passes regardless of inlining."

Would that be a more acceptable solution? Is there a better option I
am not seeing?

BR,
Albert.

diff --git a/kernel/panic.c b/kernel/panic.c
index c78600212b6c1..3cb004a7803f4 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/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_at((unsigned
long)__builtin_return_address(0)))
+ return;
+
+ rcu = warn_rcu_enter();
+
pr_warn(CUT_HERE);

if (!fmt) {

diff --git a/lib/bug.c b/lib/bug.c
index 623c467a8b76c..e90b038d9225e 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/bug.h>

extern struct bug_entry __start___bug_table[], __stop___bug_table[];

@@ -223,6 +224,9 @@ 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;

+ if (warning && __kunit_is_suppressed_warning_at(bugaddr))
+ return BUG_TRAP_TYPE_WARN;
+
if (warning && once) {
if (done)
return BUG_TRAP_TYPE_WARN;

diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
index 9c2c4ee013d92..13ffddb044636 100644
--- a/lib/kunit/bug.c
+++ b/lib/kunit/bug.c
@@ -11,6 +11,7 @@
#include <linux/export.h>
#include <linux/instrumentation.h>
#include <linux/jump_label.h>
+#include <linux/kallsyms.h>
#include <linux/rculist.h>
#include <linux/string.h>

@@ -68,4 +69,16 @@ noinstr bool __kunit_is_suppressed_warning(const
char *function)
}
EXPORT_SYMBOL_GPL(__kunit_is_suppressed_warning);

+bool __kunit_is_suppressed_warning_at(unsigned long addr)
+{
+ char buf[KSYM_SYMBOL_LEN];
+
+ if (!static_branch_unlikely(&kunit_suppress_warnings_key))
+ return false;
+ if (sprint_symbol_no_offset(buf, addr) <= 0)
+ return false;
+ return __kunit_check_suppress(buf);
+}
+EXPORT_SYMBOL_GPL(__kunit_is_suppressed_warning_at);
+
#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */

Peter Zijlstra

unread,
Mar 18, 2026, 7:51:44 AM (3 days ago) Mar 18
to Albert Esteve, Vlastimil Babka (SUSE), Arnd Bergmann, Brendan Higgins, David Gow, Rae Moar, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter, Jonathan Corbet, Shuah Khan, linux-...@vger.kernel.org, linux...@vger.kernel.org, linux-k...@vger.kernel.org, kuni...@googlegroups.com, dri-...@lists.freedesktop.org, work...@vger.kernel.org, linu...@vger.kernel.org, Alessandro Carminati, Guenter Roeck, Kees Cook, Linux Kernel Functional Testing, Dan Carpenter, Maíra Canal, Simona Vetter
On Wed, Mar 18, 2026 at 10:25:00AM +0100, Albert Esteve wrote:

> So back to my test on this. Alessandro detailed two strategies in the
> last version, one of them (storing the function name in `struct
> bug_entry`) was already used and discarded in older iterations of this
> series. So let's focus on the other strategy, using 'kallsyms`. Let's
> assume we still store the function name when registering a new symbol
> to suppress. Otherwise, we might need to check address ranges to
> ensure bugaddr is within the function's scope, which sounds trickier?

> With `kallsyms` we can infer the originating functionid. But this
> approach works unreliably with compiler-induced transformations (e.g.,
> inlining, cloning, code fragmentation). And we still cannot prevent
> all output.

> Additionally, we would need to prevent prints in
> `warn_slowpath_fmt()`. There may be other `printk`s embedded in the
> macros, but let's focus on suppressing all warnings as a best effort.
> It would already improve the quality of life for testers.

warn_slowpath_fmt() should be completely unused on x86, s390 and
possibly others. But sure.

> Considering these remaining issues, I managed to create a centralised
> proposal. Please find the main changes at the bottom of this message.
>
> But again, even with these, the solution remains unreliable. We can
> mitigate this by registering the test name on the suppression list (at
> least, I can make the new test in this series pass with that). Not
> ideal, but we could mention it in the documentation. Something like
> "Suppression is matched by the function where the warning is reported.
> If the warning is triggered from a helper (or the compiler inlines it
> into the test), that function name may differ. In that case, register
> and start suppression for both the test and the helper so the test
> passes regardless of inlining."
>
> Would that be a more acceptable solution? Is there a better option I
> am not seeing?

Yes, definitely. This is the sort of thing I was aiming for.

Other option would be the __FILE__ and __LINE__ data, you can match
uniquely against that and not worry about the compiler transforms.
Perhaps less user friendly as a function identifier, but meh, this isn't
aimed at users anyway, but kernel devs.

The kunit userspace could even scrape the __FILE__ and __LINE__ from the
kernel it was compiled against if it so wants. It should be simple
enough to write a tool that takes the source and a function name and
outputs the relevant __FILE__ and __LINE__ thingies. All you need is a
very rudimentary C parser and we've got a ton of those around.
I suppose there's a question here if a suppressed warn counts towards
the once logic below.

> if (warning && once) {
> if (done)
> return BUG_TRAP_TYPE_WARN;
>
> diff --git a/lib/kunit/bug.c b/lib/kunit/bug.c
> index 9c2c4ee013d92..13ffddb044636 100644
> --- a/lib/kunit/bug.c
> +++ b/lib/kunit/bug.c
> @@ -11,6 +11,7 @@
> #include <linux/export.h>
> #include <linux/instrumentation.h>
> #include <linux/jump_label.h>
> +#include <linux/kallsyms.h>
> #include <linux/rculist.h>
> #include <linux/string.h>
>
> @@ -68,4 +69,16 @@ noinstr bool __kunit_is_suppressed_warning(const
> char *function)
> }
> EXPORT_SYMBOL_GPL(__kunit_is_suppressed_warning);
>
> +bool __kunit_is_suppressed_warning_at(unsigned long addr)
> +{
> + char buf[KSYM_SYMBOL_LEN];
> +
> + if (!static_branch_unlikely(&kunit_suppress_warnings_key))
> + return false;

At this point you don't need this to be a static_branch either, both
callers are already in warn slowpaths and nobody cares.

> + if (sprint_symbol_no_offset(buf, addr) <= 0)
> + return false;
> + return __kunit_check_suppress(buf);
> +}
> +EXPORT_SYMBOL_GPL(__kunit_is_suppressed_warning_at);

I don't think you need this export, both callers are definitely
in-kernel.
Reply all
Reply to author
Forward
0 new messages