Skip to contentSkip to conversation

KVM ARM64 pre_fault_memory

PATCH Superseded Jack Thomson 3 participants 12 messages Oct 13, 2025 – Nov 13, 2025

Subsystems (5)
Documentation KVM Kselftest arm64 +1 more subsystems

v2 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v2 cover KVM ARM64 pre_fault_memory

From: Jack Thomson <[email protected]>

This patch series adds ARM64 support for the KVM_PRE_FAULT_MEMORY feature, which was previously only available on x86 [1]. This allows us to reduce the number of stage-2 faults during execution. This is of benefit in post-copy migration scenarios, particularly in memory intensive applications, where we are experiencing high latencies due to the stage-2 faults.

Patch Overview:

- The first patch adds support for the KVM_PRE_FAULT_MEMORY ioctl on arm64.

- The second patch fixes an issue with unaligned mmap allocations in the selftests.

- The third patch updates the pre_fault_memory_test to support arm64.

- The last patch extends the pre_fault_memory_test to cover different vm memory backings.

=== Changes Since v1 [2] ===

Addressing feedback from Oliver:

- No pre-fault flag is passed to user_mem_abort() or gmem_abort() now aborts are synthesized.
- Remove retry loop from kvm_arch_vcpu_pre_fault_memory()

[1]: https://lore.kernel.org/kvm/[email protected]
[2]: https://lore.kernel.org/all/[email protected]

Jack Thomson (4):
KVM: arm64: Add pre_fault_memory implementation
KVM: selftests: Fix unaligned mmap allocations
KVM: selftests: Enable pre_fault_memory_test for arm64
KVM: selftests: Add option for different backing in pre-fault tests

 Documentation/virt/kvm/api.rst                |   3 +-
 arch/arm64/kvm/Kconfig                        |   1 +
 arch/arm64/kvm/arm.c                          |   1 +
 arch/arm64/kvm/mmu.c                          |  73 +++++++++++-
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 tools/testing/selftests/kvm/lib/kvm_util.c    |  12 +-
 .../selftests/kvm/pre_fault_memory_test.c     | 110 +++++++++++++-----
 7 files changed, 163 insertions(+), 38 deletions(-)

base-commit: 42188667be387867d2bf763d028654cbad046f7b

Signature · 1 line
2.43.0
#1 lore raw

PATCH v2 1/4 KVM: arm64: Add pre_fault_memory implementation

From: Jack Thomson <[email protected]>

Add kvm_arch_vcpu_pre_fault_memory() for arm64. The implementation hands off the stage-2 faulting logic to either gmem_abort() or user_mem_abort().

Add an optional page_size output parameter to user_mem_abort() to return the VMA page size, which is needed when pre-faulting.

Update the documentation to clarify x86 specific behaviour.

---
 Documentation/virt/kvm/api.rst |  3 +-
 arch/arm64/kvm/Kconfig         |  1 +
 arch/arm64/kvm/arm.c           |  1 +
 arch/arm64/kvm/mmu.c           | 73 ++++++++++++++++++++++++++++++++--
 4 files changed, 73 insertions(+), 5 deletions(-)
Documentation/virt/kvm/api.rst +2 -1
index c17a87a0a5ac..9e8cc4eb505d 100644
@@ -6461,7 +6461,8 @@ Errors:
 KVM_PRE_FAULT_MEMORY populates KVM's stage-2 page tables used to map memory
 for the current vCPU state.  KVM maps memory as if the vCPU generated a
 stage-2 read page fault, e.g. faults in memory as needed, but doesn't break
-CoW.  However, KVM does not mark any newly created stage-2 PTE as Accessed.
+CoW.  However, on x86, KVM does not mark any newly created stage-2 PTE as
+Accessed.
 
 In the case of confidential VM types where there is an initial set up of
 private guest memory before the guest is 'finalized'/measured, this ioctl
arch/arm64/kvm/Kconfig +1 -0
index bff62e75d681..1ac0605f86cb 100644
@@ -25,6 +25,7 @@ menuconfig KVM
 	select HAVE_KVM_CPU_RELAX_INTERCEPT
 	select KVM_MMIO
 	select KVM_GENERIC_DIRTYLOG_READ_PROTECT
