mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Amit Patole <amit.patole@gmail.com>
To: Andrew Morton <akpm@linux-foundation.org>,
	 Fredrik Noring <noring@nocrew.org>
Cc: Christoph Hellwig <hch@lst.de>,
	 Brendan Higgins <brendan.higgins@linux.dev>,
	David Gow <david@davidgow.net>,  Rae Moar <raemoar63@gmail.com>,
	linux-kselftest@vger.kernel.org,  kunit-dev@googlegroups.com,
	linux-kernel@vger.kernel.org,
	 Amit Patole <amit.patole@gmail.com>
Subject: [PATCH 2/2] lib/tests: add KUnit tests for genalloc
Date: Fri, 06 Nov 2026 09:02:33 -0500	[thread overview]
Message-ID: <20261106-kunit-genalloc-v1-2-ba1ea397dfd8@gmail.com> (raw)
In-Reply-To: <20261106-kunit-genalloc-v1-0-ba1ea397dfd8@gmail.com>

lib/genalloc.c is the allocator that drivers use for on-chip SRAM,
IRAM and other special memory (i.MX SDMA, CODA, Tegra BPMP, s390 CIO,
USB local memory and others), but it has no unit tests.

Add a KUnit suite that covers:

- gen_pool_size(), gen_pool_avail() and gen_pool_has_addr();
- allocation and freeing, rounding to the allocation granule, zero
  and oversized requests, and an exact fit;
- the first fit, best fit (including the tie-break between equal
  holes), first fit aligned, first fit order aligned and fixed
  offset algorithms, and gen_pool_set_algo(pool, NULL, NULL);
- aligned allocation from a chunk that does not start on the
  alignment boundary;
- pools with several chunks, gen_pool_virt_to_phys() and
  gen_pool_for_each_chunk();
- chunk owners;
- the DMA helpers, including zeroing, alignment and a NULL pool;
- devm_gen_pool_create() and gen_pool_get() with named and unnamed
  pools on a KUnit device.

gen_pool_destroy() BUGs on outstanding allocations, so the cleanup
clears the chunk bitmaps first, and allocation results that are freed
later are checked with assertions; a failing test then fails cleanly
instead of crashing the kernel.

An AI coding assistant (Claude Opus 5.5) was used to draft the tests.
They were run with "tools/testing/kunit/kunit.py run" on UML (12/12
pass), built with W=1 without warnings, and checked against
deliberately broken versions of gen_pool_best_fit(),
gen_pool_first_fit_align() and the accounting in gen_pool_free_owner(),
each of which makes one test fail.

Signed-off-by: Amit Patole <amit.patole@gmail.com>
---
 lib/Kconfig.debug          |  15 ++
 lib/tests/Makefile         |   1 +
 lib/tests/genalloc_kunit.c | 508 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 524 insertions(+)

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2d22638ec13a..c66c3711e68c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3558,6 +3558,21 @@ config PRIME_NUMBERS_KUNIT_TEST
 
 	  If unsure, say N
 
+config GENALLOC_KUNIT_TEST
+	tristate "KUnit test for the general purpose allocator" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	select GENERIC_ALLOCATOR
+	default KUNIT_ALL_TESTS
+	help
+	  This option enables the KUnit test suite for lib/genalloc.c, the
+	  allocator drivers use to manage on-chip SRAM and other special
+	  memory regions. The tests cover allocation and freeing, the
+	  first fit, best fit, aligned, order aligned and fixed offset
+	  algorithms, multiple chunks, virtual to physical translation,
+	  the DMA helpers and the device-managed pools.
+
+	  If unsure, say N.
+
 config GLOB_KUNIT_TEST
 	tristate "Glob matching test" if !KUNIT_ALL_TESTS
 	depends on GLOB
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index df0537e1b91f..f17e4241e148 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -23,6 +23,7 @@ CFLAGS_fortify_kunit.o += $(DISABLE_STRUCTLEAK_PLUGIN)
 obj-$(CONFIG_FORTIFY_KUNIT_TEST) += fortify_kunit.o
 CFLAGS_test_fprobe.o += $(CC_FLAGS_FTRACE)
 obj-$(CONFIG_FPROBE_SANITY_TEST) += test_fprobe.o
