[kvm-unit-tests PATCH 32/33] arm: Add memtest support

Suzuki K Poulose suzuki.poulose at arm.com
Fri Apr 12 03:34:07 PDT 2024


From: Joey Gouly <joey.gouly at arm.com>

Add a test to touch all memory allocated to the guest.
Provides options to allocate in block, shared mode etc.
Also adds a "memstress" variant which would test all the
combinations in order.

PS: The memory allocator fragments the available memory
on page allocation and doesn't allow merging them for a
higher order allocation. Hence, all the block alloc tests
are run one after the other, before any page allocation
tests are run

Signed-off-by: Joey Gouly <joey.gouly at arm.com>
Co-developed-by: Suzuki K Poulose <suzuki.poulose at arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose at arm.com>
---
 arm/selftest.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 1 deletion(-)

diff --git a/arm/selftest.c b/arm/selftest.c
index 7bc5fb76..d9fd9750 100644
--- a/arm/selftest.c
+++ b/arm/selftest.c
@@ -9,6 +9,7 @@
 #include <util.h>
 #include <devicetree.h>
 #include <memregions.h>
+#include <alloc_page.h>
 #include <vmalloc.h>
 #include <asm/setup.h>
 #include <asm/ptrace.h>
@@ -435,6 +436,123 @@ static void cpu_report(void *data __unused)
 	report_info("CPU%3d: MPIDR=%010" PRIx64, cpu, mpidr);
 }
 
+
+/*
+ * do_memtest: Accepts the following paramters.
+ *
+ * shared[=0/1] - Use shared page for the memtests.
+ * block[=0/1]  - Use SZ_2M allocation/free.
+ * nofree	- Do not free the pages after the test.
+ */
+static void do_memtest(int argc, char *argv[])
+{
+	int i;
+	int npages = 0;
+	bool result = true;
+	const char pattern = 0xFB;
+	void *prev_page = NULL;
+	uintptr_t *page_to_free = NULL;
+	int size;
+	void* (*alloc_order_fn)(unsigned int);
+	void (*free_order_fn)(void *, unsigned int);
+	bool shared = false;
+	bool block = false;
+	bool nofree = false;
+	int order = 0;
+
+	for (i = 2; i < argc; i++) {
+		long val, len;
+
+		len = parse_keyval(argv[i], &val);
+		if (len == -1) {
+			if (!strcmp(argv[i], "shared")) {
+				shared = true;
+				continue;
+			} else if (!strcmp(argv[i], "nofree")) {
+				nofree = true;
+				continue;
+			} else if (!strcmp(argv[i], "block")) {
+				block = true;
+			} else {
+				printf("Unknown options %s\n", argv[i]);
+				abort();
+			}
+		} else if (!strncmp(argv[i], "block", len)) {
+			block = !!val;
+		} else if (!strncmp(argv[i], "shared", len)) {
+			shared = !!val;
+		}
+	}
+
+	/* Block mapping is 2MB */
+	if (block)
+		order = (21 - PAGE_SHIFT);
+
+	size = (1 << order) * PAGE_SIZE;
+	if (shared) {
+		alloc_order_fn = &alloc_pages_shared;
+		free_order_fn = &free_pages_shared_by_order;
+	} else {
+		alloc_order_fn = &alloc_pages;
+		free_order_fn = &free_pages_by_order;
+	}
+
+	report_info("Running %smemtest with size %dK%s, order=%d",
+		    shared ? "shared " : "",
+		    size >> 10,
+		    nofree ? " with no freeing" :"",
+		    order);
+
+	while (1) {
+		void *page = alloc_order_fn(order);
+
+		if (!page)
+			break;
+		npages += 1;
+
+		memset(page, pattern, size);
+
+		for (i = 0; i < size; i += 1) {
+			if (((char *)page)[i] != pattern) {
+				result = false;
+				report(false, "Failed to find the pattern in page %p, expected: %d, got: %d\n",
+					page, pattern, ((char *)page)[i]);
+				goto exit;
+			}
+		}
+
+		/*
+		 * Save a pointer to the allocated page so that it can be
+		 * free'd at the end of the test.
+		*/
+		*(uintptr_t *)page = (uintptr_t)prev_page;
+		prev_page = page;
+	}
+
+	page_to_free = prev_page;
+	while (!nofree && page_to_free) {
+		prev_page = (uintptr_t *)(*page_to_free);
+		free_order_fn(page_to_free, order);
+		page_to_free = prev_page;
+	}
+
+exit:
+	report(result, "Tested with %dKB", (npages  * size) >> 10);
+}
+
+static void do_memstress(void)
+{
+	char shared[16] = "shared";
+	char block[16] = "block";
+	char nofree[16] = "nofree";
+	char null[4] = "";
+
+	do_memtest(4, &((char *[]){ null, null, shared, block })[0]);
+	do_memtest(3, &((char *[]){ null, null, block })[0]);
+	do_memtest(3, &((char *[]){ null, null, shared })[0]);
+	do_memtest(3, &((char *[]){ null, null, nofree })[0]);
+}
+
 int main(int argc, char **argv)
 {
 	report_prefix_push("selftest");
@@ -466,7 +584,10 @@ int main(int argc, char **argv)
 		smp_rmb();		/* Paired with wmb in cpu_report(). */
 		report(cpumask_full(&valid), "MPIDR test on all CPUs");
 		report_info("%d CPUs reported back", nr_cpus);
-
+	} else if (strcmp(argv[1], "memtest") == 0) {
+		do_memtest(argc, argv);
+	} else if (strcmp(argv[1], "memstress") == 0) {
+		do_memstress();
 	} else {
 		printf("Unknown subtest\n");
 		abort();
-- 
2.34.1




More information about the linux-arm-kernel mailing list