[PATCH 3/3] resource, kunit: add test case for resource_coalesce()

Ilpo Järvinen posted 3 patches 2 months, 1 week ago
[PATCH 3/3] resource, kunit: add test case for resource_coalesce()
Posted by Ilpo Järvinen 2 months, 1 week ago
Add a test case for resource_coalesce() which has both parent and
children resources to adjust.

Add framework to build resource trees in a human-readable way and to
verify them which could be used to kunit test also other resource tree
manipulation functions.

Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
---
 include/linux/ioport.h  |   5 ++
 kernel/resource.c       |   8 ++-
 kernel/resource_kunit.c | 121 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 132 insertions(+), 2 deletions(-)

diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index e8b2d6aa4013..56f4d1cfde29 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -426,5 +426,10 @@ static inline void irqresource_disabled(struct resource *res, u32 irq)
 
 extern struct address_space *iomem_get_mapping(void);
 
+#if IS_ENABLED(CONFIG_KUNIT)
+int resource_coalesce(struct resource *res, struct resource *next_res,
+		      struct resource *new_res);
+#endif
+
 #endif /* __ASSEMBLY__ */
 #endif	/* _LINUX_IOPORT_H */
diff --git a/kernel/resource.c b/kernel/resource.c
index c6e1872abb78..f7e7b49dc9a4 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -10,6 +10,8 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <kunit/visibility.h>
+
 #include <linux/cleanup.h>
 #include <linux/export.h>
 #include <linux/errno.h>
@@ -299,8 +301,9 @@ static bool resource_mergeable(struct resource *r1, struct resource *r2)
 	return false;
 }
 
