[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250618112935.7629-8-shivankg@amd.com>
Date: Wed, 18 Jun 2025 11:29:35 +0000
From: Shivank Garg <shivankg@....com>
To: <seanjc@...gle.com>, <david@...hat.com>, <vbabka@...e.cz>,
<willy@...radead.org>, <akpm@...ux-foundation.org>, <shuah@...nel.org>,
<pbonzini@...hat.com>, <brauner@...nel.org>, <viro@...iv.linux.org.uk>
CC: <ackerleytng@...gle.com>, <paul@...l-moore.com>, <jmorris@...ei.org>,
<serge@...lyn.com>, <pvorel@...e.cz>, <bfoster@...hat.com>,
<tabba@...gle.com>, <vannapurve@...gle.com>, <chao.gao@...el.com>,
<bharata@....com>, <nikunj@....com>, <michael.day@....com>,
<yan.y.zhao@...el.com>, <Neeraj.Upadhyay@....com>, <thomas.lendacky@....com>,
<michael.roth@....com>, <aik@....com>, <jgg@...dia.com>,
<kalyazin@...zon.com>, <peterx@...hat.com>, <shivankg@....com>,
<jack@...e.cz>, <rppt@...nel.org>, <hch@...radead.org>,
<cgzones@...glemail.com>, <ira.weiny@...el.com>, <rientjes@...gle.com>,
<roypat@...zon.co.uk>, <ziy@...dia.com>, <matthew.brost@...el.com>,
<joshua.hahnjy@...il.com>, <rakie.kim@...com>, <byungchul@...com>,
<gourry@...rry.net>, <kent.overstreet@...ux.dev>,
<ying.huang@...ux.alibaba.com>, <apopple@...dia.com>,
<chao.p.peng@...el.com>, <amit@...radead.org>, <ddutile@...hat.com>,
<dan.j.williams@...el.com>, <ashish.kalra@....com>, <gshan@...hat.com>,
<jgowans@...zon.com>, <pankaj.gupta@....com>, <papaluri@....com>,
<yuzhao@...gle.com>, <suzuki.poulose@....com>, <quic_eberman@...cinc.com>,
<aneeshkumar.kizhakeveetil@....com>, <linux-fsdevel@...r.kernel.org>,
<linux-mm@...ck.org>, <linux-kernel@...r.kernel.org>,
<linux-security-module@...r.kernel.org>, <kvm@...r.kernel.org>,
<linux-kselftest@...r.kernel.org>, <linux-coco@...ts.linux.dev>
Subject: [RFC PATCH v8 7/7] KVM: guest_memfd: selftests: Add tests for mmap and NUMA policy support
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 <shivankg@....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
Powered by blists - more mailing lists