+	select KVM_GENERIC_PRE_FAULT_MEMORY
 	select KVM_XFER_TO_GUEST_WORK
 	select KVM_VFIO
 	select HAVE_KVM_DIRTY_RING_ACQ_REL
arch/arm64/kvm/arm.c +1 -0
index 888f7c7abf54..65654a742864 100644
@@ -322,6 +322,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_IRQFD_RESAMPLE:
 	case KVM_CAP_COUNTER_OFFSET:
 	case KVM_CAP_ARM_WRITABLE_IMP_ID_REGS:
+	case KVM_CAP_PRE_FAULT_MEMORY:
 		r = 1;
 		break;
 	case KVM_CAP_SET_GUEST_DEBUG2:
arch/arm64/kvm/mmu.c +69 -4

94 lines not on this page · raw message

Signature · 1 line
2.43.0
#2 lore raw

Re: [PATCH v2 1/4] KVM: arm64: Add pre_fault_memory implementation

Hi

Quoted 10 lines · Jack Thomson

From: Jack Thomson <[email protected]>

Add kvm_arch_vcpu_pre_fault_memory() for arm64. The implementation hands off the stage-2 faulting logic to either gmem_abort() or user_mem_abort().

Add an optional page_size output parameter to user_mem_abort() to return the VMA page size, which is needed when pre-faulting.

Update the documentation to clarify x86 specific behaviour.

Thanks for the patch ! Do we care about faulting beyond the requested range ? I understand this doesn't happen for anything that is not backed by gmem (which might change with hugetlbfs support) or normal
VMs. But for coco VMs this might affect the measurement or even cause
failure in "pre-faulting" because of the extra security checks.
(e.g., trying to fault in twice, because the range is backed by say,
1G page).

Of course these could be addressed via a separate patch, when this becomes a real requirement.

One way to solve this could be pass on the "pagesize" as the input parameter which could force the backend to limit the vma_pagesize that gets used for the stage2 mapping.

Quoted 109 lines
---
  Documentation/virt/kvm/api.rst |  3 +-
  arch/arm64/kvm/Kconfig         |  1 +
  arch/arm64/kvm/arm.c           |  1 +
  arch/arm64/kvm/mmu.c           | 73 ++++++++++++++++++++++++++++++++--
  4 files changed, 73 insertions(+), 5 deletions(-)
Documentation/virt/kvm/api.rst +2 -1
index c17a87a0a5ac..9e8cc4eb505d 100644
@@ -6461,7 +6461,8 @@ Errors:
  KVM_PRE_FAULT_MEMORY populates KVM's stage-2 page tables used to map memory
  for the current vCPU state.  KVM maps memory as if the vCPU generated a
  stage-2 read page fault, e.g. faults in memory as needed, but doesn't break
-CoW.  However, KVM does not mark any newly created stage-2 PTE as Accessed.
+CoW.  However, on x86, KVM does not mark any newly created stage-2 PTE as
+Accessed.
 
  In the case of confidential VM types where there is an initial set up of
  private guest memory before the guest is 'finalized'/measured, this ioctl
arch/arm64/kvm/Kconfig +1 -0
index bff62e75d681..1ac0605f86cb 100644
@@ -25,6 +25,7 @@ menuconfig KVM
  	select HAVE_KVM_CPU_RELAX_INTERCEPT
  	select KVM_MMIO
  	select KVM_GENERIC_DIRTYLOG_READ_PROTECT
+	select KVM_GENERIC_PRE_FAULT_MEMORY
  	select KVM_XFER_TO_GUEST_WORK
  	select KVM_VFIO
  	select HAVE_KVM_DIRTY_RING_ACQ_REL
arch/arm64/kvm/arm.c +1 -0
index 888f7c7abf54..65654a742864 100644
@@ -322,6 +322,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
  	case KVM_CAP_IRQFD_RESAMPLE:
  	case KVM_CAP_COUNTER_OFFSET:
  	case KVM_CAP_ARM_WRITABLE_IMP_ID_REGS:
+	case KVM_CAP_PRE_FAULT_MEMORY:
  		r = 1;
  		break;
  	case KVM_CAP_SET_GUEST_DEBUG2:
