Re: [PATCH v11 2/4] kunit: Add backtrace suppression self-tests
Albert Esteve <[email protected]>
| Newsgroups | org.kernel.vger.workflows,org.freedesktop.lists.dri-devel,org.infradead.lists.linux-riscv,org.kernel.vger.linux-arch,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest |
|---|---|
| Message-ID | <CADSE00KxDcyvw5MzRzrY0haZze8=9Npb8W8SwsxR+wjcS0R9pw@mail.gmail.com> |
On Thu, May 14, 2026 at 1:07 PM Albert Esteve <[email protected]> wrote: > > From: Guenter Roeck <[email protected]> > > 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 <[email protected]> > Acked-by: Dan Carpenter <[email protected]> > Reviewed-by: Kees Cook <[email protected]> > Signed-off-by: Guenter Roeck <[email protected]> > Signed-off-by: Alessandro Carminati <[email protected]> > Reviewed-by: David Gow <[email protected]> > Signed-off-by: Albert Esteve <[email protected]> > --- > 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 <[email protected]> > + */ > + > +#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"); 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. > + > + 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"); > + 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); > + 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; > + > + 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); > + } > + > + 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 >