[PATCH 3/3] dma-debug: add bucket bound lifetime coverage

From: Juanlu Herrero

Date: Tue Nov 10 2026 - 16:39:18 EST


Add a KUnit test on a local bucket for conservative bounds, equal-sized
mapping removal, empty-bucket reset and reuse. Exercise the existing
insertion and removal helpers without DMA hardware.

Signed-off-by: Juanlu Herrero <juanlu@xxxxxxxxxxxx>
---
kernel/dma/Kconfig | 10 ++++++++
kernel/dma/debug.c | 4 +++
kernel/dma/tests/debug_kunit.c | 57 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 71 insertions(+)

diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig
index 3830a63ae032d..3f295a1503597 100644
--- a/kernel/dma/Kconfig
+++ b/kernel/dma/Kconfig
@@ -291,6 +291,16 @@ config DMA_API_DEBUG

If unsure, say N.

+config DMA_API_DEBUG_KUNIT_TEST
+ bool "Tests for DMA debug bucket bounds" if !KUNIT_ALL_TESTS
+ depends on KUNIT=y && DMA_API_DEBUG
+ default KUNIT_ALL_TESTS
+ help
+ Enable KUnit (kernel unit test) coverage for DMA debug bucket bounds.
+ Check that insertion and removal keep the bound conservative and
+ reset it when a bucket becomes empty. Exercise reuse and equal-sized
+ mappings on a local bucket. No physical DMA device is required.
+
config DMA_MAP_BENCHMARK
bool "Enable benchmarking of streaming DMA mapping"
depends on DEBUG_FS
diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c
index 8dad45d0a4360..1d0990e447516 100644
--- a/kernel/dma/debug.c
+++ b/kernel/dma/debug.c
@@ -1700,3 +1700,7 @@ static int __init dma_debug_driver_setup(char *str)
return 1;
}
__setup("dma_debug_driver=", dma_debug_driver_setup);
+
+#if IS_ENABLED(CONFIG_DMA_API_DEBUG_KUNIT_TEST)
+#include "tests/debug_kunit.c"
+#endif
diff --git a/kernel/dma/tests/debug_kunit.c b/kernel/dma/tests/debug_kunit.c
new file mode 100644
index 0000000000000..96cff02a6918d
--- /dev/null
+++ b/kernel/dma/tests/debug_kunit.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <kunit/test.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+static void dma_debug_bound_lifetime_test(struct kunit *test)
+{
+ struct dma_debug_entry *entries;
+ struct hash_bucket bucket = {};
+ unsigned long bounds[7];
+
+ entries = kunit_kcalloc(test, 3, sizeof(*entries), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, entries);
+ entries[0].size = SZ_4K;
+ entries[1].size = SZ_64K;
+ entries[2].size = SZ_64K;
+ INIT_LIST_HEAD(&bucket.list);
+ spin_lock_init(&bucket.lock);
+ spin_lock(&bucket.lock);
+ hash_bucket_add(&bucket, &entries[0]);
+ bounds[0] = bucket.max_entry_size;
+ hash_bucket_add(&bucket, &entries[1]);
+ hash_bucket_add(&bucket, &entries[2]);
+ bounds[1] = bucket.max_entry_size;
+ hash_bucket_del(&bucket, &entries[1]);
+ bounds[2] = bucket.max_entry_size;
+ hash_bucket_del(&bucket, &entries[2]);
+ bounds[3] = bucket.max_entry_size;
+ hash_bucket_del(&bucket, &entries[0]);
+ bounds[4] = bucket.max_entry_size;
+ hash_bucket_add(&bucket, &entries[0]);
+ bounds[5] = bucket.max_entry_size;
+ hash_bucket_del(&bucket, &entries[0]);
+ bounds[6] = bucket.max_entry_size;
+ spin_unlock(&bucket.lock);
+
+ KUNIT_EXPECT_GE(test, bounds[0], (unsigned long)SZ_4K);
+ KUNIT_EXPECT_GE(test, bounds[1], (unsigned long)SZ_64K);
+ KUNIT_EXPECT_GE(test, bounds[2], (unsigned long)SZ_64K);
+ KUNIT_EXPECT_GE(test, bounds[3], (unsigned long)SZ_4K);
+ KUNIT_EXPECT_EQ(test, bounds[4], 0UL);
+ KUNIT_EXPECT_GE(test, bounds[5], (unsigned long)SZ_4K);
+ KUNIT_EXPECT_EQ(test, bounds[6], 0UL);
+}
+
+static struct kunit_case dma_debug_bound_cases[] = {
+ KUNIT_CASE(dma_debug_bound_lifetime_test),
+ {}
+};
+
+static struct kunit_suite dma_debug_bound_suite = {
+ .name = "dma-debug-bound",
+ .test_cases = dma_debug_bound_cases,
+};
+
+kunit_test_suite(dma_debug_bound_suite);

--
2.53.0-Meta