arch/arm64/kvm/mmu.c +29 -4
index a36426ccd9b5..82f122e4b08c 100644
@@ -1597,8 +1597,8 @@ static int gmem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
 
  static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
  			  struct kvm_s2_trans *nested,
-			  struct kvm_memory_slot *memslot, unsigned long hva,
-			  bool fault_is_perm)
+			  struct kvm_memory_slot *memslot, long *page_size,
+			  unsigned long hva, bool fault_is_perm)
  {
  	int ret = 0;
  	bool topup_memcache;
@@ -1871,6 +1871,9 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
  	kvm_release_faultin_page(kvm, page, !!ret, writable);
  	kvm_fault_unlock(kvm);
 
+	if (page_size)
+		*page_size = vma_pagesize;
+
  	/* Mark the page dirty only if the fault is handled successfully */
  	if (writable && !ret)
  		mark_page_dirty_in_slot(kvm, memslot, gfn);
@@ -2069,8 +2072,8 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
  		ret = gmem_abort(vcpu, fault_ipa, nested, memslot,
  				 esr_fsc_is_permission_fault(esr));
  	else
-		ret = user_mem_abort(vcpu, fault_ipa, nested, memslot, hva,
-				     esr_fsc_is_permission_fault(esr));
+		ret = user_mem_abort(vcpu, fault_ipa, nested, memslot, NULL,
+				     hva, esr_fsc_is_permission_fault(esr));
  	if (ret == 0)
  		ret = 1;
  out:
@@ -2446,3 +2449,65 @@ void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled)
 
  	trace_kvm_toggle_cache(*vcpu_pc(vcpu), was_enabled, now_enabled);
  }
+
+long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
+				    struct kvm_pre_fault_memory *range)
+{
+	int ret, idx;
+	hva_t hva;
+	phys_addr_t end;
+	struct kvm_memory_slot *memslot;
+	struct kvm_vcpu_fault_info stored_fault, *fault_info;
+
+	long page_size = PAGE_SIZE;
+	phys_addr_t ipa = range->gpa;
+	gfn_t gfn = gpa_to_gfn(range->gpa);
+
+	idx = srcu_read_lock(&vcpu->kvm->srcu);
+
+	if (ipa >= kvm_phys_size(vcpu->arch.hw_mmu)) {
+		ret = -ENOENT;
+		goto out_unlock;
+	}
+
+	memslot = gfn_to_memslot(vcpu->kvm, gfn);

arch/arm64/kvm/mmu.c void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled) · line 2483

Quoted 10 lines
arch/arm64/kvm/mmu.c +10 -0
+	if (!memslot) {
+		ret = -ENOENT;
+		goto out_unlock;
+	}
+
+	fault_info = &vcpu->arch.fault;
+	stored_fault = *fault_info;
+
+	/* Generate a synthetic abort for the pre-fault address */
+	fault_info->esr_el2 = FIELD_PREP(ESR_ELx_EC_MASK, ESR_ELx_EC_DABT_CUR);

minor nit: Any reason why we don't use ESR_ELx_EC_DABT_LOW ? We always get that for a data abort from the guest. Otherwise, this looks good to me.

Suzuki

Quoted 20 lines
arch/arm64/kvm/mmu.c +20 -0
+	fault_info->esr_el2 &= ~ESR_ELx_ISV;
+	fault_info->esr_el2 |= ESR_ELx_FSC_FAULT_L(KVM_PGTABLE_LAST_LEVEL);
+
+	fault_info->hpfar_el2 = HPFAR_EL2_NS |
+		FIELD_PREP(HPFAR_EL2_FIPA, ipa >> 12);
+
+	if (kvm_slot_has_gmem(memslot)) {
+		ret = gmem_abort(vcpu, ipa, NULL, memslot, false);
+	} else {
+		hva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);
+		if (kvm_is_error_hva(hva)) {
+			ret = -EFAULT;
+			goto out;
+		}
+		ret = user_mem_abort(vcpu, ipa, NULL, memslot, &page_size, hva,
+				     false);
+	}
+
+	if (ret < 0)
+		goto out;

arch/arm64/kvm/mmu.c void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled) · line 2513

