Re: [PATCH v7 1/5] bug/kunit: Core support for suppressing warning backtraces

From: Peter Zijlstra

Date: Mon Apr 20 2026 - 11:34:56 EST


On Mon, Apr 20, 2026 at 02:28:03PM +0200, Albert Esteve wrote:
> From: Alessandro Carminati <acarmina@xxxxxxxxxx>
>
> 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:
> Suppression is checked at two points in the warning path:
> - In warn_slowpath_fmt(), the check runs before any output, fully
> suppressing both message and backtrace.
> - In __report_bug(), the check runs before __warn() is called,
> suppressing the backtrace and stack dump. Note that on this path,
> the WARN() format message may still appear in the kernel log since
> __warn_printk() runs before the trap that enters __report_bug().

This is for architectures that implement __WARN_FLAGS but not
__WARN_printf right? (which is arm64, loongarch, parisc, powerpc, riscv,
sh, afaict). ARM64 should eventually get __WARN_printf, but other than
that this should be fixable by moving __WARN_FLAGS() into
__warn_printk() or so. This is the only __warn_printk() user anyway.


> diff --git a/include/kunit/bug.h b/include/kunit/bug.h
> new file mode 100644
> index 0000000000000..e52c9d21d9fe6
> --- /dev/null
> +++ b/include/kunit/bug.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * KUnit helpers for backtrace suppression
> + *
> + * Copyright (C) 2025 Alessandro Carminati <acarmina@xxxxxxxxxx>
> + * Copyright (C) 2024 Guenter Roeck <linux@xxxxxxxxxxxx>
> + */
> +
> +#ifndef _KUNIT_BUG_H
> +#define _KUNIT_BUG_H
> +
> +#ifndef __ASSEMBLY__
> +
> +#include <linux/kconfig.h>
> +
> +struct kunit;
> +
> +#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
> +
> +#include <linux/types.h>
> +
> +struct task_struct;
> +
> +struct __suppressed_warning {
> + struct list_head node;
> + struct task_struct *task;
> + int counter;
> +};
> +
> +struct __suppressed_warning *
> +__kunit_start_suppress_warning(struct kunit *test);
> +void __kunit_end_suppress_warning(struct kunit *test,
> + struct __suppressed_warning *warning);
> +int __kunit_suppressed_warning_count(struct __suppressed_warning *warning);
> +bool __kunit_is_suppressed_warning(void);
> +
> +#define KUNIT_START_SUPPRESSED_WARNING(test) \
> + struct __suppressed_warning *__kunit_suppress = \
> + __kunit_start_suppress_warning(test)
> +
> +#define KUNIT_END_SUPPRESSED_WARNING(test) \
> + __kunit_end_suppress_warning(test, __kunit_suppress)

We have __cleanup for this?

> +
> +#define KUNIT_SUPPRESSED_WARNING_COUNT() \
> + __kunit_suppressed_warning_count(__kunit_suppress)
> +
> +#else /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
> +
> +#define KUNIT_START_SUPPRESSED_WARNING(test)
> +#define KUNIT_END_SUPPRESSED_WARNING(test)
> +#define KUNIT_SUPPRESSED_WARNING_COUNT() 0
> +static inline bool __kunit_is_suppressed_warning(void) { return false; }
> +
> +#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
> +#endif /* __ASSEMBLY__ */
> +#endif /* _KUNIT_BUG_H */

> 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..356c8a5928828
> --- /dev/null
> +++ b/lib/kunit/bug.c
> @@ -0,0 +1,84 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * KUnit helpers for backtrace suppression
> + *
> + * Copyright (C) 2025 Alessandro Carminati <acarmina@xxxxxxxxxx>
> + * Copyright (C) 2024 Guenter Roeck <linux@xxxxxxxxxxxx>
> + */
> +
> +#include <kunit/bug.h>
> +#include <kunit/resource.h>
> +#include <linux/export.h>
> +#include <linux/rculist.h>
> +#include <linux/sched.h>
> +
> +#ifdef CONFIG_KUNIT_SUPPRESS_BACKTRACE
> +
> +static LIST_HEAD(suppressed_warnings);
> +
> +static void __kunit_suppress_warning_remove(struct __suppressed_warning *warning)
> +{
> + list_del_rcu(&warning->node);
> + synchronize_rcu(); /* Wait for readers to finish */
> +}
> +
> +KUNIT_DEFINE_ACTION_WRAPPER(__kunit_suppress_warning_cleanup,
> + __kunit_suppress_warning_remove,
> + struct __suppressed_warning *);
> +
> +struct __suppressed_warning *
> +__kunit_start_suppress_warning(struct kunit *test)
> +{
> + struct __suppressed_warning *warning;
> + int ret;
> +
> + warning = kunit_kzalloc(test, sizeof(*warning), GFP_KERNEL);
> + if (!warning)
> + return NULL;
> +
> + warning->task = current;
> + list_add_rcu(&warning->node, &suppressed_warnings);

What if anything serializes this global list?

> +
> + ret = kunit_add_action_or_reset(test,
> + __kunit_suppress_warning_cleanup,
> + warning);
> + if (ret)
> + return NULL;
> +
> + return warning;
> +}
> +EXPORT_SYMBOL_GPL(__kunit_start_suppress_warning);
> +
> +void __kunit_end_suppress_warning(struct kunit *test,
> + struct __suppressed_warning *warning)
> +{
> + if (!warning)
> + return;
> + kunit_release_action(test, __kunit_suppress_warning_cleanup, warning);
> +}
> +EXPORT_SYMBOL_GPL(__kunit_end_suppress_warning);
> +
> +int __kunit_suppressed_warning_count(struct __suppressed_warning *warning)
> +{
> + return warning ? warning->counter : 0;
> +}
> +EXPORT_SYMBOL_GPL(__kunit_suppressed_warning_count);
> +
> +bool __kunit_is_suppressed_warning(void)
> +{
> + struct __suppressed_warning *warning;
> +
> + rcu_read_lock();
> + list_for_each_entry_rcu(warning, &suppressed_warnings, node) {
> + if (warning->task == current) {
> + warning->counter++;
> + rcu_read_unlock();
> + return true;
> + }
> + }
> + rcu_read_unlock();
> +
> + return false;
> +}
> +
> +#endif /* CONFIG_KUNIT_SUPPRESS_BACKTRACE */
>
> --
> 2.52.0
>