assert that `debug_locks` are on prior to running a kunit testcase and assert that `debug_locks` has not been turned off while the test has run. The assertions are gated behind `CONFIG_KUNIT_FAIL_TEST_ON_LOCKDEP=y`, if it is not enabled, writing and running kunit test cases that trigger a lockdep warning does _not_ fail the test if the test itself passes. If enabled, it marks any test that triggers a lockdep warning as failed, and forcefully skips all proceeding test cases, implying that the kernel is now seen as unreliable since `debug_locks` are off. Failing a test when lockdep reports a problem is useful for any unit test that takes locks, locking bugs are caught by the test that triggered them, instead of being silently logged and missed.
Signed-off-by: Malte Wechter <[email protected]> --- lib/kunit/test.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 0e3ed457daca..1292a0799c33 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -6,6 +6,7 @@ * Author: Brendan Higgins <[email protected]> */ +#include <linux/debug_locks.h> #include <kunit/resource.h> #include <kunit/test.h> #include <kunit/test-bug.h> @@ -80,6 +81,29 @@ void __printf(3, 4) __kunit_fail_current_test_impl(const char *file, int line, c kunit_kfree(current->kunit_test, buffer); } +/* + * Fails current test if test turns off debug_locks. + */ +static void kunit_check_lockdep(struct kunit *test, int prev_debug_locks) +{ + if (IS_ENABLED(CONFIG_KUNIT_FAIL_TEST_ON_LOCKDEP) && !debug_locks && prev_debug_locks) { + kunit_err(test, "Test triggered a lockdep warning"); + kunit_set_failure(test); + } +} + +/* + * Skip a KUnit test if debug_locks is turned off by lockdep. + */ +static void kunit_skip_if_lockdep(struct kunit *test, struct kunit_suite *suite) +{ + if (IS_ENABLED(CONFIG_KUNIT_FAIL_TEST_ON_LOCKDEP) && !debug_locks) { + suite->forced_skip = true; + test->forced_skip = true; + kunit_skip(test, "force skip: debug_locks are off"); + } +} + /* * Enable KUnit tests to run. */ @@ -462,6 +486,8 @@ static void kunit_run_case_internal(struct kunit *test, { struct timespec64 start, end; + kunit_skip_if_lockdep(test, suite); + if (suite->init) { int ret; @@ -512,6 +538,7 @@ static void kunit_try_run_case(void *data) struct kunit *test = ctx->test; struct kunit_suite *suite = ctx->suite; struct kunit_case *test_case = ctx->test_case; + int debug_locks_snapshot = debug_locks; current->kunit_test = test; @@ -521,6 +548,8 @@ static void kunit_try_run_case(void *data) * thread will resume control and handle any necessary clean up. */ kunit_run_case_internal(test, suite, test_case); + + kunit_check_lockdep(test, debug_locks_snapshot); } static void kunit_try_run_case_cleanup(void *data) -- 2.54.0