Quoted 10 lines
arch/arm64/kvm/mmu.c +10 -0
+
+	end = (range->gpa & ~(page_size - 1)) + page_size;
+	ret = min(range->size, end - range->gpa);
+
+out:
+	*fault_info = stored_fault;
+out_unlock:
+	srcu_read_unlock(&vcpu->kvm->srcu, idx);
+	return ret;
+}

m

#3 lore raw

PATCH v2 2/4 KVM: selftests: Fix unaligned mmap allocations

In reply to Jack Thomson

From: Jack Thomson <[email protected]>

When creating a VM using mmap with huge pages, and the memory amount does not align with the underlying page size. The stored mmap_size value does not account for the fact that mmap will automatically align the length to a multiple of the underlying page size. During the teardown of the test, munmap is used. However, munmap requires the length to be a multiple of the underlying page size.

Update the vm_mem_add method to ensure the mmap_size is aligned to the underlying page size.

---
 tools/testing/selftests/kvm/lib/kvm_util.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)
tools/testing/selftests/kvm/lib/kvm_util.c +5 -7
index c3f5142b0a54..b106fbed999c 100644
@@ -1051,7 +1051,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	/* Allocate and initialize new mem region structure. */
 	region = calloc(1, sizeof(*region));
 	TEST_ASSERT(region != NULL, "Insufficient Memory");
-	region->mmap_size = mem_size;
 
 #ifdef __s390x__
 	/* On s390x, the host address must be aligned to 1M (due to PGSTEs) */
@@ -1060,6 +1059,11 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	alignment = 1;
 #endif
 
+	alignment = max(backing_src_pagesz, alignment);
+	region->mmap_size = align_up(mem_size, alignment);
+
+	TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz));
+
 	/*
 	 * When using THP mmap is not guaranteed to returned a hugepage aligned
 	 * address so we have to pad the mmap. Padding is not needed for HugeTLB
@@ -1067,12 +1071,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	 * page size.
 	 */
 	if (src_type == VM_MEM_SRC_ANONYMOUS_THP)
-		alignment = max(backing_src_pagesz, alignment);
-
-	TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz));
-
-	/* Add enough memory to align up if necessary */
-	if (alignment > 1)
 		region->mmap_size += alignment;
 
 	region->fd = -1;
Signature · 1 line
2.43.0
#4 lore raw

Re: [PATCH v2 2/4] KVM: selftests: Fix unaligned mmap allocations

Quoted 8 lines · Jack Thomson

From: Jack Thomson <[email protected]>

When creating a VM using mmap with huge pages, and the memory amount does not align with the underlying page size. The stored mmap_size value does not account for the fact that mmap will automatically align the length to a multiple of the underlying page size. During the teardown of the test, munmap is used. However, munmap requires the length to be a multiple of the underlying page size.

What happens when selftests use the wrong map_size? E.g. is munmap() silently failing? If so, then I should probably take this particular patch through kvm-x86/gmem, otherwise it means we'll start getting asserts due to:

3223560c93eb ("KVM: selftests: Define wrappers for common syscalls to assert success")

If munmap() isn't failing, then that begs the question of what this patch is actually doing :-)

Quoted 48 lines

Update the vm_mem_add method to ensure the mmap_size is aligned to the underlying page size.

---
 tools/testing/selftests/kvm/lib/kvm_util.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)
tools/testing/selftests/kvm/lib/kvm_util.c +5 -7
index c3f5142b0a54..b106fbed999c 100644
@@ -1051,7 +1051,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	/* Allocate and initialize new mem region structure. */
 	region = calloc(1, sizeof(*region));
 	TEST_ASSERT(region != NULL, "Insufficient Memory");
-	region->mmap_size = mem_size;
 
 #ifdef __s390x__
 	/* On s390x, the host address must be aligned to 1M (due to PGSTEs) */
@@ -1060,6 +1059,11 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	alignment = 1;
 #endif
 
+	alignment = max(backing_src_pagesz, alignment);
+	region->mmap_size = align_up(mem_size, alignment);
+
+	TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz));
+
 	/*
 	 * When using THP mmap is not guaranteed to returned a hugepage aligned
 	 * address so we have to pad the mmap. Padding is not needed for HugeTLB
@@ -1067,12 +1071,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
 	 * page size.
 	 */
 	if (src_type == VM_MEM_SRC_ANONYMOUS_THP)
