[PATCH v17 41/47] dept: implement a basic unit test for dept

Byungchul Park
2025-10-02T08:14:06Z

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

Signed-off-by: Byungchul Park <byungchul@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.park@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 3c3ec2701bd6..0f4464657288 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.park@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.park@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 290563fa8b58..f0c58bee263a 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