Skip to content

Commit bb2968a

Browse files
sean-jcbonzini
authored andcommitted
KVM: selftests: Add support for creating private memslots
Add support for creating "private" memslots via KVM_CREATE_GUEST_MEMFD and KVM_SET_USER_MEMORY_REGION2. Make vm_userspace_mem_region_add() a wrapper to its effective replacement, vm_mem_add(), so that private memslots are fully opt-in, i.e. don't require update all tests that add memory regions. Pivot on the KVM_MEM_PRIVATE flag instead of the validity of the "gmem" file descriptor so that simple tests can let vm_mem_add() do the heavy lifting of creating the guest memfd, but also allow the caller to pass in an explicit fd+offset so that fancier tests can do things like back multiple memslots with a single file. If the caller passes in a fd, dup() the fd so that (a) __vm_mem_region_delete() can close the fd associated with the memory region without needing yet another flag, and (b) so that the caller can safely close its copy of the fd without having to first destroy memslots. Co-developed-by: Ackerley Tng <ackerleytng@google.com> Signed-off-by: Ackerley Tng <ackerleytng@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Message-Id: <20231027182217.3615211-27-seanjc@google.com> Reviewed-by: Fuad Tabba <tabba@google.com> Tested-by: Fuad Tabba <tabba@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent 8d99e34 commit bb2968a

3 files changed

Lines changed: 73 additions & 31 deletions

File tree

tools/testing/selftests/kvm/include/kvm_util_base.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,26 @@ static inline uint64_t vm_get_stat(struct kvm_vm *vm, const char *stat_name)
431431

432432
void vm_create_irqchip(struct kvm_vm *vm);
433433

434+
static inline int __vm_create_guest_memfd(struct kvm_vm *vm, uint64_t size,
435+
uint64_t flags)
436+
{
437+
struct kvm_create_guest_memfd guest_memfd = {
438+
.size = size,
439+
.flags = flags,
440+
};
441+
442+
return __vm_ioctl(vm, KVM_CREATE_GUEST_MEMFD, &guest_memfd);
443+
}
444+
445+
static inline int vm_create_guest_memfd(struct kvm_vm *vm, uint64_t size,
446+
uint64_t flags)
447+
{
448+
int fd = __vm_create_guest_memfd(vm, size, flags);
449+
450+
TEST_ASSERT(fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_GUEST_MEMFD, fd));
451+
return fd;
452+
}
453+
434454
void vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags,
435455
uint64_t gpa, uint64_t size, void *hva);
436456
int __vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags,
@@ -439,6 +459,9 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
439459
enum vm_mem_backing_src_type src_type,
440460
uint64_t guest_paddr, uint32_t slot, uint64_t npages,
441461
uint32_t flags);
462+
void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
463+
uint64_t guest_paddr, uint32_t slot, uint64_t npages,
464+
uint32_t flags, int guest_memfd_fd, uint64_t guest_memfd_offset);
442465

443466
void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags);
444467
void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa);

tools/testing/selftests/kvm/include/test_util.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ static inline bool backing_src_is_shared(enum vm_mem_backing_src_type t)
142142
return vm_mem_backing_src_alias(t)->flag & MAP_SHARED;
143143
}
144144

145+
static inline bool backing_src_can_be_huge(enum vm_mem_backing_src_type t)
146+
{
147+
return t != VM_MEM_SRC_ANONYMOUS && t != VM_MEM_SRC_SHMEM;
148+
}
149+
145150
/* Aligns x up to the next multiple of size. Size must be a power of 2. */
146151
static inline uint64_t align_up(uint64_t x, uint64_t size)
147152
{

tools/testing/selftests/kvm/lib/kvm_util.c

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,8 @@ static void __vm_mem_region_delete(struct kvm_vm *vm,
669669
TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret));
670670
close(region->fd);
671671
}
672+
if (region->region.guest_memfd >= 0)
673+
close(region->region.guest_memfd);
672674

673675
free(region);
674676
}
@@ -870,36 +872,15 @@ void vm_set_user_memory_region(struct kvm_vm *vm, uint32_t slot, uint32_t flags,
870872
errno, strerror(errno));
871873
}
872874

