[RFC PATCH v2 07/10] KVM: selftests: Refactor userspace_mem_region creation out of vm_mem_add

From: Ryan Afranji
Date: Wed Jun 11 2025 - 17:19:36 EST


Refactor the creation and committing of userspace_mem_region to their
own functions so that they can reused by future TDX migration functions.

Signed-off-by: Ryan Afranji <afranji@xxxxxxxxxx>
---
tools/testing/selftests/kvm/lib/kvm_util.c | 147 +++++++++++++--------
1 file changed, 89 insertions(+), 58 deletions(-)

diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 2b442639ee2d..3c131718b81a 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -974,50 +974,47 @@ void vm_set_user_memory_region2(struct kvm_vm *vm, uint32_t slot, uint32_t flags
errno, strerror(errno));
}

-
-/* FIXME: This thing needs to be ripped apart and rewritten. */
-void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
- uint64_t guest_paddr, uint32_t slot, uint64_t npages,
- uint32_t flags, int guest_memfd, uint64_t guest_memfd_offset)
+static struct userspace_mem_region *vm_mem_region_alloc(struct kvm_vm *vm,
+ uint64_t guest_paddr,
+ uint32_t slot,
+ size_t npages,
+ uint32_t flags)
{
- int ret;
struct userspace_mem_region *region;
- size_t backing_src_pagesz = get_backing_src_pagesz(src_type);
- size_t mem_size = npages * vm->page_size;
- size_t alignment;

TEST_REQUIRE_SET_USER_MEMORY_REGION2();

TEST_ASSERT(vm_adjust_num_guest_pages(vm->mode, npages) == npages,
- "Number of guest pages is not compatible with the host. "
- "Try npages=%d", vm_adjust_num_guest_pages(vm->mode, npages));
+ "Number of guest pages is not compatible with the host. "
+ "Try npages=%d", vm_adjust_num_guest_pages(vm->mode, npages));

TEST_ASSERT((guest_paddr % vm->page_size) == 0, "Guest physical "
- "address not on a page boundary.\n"
- " guest_paddr: 0x%lx vm->page_size: 0x%x",
- guest_paddr, vm->page_size);
+ "address not on a page boundary.\n"
+ " guest_paddr: 0x%lx vm->page_size: 0x%x",
+ guest_paddr, vm->page_size);
TEST_ASSERT((((guest_paddr >> vm->page_shift) + npages) - 1)
- <= vm->max_gfn, "Physical range beyond maximum "
- "supported physical address,\n"
- " guest_paddr: 0x%lx npages: 0x%lx\n"
- " vm->max_gfn: 0x%lx vm->page_size: 0x%x",
- guest_paddr, npages, vm->max_gfn, vm->page_size);
+ <= vm->max_gfn, "Physical range beyond maximum "
+ "supported physical address,\n"
+ " guest_paddr: 0x%lx npages: 0x%lx\n"
+ " vm->max_gfn: 0x%lx vm->page_size: 0x%x",
+ guest_paddr, npages, vm->max_gfn, vm->page_size);

/*
* Confirm a mem region with an overlapping address doesn't
* already exist.
*/
region = (struct userspace_mem_region *) userspace_mem_region_find(
- vm, guest_paddr, (guest_paddr + npages * vm->page_size) - 1);
+ vm, guest_paddr,
+ (guest_paddr + npages * vm->page_size) - 1);
if (region != NULL)
TEST_FAIL("overlapping userspace_mem_region already "
- "exists\n"
- " requested guest_paddr: 0x%lx npages: 0x%lx "
- "page_size: 0x%x\n"
- " existing guest_paddr: 0x%lx size: 0x%lx",
- guest_paddr, npages, vm->page_size,
- (uint64_t) region->region.guest_phys_addr,
- (uint64_t) region->region.memory_size);
+ "exists\n"
+ " requested guest_paddr: 0x%lx npages: 0x%lx "
+ "page_size: 0x%x\n"
+ " existing guest_paddr: 0x%lx size: 0x%lx",
+ guest_paddr, npages, vm->page_size,
+ (uint64_t) region->region.guest_phys_addr,
+ (uint64_t) region->region.memory_size);

/* Confirm no region with the requested slot already exists. */
hash_for_each_possible(vm->regions.slot_hash, region, slot_node,
@@ -1026,19 +1023,73 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
continue;

TEST_FAIL("A mem region with the requested slot "
- "already exists.\n"
- " requested slot: %u paddr: 0x%lx npages: 0x%lx\n"
- " existing slot: %u paddr: 0x%lx size: 0x%lx",
- slot, guest_paddr, npages,
- region->region.slot,
- (uint64_t) region->region.guest_phys_addr,
- (uint64_t) region->region.memory_size);
+ "already exists.\n"
+ " requested slot: %u paddr: 0x%lx npages: 0x%lx\n"
+ " existing slot: %u paddr: 0x%lx size: 0x%lx",
+ slot, guest_paddr, npages,
+ region->region.slot,
+ (uint64_t) region->region.guest_phys_addr,
+ (uint64_t) region->region.memory_size);
}

