Add tests for NUMA memory policy binding and NUMA aware allocation in
guest_memfd. This extends the existing selftests by adding proper
validation for:
- KVM GMEM set_policy and get_policy() vm_ops functionality using
  mbind() and get_mempolicy()
- NUMA policy application before and after memory allocation

These tests help ensure NUMA support for guest_memfd works correctly.

Signed-off-by: Shivank Garg <shiva...@amd.com>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../testing/selftests/kvm/guest_memfd_test.c  | 123 +++++++++++++++++-
 2 files changed, 122 insertions(+), 2 deletions(-)

diff --git a/tools/testing/selftests/kvm/Makefile.kvm 
b/tools/testing/selftests/kvm/Makefile.kvm
index e11ed9e59ab5..f4bb02231d6a 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -273,6 +273,7 @@ pgste-option = $(call try-run, echo 'int main(void) { 
return 0; }' | \
        $(CC) -Werror -Wl$(comma)--s390-pgste -x c - -o 
"$$TMP",-Wl$(comma)--s390-pgste)
 
 LDLIBS += -ldl
+LDLIBS += -lnuma
 LDFLAGS += -pthread $(no-pie-option) $(pgste-option)
 
 LIBKVM_C := $(filter %.c,$(LIBKVM))
diff --git a/tools/testing/selftests/kvm/guest_memfd_test.c 
b/tools/testing/selftests/kvm/guest_memfd_test.c
index 5da2ed6277ac..a5d261dcfdf5 100644
--- a/tools/testing/selftests/kvm/guest_memfd_test.c
+++ b/tools/testing/selftests/kvm/guest_memfd_test.c
@@ -7,6 +7,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <numa.h>
+#include <numaif.h>
 #include <errno.h>
 #include <stdio.h>
 #include <fcntl.h>
@@ -18,6 +20,7 @@
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 
 #include "kvm_util.h"
 #include "test_util.h"
@@ -115,6 +118,122 @@ static void test_mmap_not_supported(int fd, size_t 
page_size, size_t total_size)
        TEST_ASSERT_EQ(mem, MAP_FAILED);
 }
 