+obj-$(CONFIG_GENALLOC_KUNIT_TEST) += genalloc_kunit.o
 obj-$(CONFIG_GLOB_KUNIT_TEST) += glob_kunit.o
 obj-$(CONFIG_HASHTABLE_KUNIT_TEST) += hashtable_test.o
 obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
diff --git a/lib/tests/genalloc_kunit.c b/lib/tests/genalloc_kunit.c
new file mode 100644
index 000000000000..09b1bde9cf43
--- /dev/null
+++ b/lib/tests/genalloc_kunit.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for the general purpose allocator in lib/genalloc.c
+ */
+
+#include <kunit/device.h>
+#include <kunit/test.h>
+#include <linux/bitmap.h>
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/module.h>
+#include <linux/numa.h>
+#include <linux/string.h>
+
+/* Allocation granule used by most tests: 16 bytes. */
+#define GP_ORDER	4
+#define GP_UNIT		BIT(GP_ORDER)
+#define GP_CHUNK	1024
+
+static void gp_clear_chunk(struct gen_pool *pool, struct gen_pool_chunk *chunk,
+			   void *data)
+{
+	size_t size = chunk->end_addr - chunk->start_addr + 1;
+
+	bitmap_zero(chunk->bits, size >> pool->min_alloc_order);
+}
+
+/*
+ * gen_pool_destroy() BUGs on outstanding allocations. Clear them first so
+ * that a test which fails half way does not take the whole run down.
+ */
+static void gp_destroy(void *data)
+{
+	struct gen_pool *pool = data;
+
+	gen_pool_for_each_chunk(pool, gp_clear_chunk, NULL);
+	gen_pool_destroy(pool);
+}
+
+static struct gen_pool *gp_create(struct kunit *test, int order)
+{
+	struct gen_pool *pool = gen_pool_create(order, NUMA_NO_NODE);
+
+	KUNIT_ASSERT_NOT_NULL(test, pool);
+	KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, gp_destroy, pool),
+			0);
+	return pool;
+}
+
+/* Backing memory for a chunk, aligned to @size (a power of two). */
+static unsigned long gp_mem(struct kunit *test, size_t size)
+{
+	void *mem = kunit_kzalloc(test, size, GFP_KERNEL);
+
+	KUNIT_ASSERT_NOT_NULL(test, mem);
+	return (unsigned long)mem;
+}
+
+static void genalloc_size_avail_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+
+	KUNIT_EXPECT_EQ(test, gen_pool_size(pool), 0);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), 0);
+	KUNIT_EXPECT_EQ(test, gen_pool_alloc(pool, GP_UNIT), 0);
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add(pool, base, GP_CHUNK, NUMA_NO_NODE),
+			0);
+	KUNIT_EXPECT_EQ(test, gen_pool_size(pool), GP_CHUNK);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK);
+
+	KUNIT_EXPECT_TRUE(test, gen_pool_has_addr(pool, base, GP_CHUNK));
+	KUNIT_EXPECT_TRUE(test, gen_pool_has_addr(pool, base + GP_CHUNK - 1, 1));
+	KUNIT_EXPECT_FALSE(test, gen_pool_has_addr(pool, base + GP_CHUNK - 4, 8));
+	KUNIT_EXPECT_FALSE(test, gen_pool_has_addr(pool, base + GP_CHUNK, 1));
+	KUNIT_EXPECT_FALSE(test, gen_pool_has_addr(pool, base - 1, 2));
+}
+
+static void genalloc_alloc_free_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+	unsigned long a, b, c;
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add(pool, base, GP_CHUNK, NUMA_NO_NODE),
+			0);
+
+	/* A zero-sized request fails. */
+	KUNIT_EXPECT_EQ(test, gen_pool_alloc(pool, 0), 0);
+
+	a = gen_pool_alloc(pool, GP_UNIT);
+	KUNIT_ASSERT_EQ(test, a, base);
+
+	/* Sizes are rounded up to whole granules. */
+	b = gen_pool_alloc(pool, 1);
+	KUNIT_ASSERT_EQ(test, b, base + GP_UNIT);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK - 2 * GP_UNIT);
+
+	c = gen_pool_alloc(pool, GP_UNIT + 1);
+	KUNIT_ASSERT_EQ(test, c, base + 2 * GP_UNIT);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK - 4 * GP_UNIT);
+
+	/* The pool size never changes. */
+	KUNIT_EXPECT_EQ(test, gen_pool_size(pool), GP_CHUNK);
+
+	/* Freeing with the size used to allocate returns the whole granules. */
+	gen_pool_free(pool, b, 1);
+	gen_pool_free(pool, c, GP_UNIT + 1);
+	gen_pool_free(pool, a, GP_UNIT);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK);
+
+	/* Too large a request fails; an exact fit succeeds. */
+	KUNIT_EXPECT_EQ(test, gen_pool_alloc(pool, GP_CHUNK + 1), 0);
+	a = gen_pool_alloc(pool, GP_CHUNK);
+	KUNIT_ASSERT_EQ(test, a, base);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), 0);
+	KUNIT_EXPECT_EQ(test, gen_pool_alloc(pool, 1), 0);
+	gen_pool_free(pool, a, GP_CHUNK);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK);
+}
+
+static void genalloc_first_fit_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+	unsigned long a, b, c, d;
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add(pool, base, GP_CHUNK, NUMA_NO_NODE),
+			0);
+
+	a = gen_pool_alloc(pool, 4 * GP_UNIT);
+	b = gen_pool_alloc(pool, 4 * GP_UNIT);
+	c = gen_pool_alloc(pool, 4 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, a, base);
+	KUNIT_ASSERT_EQ(test, b, base + 4 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, c, base + 8 * GP_UNIT);
+
+	/* A hole left by a free is reused by the next request that fits. */
+	gen_pool_free(pool, b, 4 * GP_UNIT);
+	d = gen_pool_alloc(pool, 2 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, d, b);
+
+	/* A request too large for the hole goes after the last allocation. */
+	b = gen_pool_alloc(pool, 3 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, b, base + 12 * GP_UNIT);
+
+	gen_pool_free(pool, a, 4 * GP_UNIT);
+	gen_pool_free(pool, b, 3 * GP_UNIT);
+	gen_pool_free(pool, c, 4 * GP_UNIT);
+	gen_pool_free(pool, d, 2 * GP_UNIT);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK);
+}
+
+static void genalloc_best_fit_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+	unsigned long big, sep, small, rest, addr;
+	size_t rest_size = GP_CHUNK - 14 * GP_UNIT;
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add(pool, base, GP_CHUNK, NUMA_NO_NODE),
+			0);
+
+	/* Leave an 8 granule hole at the start and a 2 granule hole later. */
+	big = gen_pool_alloc(pool, 8 * GP_UNIT);
+	sep = gen_pool_alloc(pool, 4 * GP_UNIT);
+	small = gen_pool_alloc(pool, 2 * GP_UNIT);
+	rest = gen_pool_alloc(pool, rest_size);
+	KUNIT_ASSERT_EQ(test, small, base + 12 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, rest, base + 14 * GP_UNIT);
+	gen_pool_free(pool, big, 8 * GP_UNIT);
+	gen_pool_free(pool, small, 2 * GP_UNIT);
+
+	/* First fit takes the first hole... */
+	addr = gen_pool_alloc(pool, 2 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, addr, big);
+	gen_pool_free(pool, addr, 2 * GP_UNIT);
+
+	/* ...best fit takes the smallest hole that is large enough. */
+	gen_pool_set_algo(pool, gen_pool_best_fit, NULL);
+	addr = gen_pool_alloc(pool, 2 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, addr, small);
+
+	/* With that hole used, best fit falls back to the larger one. */
+	KUNIT_ASSERT_EQ(test, gen_pool_alloc(pool, GP_UNIT), big);
+
+	gen_pool_free(pool, addr, 2 * GP_UNIT);
+	gen_pool_free(pool, big, GP_UNIT);
+
+	/*
+	 * Of two equally good holes, best fit takes the first. Free the
+	 * separator so granules 0-13 are free, then carve them into two
+	 * three granule holes (0-2 and 4-6) with allocations in between.
+	 */
+	gen_pool_free(pool, sep, 4 * GP_UNIT);
+	big = gen_pool_alloc(pool, 3 * GP_UNIT);
+	sep = gen_pool_alloc(pool, GP_UNIT);
+	small = gen_pool_alloc(pool, 3 * GP_UNIT);
+	addr = gen_pool_alloc(pool, 7 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, big, base);
+	KUNIT_ASSERT_EQ(test, sep, base + 3 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, small, base + 4 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, addr, base + 7 * GP_UNIT);
+	gen_pool_free(pool, big, 3 * GP_UNIT);
+	gen_pool_free(pool, small, 3 * GP_UNIT);
+
+	small = gen_pool_alloc(pool, 2 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, small, base);
+
+	gen_pool_free(pool, small, 2 * GP_UNIT);
+	gen_pool_free(pool, sep, GP_UNIT);
+	gen_pool_free(pool, addr, 7 * GP_UNIT);
+	gen_pool_free(pool, rest, rest_size);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK);
+
+	/* Setting a NULL algorithm restores first fit. */
+	gen_pool_set_algo(pool, NULL, NULL);
+	KUNIT_EXPECT_TRUE(test, pool->algo == gen_pool_first_fit);
+}
+
+static void genalloc_first_fit_align_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long mem = gp_mem(test, 2 * GP_CHUNK);
+	/* A chunk that starts one granule past a 256 byte boundary. */
+	unsigned long base = mem + GP_UNIT;
+	struct genpool_data_align align = { .align = 256 };
+	unsigned long a, b;
+
+	KUNIT_ASSERT_EQ(test, mem & 255, 0);
+	KUNIT_ASSERT_EQ(test, gen_pool_add(pool, base, GP_CHUNK, NUMA_NO_NODE),
+			0);
+
+	/* Alignment is of the address, not of the offset in the chunk. */
+	a = gen_pool_alloc_algo(pool, GP_UNIT, gen_pool_first_fit_align,
+				&align);
+	KUNIT_ASSERT_EQ(test, a, mem + 256);
+
+	b = gen_pool_alloc_algo(pool, GP_UNIT, gen_pool_first_fit_align,
+				&align);
+	KUNIT_ASSERT_EQ(test, b, mem + 512);
+
+	/* The space skipped for alignment is still free for plain requests. */
+	KUNIT_ASSERT_EQ(test, gen_pool_alloc(pool, GP_UNIT), base);
+
+	gen_pool_free(pool, a, GP_UNIT);
+	gen_pool_free(pool, b, GP_UNIT);
+	gen_pool_free(pool, base, GP_UNIT);
+}
+
+static void genalloc_order_align_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+	unsigned long a, b, c;
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add(pool, base, GP_CHUNK, NUMA_NO_NODE),
+			0);
+	gen_pool_set_algo(pool, gen_pool_first_fit_order_align, NULL);
+
+	a = gen_pool_alloc(pool, GP_UNIT);
+	KUNIT_ASSERT_EQ(test, a, base);
+
+	/* Three granules round up to an alignment of four granules. */
+	b = gen_pool_alloc(pool, 3 * GP_UNIT);
+	KUNIT_ASSERT_EQ(test, b, base + 4 * GP_UNIT);
+
+	/* A single granule needs no alignment and fills the first gap. */
+	c = gen_pool_alloc(pool, GP_UNIT);
+	KUNIT_ASSERT_EQ(test, c, base + GP_UNIT);
+
+	gen_pool_free(pool, a, GP_UNIT);
+	gen_pool_free(pool, b, 3 * GP_UNIT);
+	gen_pool_free(pool, c, GP_UNIT);
+}
+
+static void genalloc_fixed_alloc_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+	struct genpool_data_fixed fixed = { .offset = 8 * GP_UNIT };
+	unsigned long a;
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add(pool, base, GP_CHUNK, NUMA_NO_NODE),
+			0);
+
+	a = gen_pool_alloc_algo(pool, 2 * GP_UNIT, gen_pool_fixed_alloc, &fixed);
+	KUNIT_ASSERT_EQ(test, a, base + 8 * GP_UNIT);
+
+	/* The same offset cannot be handed out twice... */
+	KUNIT_EXPECT_EQ(test, gen_pool_alloc_algo(pool, GP_UNIT,
+						  gen_pool_fixed_alloc, &fixed),
+			0);
+
+	/* ...nor an offset whose range overlaps it. */
+	fixed.offset = 7 * GP_UNIT;
+	KUNIT_EXPECT_EQ(test, gen_pool_alloc_algo(pool, 2 * GP_UNIT,
+						  gen_pool_fixed_alloc, &fixed),
+			0);
+
+	/* An offset just before it, that fits, works. */
+	KUNIT_ASSERT_EQ(test, gen_pool_alloc_algo(pool, GP_UNIT,
+						  gen_pool_fixed_alloc, &fixed),
+			base + 7 * GP_UNIT);
+
+	gen_pool_free(pool, a, 2 * GP_UNIT);
+	gen_pool_free(pool, base + 7 * GP_UNIT, GP_UNIT);
+}
+
+static void genalloc_chunks_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long m1 = gp_mem(test, GP_CHUNK);
+	unsigned long m2 = gp_mem(test, GP_CHUNK / 4);
+	const phys_addr_t p1 = 0x100000, p2 = 0x200000;
+	unsigned long a;
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add_virt(pool, m1, p1, GP_CHUNK,
+						NUMA_NO_NODE), 0);
+	KUNIT_ASSERT_EQ(test, gen_pool_add_virt(pool, m2, p2, GP_CHUNK / 4,
+						NUMA_NO_NODE), 0);
+
+	KUNIT_EXPECT_EQ(test, gen_pool_size(pool), GP_CHUNK + GP_CHUNK / 4);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK + GP_CHUNK / 4);
+
+	KUNIT_EXPECT_EQ(test, gen_pool_virt_to_phys(pool, m1), p1);
+	KUNIT_EXPECT_EQ(test, gen_pool_virt_to_phys(pool, m1 + 100), p1 + 100);
+	KUNIT_EXPECT_EQ(test, gen_pool_virt_to_phys(pool, m2 + GP_CHUNK / 4 - 1),
+			p2 + GP_CHUNK / 4 - 1);
+	KUNIT_EXPECT_EQ(test, gen_pool_virt_to_phys(pool, m2 + GP_CHUNK / 4),
+			(phys_addr_t)-1);
+
+	/* A request that only fits in the larger chunk is served from it. */
+	a = gen_pool_alloc(pool, GP_CHUNK / 2);
+	KUNIT_ASSERT_EQ(test, a, m1);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK / 2 + GP_CHUNK / 4);
+
+	KUNIT_EXPECT_TRUE(test, gen_pool_has_addr(pool, m2, GP_CHUNK / 4));
+	/* A range spanning two chunks is not in the pool. */
+	KUNIT_EXPECT_FALSE(test, gen_pool_has_addr(pool, m1, 2 * GP_CHUNK));
+
+	gen_pool_free(pool, a, GP_CHUNK / 2);
+	KUNIT_EXPECT_EQ(test, gen_pool_avail(pool), GP_CHUNK + GP_CHUNK / 4);
+}
+
+static void genalloc_count_chunk(struct gen_pool *pool,
+				 struct gen_pool_chunk *chunk, void *data)
+{
+	int *count = data;
+
+	(*count)++;
+}
+
+static void genalloc_chunk_walk_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	int i, count = 0;
+
+	for (i = 0; i < 3; i++)
+		KUNIT_ASSERT_EQ(test, gen_pool_add(pool, gp_mem(test, GP_CHUNK),
+						   GP_CHUNK, NUMA_NO_NODE), 0);
+
+	gen_pool_for_each_chunk(pool, genalloc_count_chunk, &count);
+	KUNIT_EXPECT_EQ(test, count, 3);
+}
+
+static void genalloc_owner_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+	int token;
+	void *owner = NULL;
+	unsigned long a;
+
+	KUNIT_ASSERT_EQ(test, gen_pool_add_owner(pool, base, -1, GP_CHUNK,
+						 NUMA_NO_NODE, &token), 0);
+
+	a = gen_pool_alloc_owner(pool, GP_UNIT, &owner);
+	KUNIT_ASSERT_EQ(test, a, base);
+	KUNIT_EXPECT_PTR_EQ(test, owner, (void *)&token);
+
+	owner = NULL;
+	gen_pool_free_owner(pool, a, GP_UNIT, &owner);
+	KUNIT_EXPECT_PTR_EQ(test, owner, (void *)&token);
+
+	/* A failed allocation reports no owner. */
+	owner = &owner;
+	KUNIT_EXPECT_EQ(test, gen_pool_alloc_owner(pool, 2 * GP_CHUNK, &owner),
+			0);
+	KUNIT_EXPECT_NULL(test, owner);
+}
+
+static void genalloc_dma_test(struct kunit *test)
+{
+	struct gen_pool *pool = gp_create(test, GP_ORDER);
+	unsigned long base = gp_mem(test, GP_CHUNK);
+	const phys_addr_t phys = 0x40000000;
+	dma_addr_t dma = 0;
+	u8 *a, *b;
+	int i;
+
+	memset((void *)base, 0xa5, GP_CHUNK);
+	KUNIT_ASSERT_EQ(test, gen_pool_add_virt(pool, base, phys, GP_CHUNK,
+						NUMA_NO_NODE), 0);
+
+	a = gen_pool_dma_alloc(pool, 2 * GP_UNIT, &dma);
+	KUNIT_ASSERT_PTR_EQ(test, a, (u8 *)base);
+	KUNIT_EXPECT_EQ(test, dma, (dma_addr_t)phys);
+	/* gen_pool_dma_alloc() does not clear the memory. */
+	KUNIT_EXPECT_EQ(test, a[0], 0xa5);
+
+	b = gen_pool_dma_zalloc(pool, 2 * GP_UNIT, &dma);
+	KUNIT_ASSERT_PTR_EQ(test, b, (u8 *)base + 2 * GP_UNIT);
+	KUNIT_EXPECT_EQ(test, dma, (dma_addr_t)(phys + 2 * GP_UNIT));
+	for (i = 0; i < 2 * GP_UNIT; i++)
+		KUNIT_EXPECT_EQ(test, b[i], 0);
+	/* Only the requested memory is cleared. */
+	KUNIT_EXPECT_EQ(test, b[2 * GP_UNIT], 0xa5);
+
+	gen_pool_free(pool, (unsigned long)a, 2 * GP_UNIT);
+	gen_pool_free(pool, (unsigned long)b, 2 * GP_UNIT);
+
+	/* With the start in use, the aligned variant skips to 128 bytes. */
+	a = gen_pool_dma_alloc(pool, GP_UNIT, NULL);
+	KUNIT_ASSERT_PTR_EQ(test, a, (u8 *)base);
+	b = gen_pool_dma_alloc_align(pool, GP_UNIT, &dma, 128);
+	KUNIT_ASSERT_PTR_EQ(test, b, (u8 *)base + 128);
+	KUNIT_EXPECT_EQ(test, dma, (dma_addr_t)(phys + 128));
+	gen_pool_free(pool, (unsigned long)a, GP_UNIT);
+	gen_pool_free(pool, (unsigned long)b, GP_UNIT);
+
+	/* A NULL @dma is allowed, and so is a NULL pool. */
+	a = gen_pool_dma_alloc(pool, GP_UNIT, NULL);
+	KUNIT_ASSERT_PTR_EQ(test, a, (u8 *)base);
+	gen_pool_free(pool, (unsigned long)a, GP_UNIT);
+	KUNIT_EXPECT_NULL(test, gen_pool_dma_alloc(NULL, GP_UNIT, &dma));
+	KUNIT_EXPECT_NULL(test, gen_pool_dma_zalloc(NULL, GP_UNIT, &dma));
+	KUNIT_EXPECT_NULL(test, gen_pool_dma_alloc_align(NULL, GP_UNIT, &dma,
+							 128));
+
+	/* Exhaustion returns NULL. */
+	KUNIT_EXPECT_NULL(test, gen_pool_dma_zalloc(pool, 2 * GP_CHUNK, &dma));
+}
+
+static void genalloc_devm_test(struct kunit *test)
+{
+	struct gen_pool *named, *unnamed;
+	struct device *dev;
+
+	dev = kunit_device_register(test, "genalloc-kunit");
+	KUNIT_ASSERT_FALSE(test, IS_ERR(dev));
+
+	KUNIT_EXPECT_NULL(test, gen_pool_get(dev, NULL));
+	KUNIT_EXPECT_NULL(test, gen_pool_get(dev, "sram"));
+
+	named = devm_gen_pool_create(dev, GP_ORDER, NUMA_NO_NODE, "sram");
+	KUNIT_ASSERT_FALSE(test, IS_ERR(named));
+	KUNIT_EXPECT_STREQ(test, named->name, "sram");
+	KUNIT_EXPECT_EQ(test, named->min_alloc_order, GP_ORDER);
+	KUNIT_EXPECT_PTR_EQ(test, gen_pool_get(dev, "sram"), named);
+
+	/* An unnamed lookup does not match a named pool, nor the reverse. */
+	KUNIT_EXPECT_NULL(test, gen_pool_get(dev, NULL));
+	KUNIT_EXPECT_NULL(test, gen_pool_get(dev, "other"));
+
+	unnamed = devm_gen_pool_create(dev, GP_ORDER, NUMA_NO_NODE, NULL);
+	KUNIT_ASSERT_FALSE(test, IS_ERR(unnamed));
+	KUNIT_EXPECT_PTR_EQ(test, gen_pool_get(dev, NULL), unnamed);
+	KUNIT_EXPECT_PTR_EQ(test, gen_pool_get(dev, "sram"), named);
+
+	/* Each name, including no name, can only be used once per device. */
+	KUNIT_EXPECT_EQ(test, PTR_ERR(devm_gen_pool_create(dev, GP_ORDER,
+							   NUMA_NO_NODE,
+							   "sram")),
+			-EINVAL);
+	KUNIT_EXPECT_EQ(test, PTR_ERR(devm_gen_pool_create(dev, GP_ORDER,
+							   NUMA_NO_NODE, NULL)),
+			-EINVAL);
+
+	/* Both pools are destroyed when the device goes away. */
+}
+
+static struct kunit_case genalloc_test_cases[] = {
+	KUNIT_CASE(genalloc_size_avail_test),
+	KUNIT_CASE(genalloc_alloc_free_test),
+	KUNIT_CASE(genalloc_first_fit_test),
+	KUNIT_CASE(genalloc_best_fit_test),
+	KUNIT_CASE(genalloc_first_fit_align_test),
+	KUNIT_CASE(genalloc_order_align_test),
+	KUNIT_CASE(genalloc_fixed_alloc_test),
+	KUNIT_CASE(genalloc_chunks_test),
+	KUNIT_CASE(genalloc_chunk_walk_test),
+	KUNIT_CASE(genalloc_owner_test),
+	KUNIT_CASE(genalloc_dma_test),
+	KUNIT_CASE(genalloc_devm_test),
+	{}
+};
+
+static struct kunit_suite genalloc_test_suite = {
+	.name = "genalloc",
+	.test_cases = genalloc_test_cases,
+};
+
+kunit_test_suite(genalloc_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for the general purpose allocator");
+MODULE_LICENSE("GPL");

-- 
2.43.5


      parent reply	other threads:[~2026-11-06 14:02 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-06 14:02 [PATCH 0/2] lib/genalloc: fix a NULL pool check and add KUnit tests Amit Patole
2026-11-06 14:02 ` [PATCH 1/2] lib/genalloc: don't dereference a NULL pool in gen_pool_dma_[z]alloc() Amit Patole
2026-11-06 18:46   ` Fredrik Noring
2026-11-06 14:02 ` Amit Patole [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261106-kunit-genalloc-v1-2-ba1ea397dfd8@gmail.com \
    --to=amit.patole@gmail.com \
    --cc=akpm@linux-foundation.org \
    --cc=brendan.higgins@linux.dev \
    --cc=david@davidgow.net \
    --cc=hch@lst.de \
    --cc=kunit-dev@googlegroups.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=noring@nocrew.org \
    --cc=raemoar63@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®