-		alignment = max(backing_src_pagesz, alignment);
-
-	TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz));
-
-	/* Add enough memory to align up if necessary */
-	if (alignment > 1)
 		region->mmap_size += alignment;
 
 	region->fd = -1;
Signature · 1 line
2.43.0
#5 lore raw

Re: [PATCH v2 2/4] KVM: selftests: Fix unaligned mmap allocations

Quoted 10 lines · Sean Christopherson
Quoted 8 lines · Jack Thomson

From: Jack Thomson <[email protected]>

When creating a VM using mmap with huge pages, and the memory amount does not align with the underlying page size. The stored mmap_size value does not account for the fact that mmap will automatically align the length to a multiple of the underlying page size. During the teardown of the test, munmap is used. However, munmap requires the length to be a multiple of the underlying page size.

Quoted 9 lines

What happens when selftests use the wrong map_size? E.g. is munmap() silently failing? If so, then I should probably take this particular patch through kvm-x86/gmem, otherwise it means we'll start getting asserts due to:

3223560c93eb ("KVM: selftests: Define wrappers for common syscalls to assert success")

If munmap() isn't failing, then that begs the question of what this patch is actually doing :-)

Hi Sean, sorry I completely missed your reply.

Yeah currently with a misaligned map_size it causes munmap() to fail, I noticed when tested with different backings.

Quoted 48 lines
Quoted 48 lines

Update the vm_mem_add method to ensure the mmap_size is aligned to the underlying page size.

---
  tools/testing/selftests/kvm/lib/kvm_util.c | 12 +++++-------
  1 file changed, 5 insertions(+), 7 deletions(-)
tools/testing/selftests/kvm/lib/kvm_util.c +5 -7
index c3f5142b0a54..b106fbed999c 100644
@@ -1051,7 +1051,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
  	/* Allocate and initialize new mem region structure. */
  	region = calloc(1, sizeof(*region));
  	TEST_ASSERT(region != NULL, "Insufficient Memory");
-	region->mmap_size = mem_size;
 
  #ifdef __s390x__
  	/* On s390x, the host address must be aligned to 1M (due to PGSTEs) */
@@ -1060,6 +1059,11 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
  	alignment = 1;
  #endif
 
+	alignment = max(backing_src_pagesz, alignment);
+	region->mmap_size = align_up(mem_size, alignment);
+
+	TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz));
+
  	/*
  	 * When using THP mmap is not guaranteed to returned a hugepage aligned
  	 * address so we have to pad the mmap. Padding is not needed for HugeTLB
@@ -1067,12 +1071,6 @@ void vm_mem_add(struct kvm_vm *vm, enum vm_mem_backing_src_type src_type,
  	 * page size.
  	 */
  	if (src_type == VM_MEM_SRC_ANONYMOUS_THP)
-		alignment = max(backing_src_pagesz, alignment);
-
-	TEST_ASSERT_EQ(guest_paddr, align_up(guest_paddr, backing_src_pagesz));
-
-	/* Add enough memory to align up if necessary */
-	if (alignment > 1)
  		region->mmap_size += alignment;
 
  	region->fd = -1;
Signature · 1 line
2.43.0
Signature · 2 lines
Thanks,
Jack
#6 lore raw

Re: [PATCH v2 2/4] KVM: selftests: Fix unaligned mmap allocations

Quoted 19 lines · Jack Thomson
Quoted 16 lines · Sean Christopherson
Quoted 8 lines · Jack Thomson

From: Jack Thomson <[email protected]>

When creating a VM using mmap with huge pages, and the memory amount does not align with the underlying page size. The stored mmap_size value does not account for the fact that mmap will automatically align the length to a multiple of the underlying page size. During the teardown of the test, munmap is used. However, munmap requires the length to be a multiple of the underlying page size.

What happens when selftests use the wrong map_size? E.g. is munmap() silently failing? If so, then I should probably take this particular patch through kvm-x86/gmem, otherwise it means we'll start getting asserts due to:

3223560c93eb ("KVM: selftests: Define wrappers for common syscalls to assert success")

