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


Reply via email to