/* Allocate and initialize new mem region structure. */
region = calloc(1, sizeof(*region));
TEST_ASSERT(region != NULL, "Insufficient Memory");
- region->mmap_size = mem_size;
+
+ region->unused_phy_pages = sparsebit_alloc();
+ if (vm_arch_has_protected_memory(vm))
+ region->protected_phy_pages = sparsebit_alloc();
+ sparsebit_set_num(region->unused_phy_pages,
+ guest_paddr >> vm->page_shift, npages);
+ region->region.slot = slot;
+ region->region.flags = flags;
+ region->region.guest_phys_addr = guest_paddr;
+ region->region.memory_size = npages * vm->page_size;
+
+ region->mmap_start = NULL;
+ region->mmap_size = 0;
+ region->host_mem = NULL;
+ region->fd = -1;
+
+ return region;
+}
+
+static void userspace_mem_region_commit(struct kvm_vm *vm,
+ struct userspace_mem_region *region)
+{
+ int ret;
+
+ region->region.userspace_addr = (uintptr_t) region->host_mem;
+ ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION2, &region->region);
+ TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION2 IOCTL failed,\n"
+ " rc: %i errno: %i\n"
+ " slot: %u flags: 0x%x\n"
+ " guest_phys_addr: 0x%lx size: 0x%lx guest_memfd: %d",
+ ret, errno, region->region.slot, region->region.flags,
+ (uint64_t) region->region.guest_phys_addr,
+ (uint64_t) region->region.memory_size,
+ region->region.guest_memfd);
+
+ /* Add to quick lookup data structures */
+ vm_userspace_mem_region_gpa_insert(&vm->regions.gpa_tree, region);
+ vm_userspace_mem_region_hva_insert(&vm->regions.hva_tree, region);
+ hash_add(vm->regions.slot_hash, &region->slot_node,
+ region->region.slot);
+}
+
+/* FIXME: This thing needs to be ripped apart and rewritten. */
+void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
+ uint64_t guest_paddr, uint32_t slot, uint64_t npages,
+ uint32_t flags, int guest_memfd, uint64_t guest_memfd_offset)
+{
+ int ret;
+ struct userspace_mem_region *region;
+ size_t backing_src_pagesz = get_backing_src_pagesz(src_type);
+ size_t mem_size = npages * vm->page_size;
+ size_t alignment;
+
+ region = vm_mem_region_alloc(vm, guest_paddr, slot, npages,
+ flags);

#ifdef __s390x__
/* On s390x, the host address must be aligned to 1M (due to PGSTEs) */
@@ -1058,6 +1109,8 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,

TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz));

+ region->mmap_size = mem_size;
+
/* Add enough memory to align up if necessary */
if (alignment > 1)
region->mmap_size += alignment;
@@ -1117,29 +1170,7 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
region->region.guest_memfd = -1;
}

- region->unused_phy_pages = sparsebit_alloc();
- if (vm_arch_has_protected_memory(vm))
- region->protected_phy_pages = sparsebit_alloc();
- sparsebit_set_num(region->unused_phy_pages,
- guest_paddr >> vm->page_shift, npages);
- region->region.slot = slot;
- region->region.flags = flags;
- region->region.guest_phys_addr = guest_paddr;
- region->region.memory_size = npages * vm->page_size;
- region->region.userspace_addr = (uintptr_t) region->host_mem;
- ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION2, &region->region);
- TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION2 IOCTL failed,\n"
- " rc: %i errno: %i\n"
- " slot: %u flags: 0x%x\n"
- " guest_phys_addr: 0x%lx size: 0x%lx guest_memfd: %d",
- ret, errno, slot, flags,
- guest_paddr, (uint64_t) region->region.memory_size,
- region->region.guest_memfd);
-
- /* Add to quick lookup data structures */
- vm_userspace_mem_region_gpa_insert(&vm->regions.gpa_tree, region);
- vm_userspace_mem_region_hva_insert(&vm->regions.hva_tree, region);
- hash_add(vm->regions.slot_hash, &region->slot_node, slot);
+ userspace_mem_region_commit(vm, region);

/* If shared memory, create an alias. */
if (region->fd >= 0) {
--
2.50.0.rc1.591.g9c95f17f64-goog