[PATCH RFC v2 50/50] KVM: selftests: Test guest_memfd conversions with sample memory provider backing

From: Ackerley Tng via B4 Relay

Date: Thu Oct 22 2026 - 19:48:46 EST


From: Ackerley Tng <ackerleytng@xxxxxxxxxx>

Add fixture variant for the sample memory provider.

If /dev/mem_provider_sample cannot be opened because the module is not
loaded, the mps variant is skipped gracefully via TEST_REQUIRE().

Because PFN-backed memory cannot be pinned via GUP, expect pinning to fail
for the mps variant in the elevated_refcount test.

Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
---
tools/testing/selftests/kvm/include/kvm_util.h | 1 +
tools/testing/selftests/kvm/lib/kvm_util.c | 9 ++++-
.../kvm/x86/guest_memfd_conversions_test.c | 44 +++++++++++++++++++++-
3 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util.h b/tools/testing/selftests/kvm/include/kvm_util.h
index eca296539db79..1a1387f36a52c 100644
--- a/tools/testing/selftests/kvm/include/kvm_util.h
+++ b/tools/testing/selftests/kvm/include/kvm_util.h
@@ -1350,6 +1350,7 @@ static inline int pin_self_to_any_cpu(void)
return pin_task_to_any_cpu(pthread_self());
}

+int __pin_pages(void *vaddr, uint64_t size);
void pin_pages(void *vaddr, uint64_t size);
void unpin_pages(void);

diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index d42e201eba763..9793818f54d23 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -674,7 +674,7 @@ int __pin_task_to_cpu(pthread_t task, int cpu)

static int gup_test_fd = -1;

-void pin_pages(void *vaddr, uint64_t size)
+int __pin_pages(void *vaddr, uint64_t size)
{
const struct pin_longterm_test args = {
.addr = (uint64_t)vaddr,
@@ -685,7 +685,12 @@ void pin_pages(void *vaddr, uint64_t size)
gup_test_fd = __open_path_or_exit("/sys/kernel/debug/gup_test", O_RDWR,
"Is CONFIG_GUP_TEST enabled?");

- TEST_ASSERT_EQ(ioctl(gup_test_fd, PIN_LONGTERM_TEST_START, &args), 0);
+ return ioctl(gup_test_fd, PIN_LONGTERM_TEST_START, &args);
+}
+
+void pin_pages(void *vaddr, uint64_t size)
+{
+ TEST_ASSERT_EQ(__pin_pages(vaddr, size), 0);
}

void unpin_pages(void)
diff --git a/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c b/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
index 023df9cb9d4a6..b99e4cdbab8e6 100644
--- a/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
+++ b/tools/testing/selftests/kvm/x86/guest_memfd_conversions_test.c
@@ -16,11 +16,13 @@
#include "kselftest_harness.h"
#include "test_util.h"
#include "ucall_common.h"
+#include "../../../../samples/kvm/mem_provider_sample.h"

FIXTURE(gmem_conversions) {
struct kvm_vcpu *vcpu;
int gmem_fd;
int resource_fd;
+ bool expect_pin_failure;
/* HVA of the first byte of the memory mmap()-ed from gmem_fd. */
char *mem;
};
@@ -29,10 +31,14 @@ typedef FIXTURE_DATA(gmem_conversions) test_data_t;

FIXTURE_VARIANT(gmem_conversions) {
int (*create_resource)(size_t size);
+ uint64_t extra_flags;
+ bool expect_pin_failure;
};

FIXTURE_VARIANT_ADD(gmem_conversions, native) {
.create_resource = NULL,
+ .extra_flags = 0,
+ .expect_pin_failure = false,
};

static int create_tmpfs_resource(size_t size)
@@ -42,6 +48,34 @@ static int create_tmpfs_resource(size_t size)

FIXTURE_VARIANT_ADD(gmem_conversions, tmpfs) {
.create_resource = create_tmpfs_resource,
+ .extra_flags = 0,
+ .expect_pin_failure = false,
+};
+
+static int create_mps_resource(size_t size)
+{
+ struct mps_new_file nf = {
+ .size = size,
+ };
+ int dev_fd, fd;
+
+ dev_fd = open("/dev/mem_provider_sample", O_RDWR);
+ if (dev_fd < 0) {
+ TEST_REQUIRE(errno != ENOENT && errno != ENODEV);
+ TEST_FAIL("Failed to open /dev/mem_provider_sample: %d (%s)",
+ errno, strerror(errno));
+ }
+
+ fd = ioctl(dev_fd, MPS_NEW_FILE, &nf);
+ TEST_ASSERT(fd >= 0, "MPS_NEW_FILE failed: %d (%s)", errno, strerror(errno));
+ close(dev_fd);
+ return fd;
+}
+
+FIXTURE_VARIANT_ADD(gmem_conversions, mps) {
+ .create_resource = create_mps_resource,
+ .extra_flags = GUEST_MEMFD_FLAG_PFN_MODE,
+ .expect_pin_failure = true,
};

FIXTURE_SETUP(gmem_conversions) { }
@@ -75,10 +109,12 @@ static void gmem_conversions_do_setup(test_data_t *t,

vm = __vm_create_shape_with_one_vcpu(shape, &t->vcpu, nr_pages, guest_do_rmw);

+ t->expect_pin_failure = variant->expect_pin_failure;
t->resource_fd = -1;

if (variant->create_resource) {
- uint64_t flags = gmem_flags | GUEST_MEMFD_FLAG_USE_RESOURCE;
+ uint64_t flags = gmem_flags | GUEST_MEMFD_FLAG_USE_RESOURCE |
+ variant->extra_flags;

t->resource_fd = variant->create_resource(size);
TEST_ASSERT(t->resource_fd >= 0, "Failed to create backing resource");
@@ -506,6 +542,12 @@ GMEM_CONVERSION_MULTIPAGE_TEST_INIT_SHARED(elevated_refcount, 4)
{
int i;

+ if (t->expect_pin_failure) {
+ TEST_ASSERT(__pin_pages(t->mem + test_page * page_size, page_size) < 0,
+ "Pinning PFN-backed memory should fail");
+ return;
+ }
+
pin_pages(t->mem + test_page * page_size, page_size);

for (i = 0; i < nr_pages; i++)

--
2.56.0.257.gf7497c7d27-goog