On Fri, May 15, 2026 at 2:30 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. >
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(). > 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 | 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 <[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"); > + > + 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) > +{ > + struct kunit_suppressed_warning *sw1, *sw2; > + > + if (!IS_ENABLED(CONFIG_BUG)) > + kunit_skip(test, "requires CONFIG_BUG"); > + > + 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 >

