On Mon, Apr 20, 2026 at 02:28:03PM +0200, Albert Esteve wrote: > From: Alessandro Carminati <[email protected]> > > 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 <[email protected]> > + * Copyright (C) 2024 Guenter Roeck <[email protected]> > + */ > + > +#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 <[email protected]> > + * Copyright (C) 2024 Guenter Roeck <[email protected]> > + */ > + > +#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 >