Quoted 8 lines
Quoted 3 lines

If munmap() isn't failing, then that begs the question of what this patch is actually doing :-)

Hi Sean, sorry I completely missed your reply.

Yeah currently with a misaligned map_size it causes munmap() to fail, I noticed when tested with different backings.

Exactly which tests fail? I ask because I'm not sure we want to fix this by having vm_mem_add() paper over test issues (I vaguely recall looking at this in the past, but I can't find or recall the details).

#7 lore raw

Re: [PATCH v2 2/4] KVM: selftests: Fix unaligned mmap allocations

Quoted 24 lines · Sean Christopherson
Quoted 23 lines · Jack Thomson
Quoted 19 lines · Sean Christopherson
Quoted 8 lines · Jack Thomson

From: Jack Thomson <[email protected]>

When creating a VM using mmap with huge pages, and the memory amount does not align with the underlying page size. The stored mmap_size value does not account for the fact that mmap will automatically align the length to a multiple of the underlying page size. During the teardown of the test, munmap is used. However, munmap requires the length to be a multiple of the underlying page size.

What happens when selftests use the wrong map_size? E.g. is munmap() silently failing? If so, then I should probably take this particular patch through kvm-x86/gmem, otherwise it means we'll start getting asserts due to:

3223560c93eb ("KVM: selftests: Define wrappers for common syscalls to assert success")

If munmap() isn't failing, then that begs the question of what this patch is actually doing :-)

Quoted 8 lines
Quoted 4 lines

Hi Sean, sorry I completely missed your reply.

Yeah currently with a misaligned map_size it causes munmap() to fail, I noticed when tested with different backings.

Exactly which tests fail? I ask because I'm not sure we want to fix this by having vm_mem_add() paper over test issues (I vaguely recall looking at this in the past, but I can't find or recall the details).

The test failures happened with pre_faulting tests after adding the option to change the backing page size [1]. If you'd prefer to have the test handle with this I'll update there instead.

[1]
https://lore.kernel.org/all/[email protected]

Signature · 2 lines
Thanks,
Jack
#8 lore raw

Re: [PATCH v2 2/4] KVM: selftests: Fix unaligned mmap allocations

Quoted 30 lines · Jack Thomson
Quoted 29 lines · Sean Christopherson
Quoted 27 lines · Jack Thomson
Quoted 10 lines · Sean Christopherson
Quoted 8 lines · Jack Thomson

From: Jack Thomson <[email protected]>

When creating a VM using mmap with huge pages, and the memory amount does not align with the underlying page size. The stored mmap_size value does not account for the fact that mmap will automatically align the length to a multiple of the underlying page size. During the teardown of the test, munmap is used. However, munmap requires the length to be a multiple of the underlying page size.

Quoted 9 lines

What happens when selftests use the wrong map_size? E.g. is munmap() silently failing? If so, then I should probably take this particular patch through kvm-x86/gmem, otherwise it means we'll start getting asserts due to:

3223560c93eb ("KVM: selftests: Define wrappers for common syscalls to assert success")

If munmap() isn't failing, then that begs the question of what this patch is actually doing :-)

Hi Sean, sorry I completely missed your reply.

Yeah currently with a misaligned map_size it causes munmap() to fail, I noticed when tested with different backings.

Quoted 7 lines
Quoted 3 lines

Exactly which tests fail? I ask because I'm not sure we want to fix this by having vm_mem_add() paper over test issues (I vaguely recall looking at this in the past, but I can't find or recall the details).

The test failures happened with pre_faulting tests after adding the option to change the backing page size [1]. If you'd prefer to have the test handle with this I'll update there instead.

Ah, yeah, that's a test bug introduced by your patch. I can't find the thread, but the issue of hugepage aligntment in vm_mem_add() has come up in the past, and IIRC the conclusion was that tests need to handle the size+alignment, because having the library force the alignment risking papering over test bugs/flaws. And I think there may have even been cases where it introduced failures, as some tests deliberately wanted to do weird things?

E.g. not updating the pre-faulting test to use the "correct" size+alignment means the test is missing easy coverage for hugepages, since KVM won't create huge mappings in stage-2 due to the memslot not being sized+aligned.