-static int resource_coalesce(struct resource *res, struct resource *next_res,
-			     struct resource *new_res)
+VISIBLE_IF_KUNIT int resource_coalesce(struct resource *res,
+				       struct resource *next_res,
+				       struct resource *new_res)
 {
 	struct resource *parent, *tmp;
 	struct resource **p, **c;
@@ -367,6 +370,7 @@ static int resource_coalesce(struct resource *res, struct resource *next_res,
 
 	return 0;
 }
+EXPORT_SYMBOL_IF_KUNIT(resource_coalesce);
 
 /**
  * request_resource_conflict - request and reserve an I/O or memory resource
diff --git a/kernel/resource_kunit.c b/kernel/resource_kunit.c
index b8ef75b99eb2..3b5d09bb612a 100644
--- a/kernel/resource_kunit.c
+++ b/kernel/resource_kunit.c
@@ -4,6 +4,8 @@
  */
 
 #include <kunit/test.h>
+
+#include <linux/array_size.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
@@ -20,6 +22,16 @@
 #define R3_END		0x789a
 #define R4_START	0x2000
 #define R4_END		0x7000
+#define  R4A_START	0x2000
+#define  R4A_END	0x3000
+#define  R4B_START	0x3001
+#define  R4B_END	0x4000
+#define R5_START	0x7001
+#define R5_END		0x8000
+#define  R5A_START	0x7001
+#define  R5A_END	0x7800
+#define  R5B_START	0x7801
+#define  R5B_END	0x7c00
 
 static struct resource r0 = { .start = R0_START, .end = R0_END };
 static struct resource r1 = { .start = R1_START, .end = R1_END };
@@ -34,6 +46,11 @@ struct result {
 	bool ret;
 };
 
+struct resource_tree_def {
+	struct resource r;
+	unsigned int parent, child, sibling;
+};
+
 static struct result results_for_union[] = {
 	{
 		.r1 = &r1, .r2 = &r0, .r.start = R0_START, .r.end = R0_END, .ret = true,
@@ -139,6 +156,107 @@ static void resource_test_intersection(struct kunit *test)
 	} while (++i < ARRAY_SIZE(results_for_intersection));
 }
 
+static struct resource *copy_resource_tree(struct kunit *test,
+					   struct resource_tree_def *def,
+					   size_t len, size_t extra)
+{
+	struct resource *tree;
+	unsigned int i;
+
+	tree = kunit_kcalloc(test, len + extra, sizeof(*tree), GFP_KERNEL);
+	KUNIT_EXPECT_NOT_NULL(test, tree);
+
+	for (i = 0; i < len; i++) {
+		tree[i] = def[i].r;
+
+		if (def[i].parent)
+			tree[i].parent = &tree[def[i].parent];
+		if (def[i].child)
+			tree[i].child = &tree[def[i].child];
+		if (def[i].sibling)
+			tree[i].sibling = &tree[def[i].sibling];
+	}
+
+	return tree;
+}
+
+static void resource_do_test_tree(struct kunit *test, struct resource *tree,
+				  struct resource_tree_def *exp_tree,
+				  size_t len)
+{
+	unsigned int link, i;
+
+	for (i = 0; i < len; i++) {
+		KUNIT_EXPECT_EQ_MSG(test, tree[i].start, exp_tree[i].r.start,
+				    "Start elements for resource %u are not equal", i);
+		KUNIT_EXPECT_EQ_MSG(test, tree[i].end, exp_tree[i].r.end,
+				    "End elements for resource %u are not equal", i);
+
+		link = tree[i].parent ? tree[i].parent - &tree[0] : 0;
+		KUNIT_EXPECT_EQ_MSG(test, link, exp_tree[i].parent,
+				    "Parent link for resource %u is not equal", i);
+
+		link = tree[i].child ? tree[i].child - &tree[0] : 0;
+		KUNIT_EXPECT_EQ_MSG(test, link, exp_tree[i].child,
+				    "Child link for resource %u is not equal", i);
+
+		link = tree[i].sibling ? tree[i].sibling - &tree[0] : 0;
+		KUNIT_EXPECT_EQ_MSG(test, link, exp_tree[i].sibling,
+				    "Sibling link for resource %u is not equal", i);
+	}
+}
+
+struct resource_tree_def resource_test_tree[8] = {
+	/* [0] is empty intentionally. */
+	[1] = { .r.start = R0_START, .r.end = R0_END, .child = 2 },
+	[2] = { .r.start = R4_START, .r.end = R4_END, .parent = 1, .sibling = 3, .child = 4 },
+	[3] = { .r.start = R5_START, .r.end = R5_END, .parent = 1, .child = 6 },
+	[4] = { .r.start = R4A_START, .r.end = R4A_END, .parent = 2, .sibling = 5 },
+	[5] = { .r.start = R4B_START, .r.end = R4B_END, .parent = 2 },
+	[6] = { .r.start = R5A_START, .r.end = R5A_END, .parent = 3, .sibling = 7 },
+	[7] = { .r.start = R5B_START, .r.end = R5B_END, .parent = 3 },
+};
+
+struct resource_tree_def result_for_coalesce_2_and_3[9] = {
+	/* [0] is empty intentionally. */
+	[1] = { .r.start = R0_START, .r.end = R0_END, .child = 8 },
+	[2] = { .r.start = R4_START, .r.end = R4_END },
+	[3] = { .r.start = R5_START, .r.end = R5_END },
+	[4] = { .r.start = R4A_START, .r.end = R4A_END, .parent = 8, .sibling = 5 },
+	[5] = { .r.start = R4B_START, .r.end = R4B_END, .parent = 8, .sibling = 6 },
+	[6] = { .r.start = R5A_START, .r.end = R5A_END, .parent = 8, .sibling = 7 },
+	[7] = { .r.start = R5B_START, .r.end = R5B_END, .parent = 8 },
+	[8] = { .r.start = R4_START, .r.end = R5_END, .parent = 1, .child = 4 },
+};
+
+static void resource_test_tree_test_harness(struct kunit *test)
+{
+	struct resource *tree;
+
+	tree = copy_resource_tree(test, resource_test_tree,
+				  ARRAY_SIZE(resource_test_tree), 0);
+
+	/* Sanity-check test harness with identity check */
+	resource_do_test_tree(test, tree, resource_test_tree,
+			      ARRAY_SIZE(resource_test_tree));
+}
+
+static void resource_test_coalesce(struct kunit *test)
+{
+	struct resource *tree, *result;
+	int ret;
+
+	tree = copy_resource_tree(test, resource_test_tree,
+				  ARRAY_SIZE(resource_test_tree), 1);
+
+	result = &tree[ARRAY_SIZE(resource_test_tree)];
+
+	ret = resource_coalesce(&tree[2], &tree[3], result);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+	resource_do_test_tree(test, tree, result_for_coalesce_2_and_3,
+			      ARRAY_SIZE(result_for_coalesce_2_and_3));
+}
+
 /*
  * The test resource tree for region_intersects() test:
  *
@@ -292,6 +410,8 @@ static void resource_test_region_intersects(struct kunit *test)
 static struct kunit_case resource_test_cases[] = {
 	KUNIT_CASE(resource_test_union),
 	KUNIT_CASE(resource_test_intersection),
+	KUNIT_CASE(resource_test_tree_test_harness),
+	KUNIT_CASE(resource_test_coalesce),
 	KUNIT_CASE(resource_test_region_intersects),
 	{}
 };
@@ -304,3 +424,4 @@ kunit_test_suite(resource_test_suite);
 
 MODULE_DESCRIPTION("I/O Port & Memory Resource manager unit tests");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
-- 
2.39.5