+#define TEST_REQUIRE_NUMA_MULTIPLE_NODES()     \
+       TEST_REQUIRE(numa_available() != -1 && numa_max_node() >= 1)
+
+static void test_mbind(int fd, size_t page_size, size_t total_size)
+{
+       unsigned long nodemask = 1; /* nid: 0 */
+       unsigned long maxnode = 8;
+       unsigned long get_nodemask;
+       int get_policy;
+       char *mem;
+       int ret;
+
+       TEST_REQUIRE_NUMA_MULTIPLE_NODES();
+
+       mem = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       TEST_ASSERT(mem != MAP_FAILED, "mmap for mbind test should succeed");
+
+       /* Test MPOL_INTERLEAVE policy */
+       ret = syscall(__NR_mbind, mem, page_size * 2, MPOL_INTERLEAVE,
+                     &nodemask, maxnode, 0);
+       TEST_ASSERT(!ret, "mbind with INTERLEAVE to node 0 should succeed");
+       ret = syscall(__NR_get_mempolicy, &get_policy, &get_nodemask,
+                     maxnode, mem, MPOL_F_ADDR);
+       TEST_ASSERT(!ret && get_policy == MPOL_INTERLEAVE && get_nodemask == 
nodemask,
+                   "Policy should be MPOL_INTERLEAVE and nodes match");
+
+       /* Test basic MPOL_BIND policy */
+       ret = syscall(__NR_mbind, mem + page_size * 2, page_size * 2, MPOL_BIND,
+                     &nodemask, maxnode, 0);
+       TEST_ASSERT(!ret, "mbind with MPOL_BIND to node 0 should succeed");
+       ret = syscall(__NR_get_mempolicy, &get_policy, &get_nodemask,
+                     maxnode, mem + page_size * 2, MPOL_F_ADDR);
+       TEST_ASSERT(!ret && get_policy == MPOL_BIND && get_nodemask == nodemask,
+                   "Policy should be MPOL_BIND and nodes match");
+
+       /* Test MPOL_DEFAULT policy */
+       ret = syscall(__NR_mbind, mem, total_size, MPOL_DEFAULT, NULL, 0, 0);
+       TEST_ASSERT(!ret, "mbind with MPOL_DEFAULT should succeed");
+       ret = syscall(__NR_get_mempolicy, &get_policy, &get_nodemask,
+                     maxnode, mem, MPOL_F_ADDR);
+       TEST_ASSERT(!ret && get_policy == MPOL_DEFAULT && get_nodemask == 0,
+                   "Policy should be MPOL_DEFAULT and nodes zero");
+
+       /* Test with invalid policy */
+       ret = syscall(__NR_mbind, mem, page_size, 999, &nodemask, maxnode, 0);
+       TEST_ASSERT(ret == -1 && errno == EINVAL,
+                   "mbind with invalid policy should fail with EINVAL");
+
+       TEST_ASSERT(munmap(mem, total_size) == 0, "munmap should succeed");
+}
+
+static void test_numa_allocation(int fd, size_t page_size, size_t total_size)
+{
+       unsigned long node0_mask = 1;  /* Node 0 */
+       unsigned long node1_mask = 2;  /* Node 1 */
+       unsigned long maxnode = 8;
+       void *pages[4];
+       int status[4];
+       char *mem;
+       int ret, i;
+
+       TEST_REQUIRE_NUMA_MULTIPLE_NODES();
+
+       /* Clean slate: deallocate all file space, if any */
+       ret = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 
total_size);
+       TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) should succeed");
+
+       mem = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+       TEST_ASSERT(mem != MAP_FAILED, "mmap should succeed");
+
+       for (i = 0; i < 4; i++)
+               pages[i] = (char *)mem + page_size * i;
+
+       /* Set NUMA policy after allocation */
+       memset(mem, 0xaa, page_size);
+       ret = syscall(__NR_mbind, pages[0], page_size, MPOL_BIND, &node0_mask, 
maxnode, 0);
+       TEST_ASSERT(!ret, "mbind after allocation page 0 to node 0 should 
succeed");
+       ret = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 
page_size);
+       TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) should succeed");
+
+       /* Set NUMA policy before allocation */
+       ret = syscall(__NR_mbind, pages[0], page_size * 2, MPOL_BIND, 
&node1_mask, maxnode, 0);
+       TEST_ASSERT(!ret, "mbind page 0, 1 to node 1 should succeed");
+       ret = syscall(__NR_mbind, pages[2], page_size * 2, MPOL_BIND, 
&node0_mask, maxnode, 0);
+       TEST_ASSERT(!ret, "mbind page 2, 3 to node 0 should succeed");
+       memset(mem, 0xaa, total_size);
+
+       /* Validate if pages are allocated on specified NUMA nodes */
+       ret = syscall(__NR_move_pages, 0, 4, pages, NULL, status, 0);
+       TEST_ASSERT(ret >= 0, "move_pages should succeed for status check");
+       TEST_ASSERT(status[0] == 1, "Page 0 should be allocated on node 1");
+       TEST_ASSERT(status[1] == 1, "Page 1 should be allocated on node 1");
+       TEST_ASSERT(status[2] == 0, "Page 2 should be allocated on node 0");
+       TEST_ASSERT(status[3] == 0, "Page 3 should be allocated on node 0");
+
+       /* Punch hole for all pages */
+       ret = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 
total_size);
+       TEST_ASSERT(!ret, "fallocate(PUNCH_HOLE) should succeed");
+
+       /* Change NUMA policy nodes and reallocate */
+       ret = syscall(__NR_mbind, pages[0], page_size * 2, MPOL_BIND, 
&node0_mask, maxnode, 0);
+       TEST_ASSERT(!ret, "mbind page 0, 1 to node 0 should succeed");
+       ret = syscall(__NR_mbind, pages[2], page_size * 2, MPOL_BIND, 
&node1_mask, maxnode, 0);
+       TEST_ASSERT(!ret, "mbind page 2, 3 to node 1 should succeed");
+       memset(mem, 0xaa, total_size);
+
+       ret = syscall(__NR_move_pages, 0, 4, pages, NULL, status, 0);
+       TEST_ASSERT(ret >= 0, "move_pages should succeed after reallocation");
+       TEST_ASSERT(status[0] == 0, "Page 0 should be allocated on node 0");
+       TEST_ASSERT(status[1] == 0, "Page 1 should be allocated on node 0");
+       TEST_ASSERT(status[2] == 1, "Page 2 should be allocated on node 1");
+       TEST_ASSERT(status[3] == 1, "Page 3 should be allocated on node 1");
+
+       TEST_ASSERT(munmap(mem, total_size) == 0, "munmap should succeed");
+}
+
 static void test_file_size(int fd, size_t page_size, size_t total_size)
 {
        struct stat sb;
@@ -275,7 +394,8 @@ static void test_with_type(unsigned long vm_type, uint64_t 
guest_memfd_flags,
        if (expect_mmap_allowed) {
                test_mmap_supported(fd, page_size, total_size);
                test_fault_overflow(fd, page_size, total_size);
-
+               test_mbind(fd, page_size, total_size);
+               test_numa_allocation(fd, page_size, total_size);
        } else {
                test_mmap_not_supported(fd, page_size, total_size);
        }
-- 
2.43.0


Reply via email to