#9 lore raw

Re: [PATCH v2 2/4] KVM: selftests: Fix unaligned mmap allocations

Quoted 46 lines · Sean Christopherson
Quoted 30 lines · Jack Thomson
Quoted 29 lines · Sean Christopherson
Quoted 27 lines · Jack Thomson
Quoted 10 lines · Sean Christopherson
Quoted 8 lines · Jack Thomson

From: Jack Thomson <[email protected]>

When creating a VM using mmap with huge pages, and the memory amount does not align with the underlying page size. The stored mmap_size value does not account for the fact that mmap will automatically align the length to a multiple of the underlying page size. During the teardown of the test, munmap is used. However, munmap requires the length to be a multiple of the underlying page size.

Quoted 9 lines

What happens when selftests use the wrong map_size? E.g. is munmap() silently failing? If so, then I should probably take this particular patch through kvm-x86/gmem, otherwise it means we'll start getting asserts due to:

3223560c93eb ("KVM: selftests: Define wrappers for common syscalls to assert success")

If munmap() isn't failing, then that begs the question of what this patch is actually doing :-)

Hi Sean, sorry I completely missed your reply.

Yeah currently with a misaligned map_size it causes munmap() to fail, I noticed when tested with different backings.

Quoted 7 lines
Quoted 3 lines

Exactly which tests fail? I ask because I'm not sure we want to fix this by having vm_mem_add() paper over test issues (I vaguely recall looking at this in the past, but I can't find or recall the details).

The test failures happened with pre_faulting tests after adding the option to change the backing page size [1]. If you'd prefer to have the test handle with this I'll update there instead.

Ah, yeah, that's a test bug introduced by your patch. I can't find the thread, but the issue of hugepage aligntment in vm_mem_add() has come up in the past, and IIRC the conclusion was that tests need to handle the size+alignment, because having the library force the alignment risking papering over test bugs/flaws. And I think there may have even been cases where it introduced failures, as some tests deliberately wanted to do weird things?

Quoted 3 lines

E.g. not updating the pre-faulting test to use the "correct" size+alignment means the test is missing easy coverage for hugepages, since KVM won't create huge mappings in stage-2 due to the memslot not being sized+aligned.

Got you, that makes sense I'll update this series to resolve this then. Thanks for taking a look.

Signature · 2 lines
Thanks,
Jack
#10 lore raw

PATCH v2 3/4 KVM: selftests: Enable pre_fault_memory_test for arm64

In reply to Jack Thomson

From: Jack Thomson <[email protected]>

Enable the pre_fault_memory_test to run on arm64 by making it work with different guest page sizes and testing multiple guest configurations.

Update the test_assert to compare against the UCALL_EXIT_REASON, for portability, as arm64 exits with KVM_EXIT_MMIO while x86 uses KVM_EXIT_IO.

---
 tools/testing/selftests/kvm/Makefile.kvm      |  1 +
 .../selftests/kvm/pre_fault_memory_test.c     | 79 ++++++++++++++-----
 2 files changed, 59 insertions(+), 21 deletions(-)
tools/testing/selftests/kvm/Makefile.kvm +1 -0
index 90f03f00cb04..4db1737fad04 100644
@@ -180,6 +180,7 @@ TEST_GEN_PROGS_arm64 += memslot_perf_test
 TEST_GEN_PROGS_arm64 += mmu_stress_test
 TEST_GEN_PROGS_arm64 += rseq_test
 TEST_GEN_PROGS_arm64 += steal_time
+TEST_GEN_PROGS_arm64 += pre_fault_memory_test
 
 TEST_GEN_PROGS_s390 = $(TEST_GEN_PROGS_COMMON)
 TEST_GEN_PROGS_s390 += s390/memop
tools/testing/selftests/kvm/pre_fault_memory_test.c +58 -21

127 lines not on this page · raw message

Signature · 1 line
2.43.0
#11 lore raw

PATCH v2 4/4 KVM: selftests: Add option for different backing in pre-fault tests

In reply to Jack Thomson

From: Jack Thomson <[email protected]>

Add a -m option to specify different memory backing types for the pre-fault tests (e.g., anonymous, hugetlb), allowing testing of the pre-fault functionality across different memory configurations.

