Implement CONFIG_DEPT_UNIT_TEST introducing a kernel module that runs
basic unit test for dept.

Signed-off-by: Byungchul Park <byungc...@sk.com>
---
 include/linux/dept_unit_test.h     |  67 +++++++++++
 kernel/dependency/Makefile         |   1 +
 kernel/dependency/dept.c           |  12 ++
 kernel/dependency/dept_unit_test.c | 173 +++++++++++++++++++++++++++++
 lib/Kconfig.debug                  |  12 ++
 5 files changed, 265 insertions(+)
 create mode 100644 include/linux/dept_unit_test.h
 create mode 100644 kernel/dependency/dept_unit_test.c

diff --git a/include/linux/dept_unit_test.h b/include/linux/dept_unit_test.h
new file mode 100644
index 000000000000..7612b4e97e69
--- /dev/null
+++ b/include/linux/dept_unit_test.h
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DEPT unit test
+ *
+ * Started by Byungchul Park <max.byungchul.p...@gmail.com>:
+ *
+ *  Copyright (c) 2025 SK hynix, Inc., Byungchul Park
+ */
+
+#ifndef __LINUX_DEPT_UNIT_TEST_H
+#define __LINUX_DEPT_UNIT_TEST_H
+
+#if defined(CONFIG_DEPT_UNIT_TEST) || defined(CONFIG_DEPT_UNIT_TEST_MODULE)
+struct dept_ut {
+       bool circle_detected;
+       bool recover_circle_detected;
+
+       int ecxt_stack_total_cnt;
+       int wait_stack_total_cnt;
+       int evnt_stack_total_cnt;
+       int ecxt_stack_valid_cnt;
+       int wait_stack_valid_cnt;
+       int evnt_stack_valid_cnt;
+};
+
+extern struct dept_ut dept_ut_results;
+
+static inline void dept_ut_circle_detect(void)
+{
+       dept_ut_results.circle_detected = true;
+}
+static inline void dept_ut_recover_circle_detect(void)
+{
+       dept_ut_results.recover_circle_detected = true;
+}
+static inline void dept_ut_ecxt_stack_account(bool valid)
+{
+       dept_ut_results.ecxt_stack_total_cnt++;
+
+       if (valid)
+               dept_ut_results.ecxt_stack_valid_cnt++;
+}
+static inline void dept_ut_wait_stack_account(bool valid)
+{
+       dept_ut_results.wait_stack_total_cnt++;
+
+       if (valid)
+               dept_ut_results.wait_stack_valid_cnt++;
+}
+static inline void dept_ut_evnt_stack_account(bool valid)
+{
+       dept_ut_results.evnt_stack_total_cnt++;
+
+       if (valid)
+               dept_ut_results.evnt_stack_valid_cnt++;
+}
+#else
+struct dept_ut {};
+
+#define dept_ut_circle_detect() do { } while (0)
+#define dept_ut_recover_circle_detect() do { } while (0)
+#define dept_ut_ecxt_stack_account(v) do { } while (0)
+#define dept_ut_wait_stack_account(v) do { } while (0)
+#define dept_ut_evnt_stack_account(v) do { } while (0)
+
+#endif
+#endif /* __LINUX_DEPT_UNIT_TEST_H */
diff --git a/kernel/dependency/Makefile b/kernel/dependency/Makefile
index 92f165400187..fc584ca87124 100644
--- a/kernel/dependency/Makefile
+++ b/kernel/dependency/Makefile
@@ -2,3 +2,4 @@
 
 obj-$(CONFIG_DEPT) += dept.o
 obj-$(CONFIG_DEPT) += dept_proc.o
+obj-$(CONFIG_DEPT_UNIT_TEST) += dept_unit_test.o
diff --git a/kernel/dependency/dept.c b/kernel/dependency/dept.c
index c65bb0c6dad2..a08d0e16978b 100644
--- a/kernel/dependency/dept.c
+++ b/kernel/dependency/dept.c
@@ -78,8 +78,12 @@
 #include <linux/workqueue.h>
 #include <linux/irq_work.h>
 #include <linux/vmalloc.h>
