mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-06-21 15:43:21 +02:00
bbc960d009
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. Link: https://lore.kernel.org/r/20260514-kunit_add_support-v11-2-b36a530a6d8f@redhat.com Tested-by: Linux Kernel Functional Testing <lkft@linaro.org> Acked-by: Dan Carpenter <dan.carpenter@linaro.org> Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Alessandro Carminati <acarmina@redhat.com> Reviewed-by: David Gow <david@davidgow.net> Signed-off-by: Albert Esteve <aesteve@redhat.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
199 lines
5.3 KiB
C
199 lines
5.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* KUnit test for suppressing warning tracebacks.
|
|
*
|
|
* Copyright (C) 2024, Guenter Roeck
|
|
* Author: Guenter Roeck <linux@roeck-us.net>
|
|
*/
|
|
|
|
#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");
|
|
|
|
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");
|