873-
/*
874-
* VM Userspace Memory Region Add
875-
*
876-
* Input Args:
877-
* vm - Virtual Machine
878-
* src_type - Storage source for this region.
879-
* NULL to use anonymous memory.
880-
* guest_paddr - Starting guest physical address
881-
* slot - KVM region slot
882-
* npages - Number of physical pages
883-
* flags - KVM memory region flags (e.g. KVM_MEM_LOG_DIRTY_PAGES)
884-
*
885-
* Output Args: None
886-
*
887-
* Return: None
888-
*
889-
* Allocates a memory area of the number of pages specified by npages
890-
* and maps it to the VM specified by vm, at a starting physical address
891-
* given by guest_paddr. The region is created with a KVM region slot
892-
* given by slot, which must be unique and < KVM_MEM_SLOTS_NUM. The
893-
* region is created with the flags given by flags.
894-
*/
895-
void vm_userspace_mem_region_add(struct kvm_vm *vm,
896-
enum vm_mem_backing_src_type src_type,
897-
uint64_t guest_paddr, uint32_t slot, uint64_t npages,
898-
uint32_t flags)
875+
/* FIXME: This thing needs to be ripped apart and rewritten. */
876+
void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
877+
uint64_t guest_paddr, uint32_t slot, uint64_t npages,
878+
uint32_t flags, int guest_memfd, uint64_t guest_memfd_offset)
899879
{
900880
int ret;
901881
struct userspace_mem_region *region;
902882
size_t backing_src_pagesz = get_backing_src_pagesz(src_type);
883+
size_t mem_size = npages * vm->page_size;
903884
size_t alignment;
904885

905886
TEST_ASSERT(vm_adjust_num_guest_pages(vm->mode, npages) == npages,
@@ -952,7 +933,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
952933
/* Allocate and initialize new mem region structure. */
953934
region = calloc(1, sizeof(*region));
954935
TEST_ASSERT(region != NULL, "Insufficient Memory");
955-
region->mmap_size = npages * vm->page_size;
936+
region->mmap_size = mem_size;
956937

957938
#ifdef __s390x__
958939
/* On s390x, the host address must be aligned to 1M (due to PGSTEs) */
@@ -999,14 +980,38 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
999980
/* As needed perform madvise */
1000981
if ((src_type == VM_MEM_SRC_ANONYMOUS ||
1001982
src_type == VM_MEM_SRC_ANONYMOUS_THP) && thp_configured()) {
1002-
ret = madvise(region->host_mem, npages * vm->page_size,
983+
ret = madvise(region->host_mem, mem_size,
1003984
src_type == VM_MEM_SRC_ANONYMOUS ? MADV_NOHUGEPAGE : MADV_HUGEPAGE);
1004985
TEST_ASSERT(ret == 0, "madvise failed, addr: %p length: 0x%lx src_type: %s",
1005-
region->host_mem, npages * vm->page_size,
986+
region->host_mem, mem_size,
1006987
vm_mem_backing_src_alias(src_type)->name);
1007988
}
1008989

1009990
region->backing_src_type = src_type;
991+
992+
if (flags & KVM_MEM_GUEST_MEMFD) {
993+
if (guest_memfd < 0) {
994+
uint32_t guest_memfd_flags = 0;
995+
TEST_ASSERT(!guest_memfd_offset,
996+
"Offset must be zero when creating new guest_memfd");
997+
guest_memfd = vm_create_guest_memfd(vm, mem_size, guest_memfd_flags);
998+
} else {
999+
/*
1000+
* Install a unique fd for each memslot so that the fd
1001+
* can be closed when the region is deleted without
1002+
* needing to track if the fd is owned by the framework
1003+
* or by the caller.
1004+
*/
1005+
guest_memfd = dup(guest_memfd);
1006+
TEST_ASSERT(guest_memfd >= 0, __KVM_SYSCALL_ERROR("dup()", guest_memfd));
1007+
}
1008+
1009+
region->region.guest_memfd = guest_memfd;
1010+
region->region.guest_memfd_offset = guest_memfd_offset;
1011+
} else {
1012+
region->region.guest_memfd = -1;
1013+
}
1014+
10101015
region->unused_phy_pages = sparsebit_alloc();
10111016
sparsebit_set_num(region->unused_phy_pages,
10121017
guest_paddr >> vm->page_shift, npages);
@@ -1019,9 +1024,10 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
10191024
TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION2 IOCTL failed,\n"
10201025
" rc: %i errno: %i\n"
10211026
" slot: %u flags: 0x%x\n"
1022-
" guest_phys_addr: 0x%lx size: 0x%lx",
1027+
" guest_phys_addr: 0x%lx size: 0x%lx guest_memfd: %d\n",
10231028
ret, errno, slot, flags,
1024-
guest_paddr, (uint64_t) region->region.memory_size);
1029+
guest_paddr, (uint64_t) region->region.memory_size,
1030+
region->region.guest_memfd);
10251031

10261032
/* Add to quick lookup data structures */
10271033
vm_userspace_mem_region_gpa_insert(&vm->regions.gpa_tree, region);
@@ -1042,6 +1048,14 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
10421048
}
10431049
}
10441050

1051+
void vm_userspace_mem_region_add(struct kvm_vm *vm,
1052+
enum vm_mem_backing_src_type src_type,
1053+
uint64_t guest_paddr, uint32_t slot,
1054+
uint64_t npages, uint32_t flags)
1055+
{
1056+
vm_mem_add(vm, src_type, guest_paddr, slot, npages, flags, -1, 0);
1057+
}
1058+
10451059
/*
10461060
* Memslot to region
10471061
*

0 commit comments

Comments
 (0)