+#include <linux/dept_unit_test.h>
 #include "dept_internal.h"
 
+struct dept_ut dept_ut_results;
+EXPORT_SYMBOL_GPL(dept_ut_results);
+
 static int dept_stop;
 static int dept_per_cpu_ready;
 
@@ -826,6 +830,10 @@ static void print_dep(struct dept_dep *d)
                        pr_warn("(wait to wake up)\n");
                        print_ip_stack(0, e->ewait_stack);
                }
+
+               dept_ut_ecxt_stack_account(valid_stack(e->ecxt_stack));
+               dept_ut_wait_stack_account(valid_stack(w->wait_stack));
+               dept_ut_evnt_stack_account(valid_stack(e->event_stack));
        }
 }
 
@@ -920,6 +928,8 @@ static void print_circle(struct dept_class *c)
        dump_stack();
 
        dept_outworld_exit();
+
+       dept_ut_circle_detect();
 }
 
 /*
@@ -1021,6 +1031,8 @@ static void print_recover_circle(struct dept_event_site 
*es)
        dump_stack();
 
        dept_outworld_exit();
+
+       dept_ut_recover_circle_detect();
 }
 
 static void bfs_init_recover(void *node, void *in, void **out)
diff --git a/kernel/dependency/dept_unit_test.c 
b/kernel/dependency/dept_unit_test.c
new file mode 100644
index 000000000000..88e846b9f876
--- /dev/null
+++ b/kernel/dependency/dept_unit_test.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DEPT unit test
+ *
+ * Started by Byungchul Park <max.byungchul.p...@gmail.com>:
+ *
+ *  Copyright (c) 2025 SK hynix, Inc., Byungchul Park
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/dept.h>
+#include <linux/dept_unit_test.h>
+
+MODULE_DESCRIPTION("DEPT unit test");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Byungchul Park <max.byungchul.p...@sk.com>");
+
+struct unit {
+       const char *name;
+       bool (*func)(void);
+       bool result;
+};
+
+static DEFINE_SPINLOCK(s1);
+static DEFINE_SPINLOCK(s2);
+static bool test_spin_lock_deadlock(void)
+{
+       dept_ut_results.circle_detected = false;
+
+       spin_lock(&s1);
+       spin_lock(&s2);
+       spin_unlock(&s2);
+       spin_unlock(&s1);
+
+       spin_lock(&s2);
+       spin_lock(&s1);
+       spin_unlock(&s1);
+       spin_unlock(&s2);
+
+       return dept_ut_results.circle_detected;
+}
+
+static DEFINE_MUTEX(m1);
+static DEFINE_MUTEX(m2);
+static bool test_mutex_lock_deadlock(void)
+{
+       dept_ut_results.circle_detected = false;
+
+       mutex_lock(&m1);
+       mutex_lock(&m2);
+       mutex_unlock(&m2);
+       mutex_unlock(&m1);
+
+       mutex_lock(&m2);
+       mutex_lock(&m1);
+       mutex_unlock(&m1);
+       mutex_unlock(&m2);
+
+       return dept_ut_results.circle_detected;
+}
+
+static bool test_wait_event_deadlock(void)
+{
+       struct dept_map dmap1;
+       struct dept_map dmap2;
+
+       sdt_map_init(&dmap1);
+       sdt_map_init(&dmap2);
+
+       dept_ut_results.circle_detected = false;
+
+       sdt_request_event(&dmap1); /* [S] */
+       sdt_wait(&dmap2); /* [W] */
+       sdt_event(&dmap1); /* [E] */
+
+       sdt_request_event(&dmap2); /* [S] */
+       sdt_wait(&dmap1); /* [W] */
+       sdt_event(&dmap2); /* [E] */
+
+       return dept_ut_results.circle_detected;
+}
+
+static void dummy_event(void)
+{
+       /* Do nothing. */
+}
+
+static DEFINE_DEPT_EVENT_SITE(es1);
+static DEFINE_DEPT_EVENT_SITE(es2);
+static bool test_recover_deadlock(void)
+{
+       dept_ut_results.recover_circle_detected = false;
+
+       dept_recover_event(&es1, &es2);
+       dept_recover_event(&es2, &es1);
+
+       event_site(&es1, dummy_event);
+       event_site(&es2, dummy_event);
+
+       return dept_ut_results.recover_circle_detected;
+}
+
+static struct unit units[] = {
+       {
+               .name = "spin lock deadlock test",
+               .func = test_spin_lock_deadlock,
+       },
+       {
+               .name = "mutex lock deadlock test",
+               .func = test_mutex_lock_deadlock,
+       },
+       {
+               .name = "wait event deadlock test",
+               .func = test_wait_event_deadlock,
+       },
+       {
+               .name = "event recover deadlock test",
+               .func = test_recover_deadlock,
+       },
+};
+
+static int __init dept_ut_init(void)
+{
+       int i;
+
+       lockdep_off();
+
+       dept_ut_results.ecxt_stack_valid_cnt = 0;
+       dept_ut_results.ecxt_stack_total_cnt = 0;
+       dept_ut_results.wait_stack_valid_cnt = 0;
+       dept_ut_results.wait_stack_total_cnt = 0;
+       dept_ut_results.evnt_stack_valid_cnt = 0;
+       dept_ut_results.evnt_stack_total_cnt = 0;
+
+       for (i = 0; i < ARRAY_SIZE(units); i++)
+               units[i].result = units[i].func();
+
+       pr_info("\n");
+       pr_info("******************************************\n");
+       pr_info("DEPT unit test results\n");
+       pr_info("******************************************\n");
+       for (i = 0; i < ARRAY_SIZE(units); i++) {
+               pr_info("(%s) %s\n", units[i].result ? "pass" : "fail",
+                               units[i].name);
+       }
+       pr_info("ecxt stack valid count = %d/%d\n",
+                       dept_ut_results.ecxt_stack_valid_cnt,
+                       dept_ut_results.ecxt_stack_total_cnt);
+       pr_info("wait stack valid count = %d/%d\n",
+                       dept_ut_results.wait_stack_valid_cnt,
+                       dept_ut_results.wait_stack_total_cnt);
+       pr_info("event stack valid count = %d/%d\n",
+                       dept_ut_results.evnt_stack_valid_cnt,
+                       dept_ut_results.evnt_stack_total_cnt);
+       pr_info("******************************************\n");
+       pr_info("\n");
+
+       lockdep_on();
+
+       return 0;
+}
+
+static void dept_ut_cleanup(void)
+{
+       /*
+        * Do nothing for now.
+        */
+}
+
+module_init(dept_ut_init);
+module_exit(dept_ut_cleanup);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ec840c672846..8db2a8136418 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1404,6 +1404,18 @@ config DEPT_AGGRESSIVE_TIMEOUT_WAIT
          that timeout is used to avoid a deadlock. Say N if you'd like
          to avoid verbose reports.
 
+config DEPT_UNIT_TEST
+       tristate "unit test for DEPT"
+       depends on DEBUG_KERNEL && DEPT
+       default n
+       help
+         This option provides a kernel module that runs unit test for
+         DEPT.
+
+         Say Y if you want DEPT unit test to be built into the kernel.
+         Say M if you want DEPT unit test to build as a module.
+         Say N if you are unsure.
+
 config LOCK_DEBUGGING_SUPPORT
        bool
        depends on TRACE_IRQFLAGS_SUPPORT && STACKTRACE_SUPPORT && 
LOCKDEP_SUPPORT
-- 
2.17.1

Reply via email to