---
 .../selftests/kvm/pre_fault_memory_test.c     | 31 ++++++++++++++++---
 1 file changed, 26 insertions(+), 5 deletions(-)
tools/testing/selftests/kvm/pre_fault_memory_test.c +26 -5
index ed9848a8af60..22e2e53945d9 100644
@@ -76,6 +76,7 @@ static void pre_fault_memory(struct kvm_vcpu *vcpu, u64 gpa, u64 size,
 struct test_params {
 	unsigned long vm_type;
 	bool private;
+	enum vm_mem_backing_src_type mem_backing_src;
 };
 
 static void __test_pre_fault_memory(enum vm_guest_mode guest_mode, void *arg)
@@ -94,7 +95,11 @@ static void __test_pre_fault_memory(enum vm_guest_mode guest_mode, void *arg)
 	uint64_t guest_test_virt_mem;
 	uint64_t alignment, guest_page_size;
 
+	size_t backing_src_pagesz = get_backing_src_pagesz(p->mem_backing_src);
+
 	pr_info("Testing guest mode: %s\n", vm_guest_mode_string(guest_mode));
+	pr_info("Testing memory backing src type: %s\n",
+		vm_mem_backing_src_alias(p->mem_backing_src)->name);
 
 	vm = vm_create_shape_with_one_vcpu(shape, &vcpu, guest_code);
 
@@ -110,10 +115,11 @@ static void __test_pre_fault_memory(enum vm_guest_mode guest_mode, void *arg)
 #else
 	alignment = SZ_2M;
 #endif
+	alignment = max(alignment, backing_src_pagesz);
 	guest_test_phys_mem = align_down(guest_test_phys_mem, alignment);
 	guest_test_virt_mem = guest_test_phys_mem & ((1ULL << (vm->va_bits - 1)) - 1);
 
-	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
+	vm_userspace_mem_region_add(vm, p->mem_backing_src,
 				    guest_test_phys_mem, TEST_SLOT, test_config.test_num_pages,
 				    p->private ? KVM_MEM_GUEST_MEMFD : 0);
 	virt_map(vm, guest_test_virt_mem, guest_test_phys_mem, test_config.test_num_pages);
@@ -155,7 +161,8 @@ static void __test_pre_fault_memory(enum vm_guest_mode guest_mode, void *arg)
 	kvm_vm_free(vm);
 }
 
-static void test_pre_fault_memory(unsigned long vm_type, bool private)
+static void test_pre_fault_memory(unsigned long vm_type, enum vm_mem_backing_src_type backing_src,
+				  bool private)
 {
 	if (vm_type && !(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(vm_type))) {
 		pr_info("Skipping tests for vm_type 0x%lx\n", vm_type);
@@ -165,6 +172,7 @@ static void test_pre_fault_memory(unsigned long vm_type, bool private)
 	struct test_params p = {
 		.vm_type = vm_type,
 		.private = private,
+		.mem_backing_src = backing_src,
 	};
 
 	for_each_guest_mode(__test_pre_fault_memory, &p);
@@ -174,10 +182,23 @@ int main(int argc, char *argv[])
 {
 	TEST_REQUIRE(kvm_check_cap(KVM_CAP_PRE_FAULT_MEMORY));
 
-	test_pre_fault_memory(0, false);
+	int opt;
+	enum vm_mem_backing_src_type backing = VM_MEM_SRC_ANONYMOUS;
+
+	while ((opt = getopt(argc, argv, "m:")) != -1) {
+		switch (opt) {
+		case 'm':
+			backing = parse_backing_src_type(optarg);
+			break;
+		default:
+			break;
+		}
+	}
+
+	test_pre_fault_memory(0, backing, false);
 #ifdef __x86_64__
-	test_pre_fault_memory(KVM_X86_SW_PROTECTED_VM, false);
-	test_pre_fault_memory(KVM_X86_SW_PROTECTED_VM, true);
+	test_pre_fault_memory(KVM_X86_SW_PROTECTED_VM, backing, false);
+	test_pre_fault_memory(KVM_X86_SW_PROTECTED_VM, backing, true);
 #endif
 	return 0;
 }
Signature · 1 line
2.43.0