All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/5] KVM: riscv: Add KVM_PRE_FAULT_MEMORY support
@ 2026-08-15  9:55 Jinyu Tang
  2026-08-15 10:00   ` Jinyu Tang
                   ` (4 more replies)
  0 siblings, 5 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15  9:55 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang

Add RISC-V support for the generic KVM_PRE_FAULT_MEMORY ioctl.

Patch 1 and patch 2 fix existing G-stage huge-page corner cases. They
are included first because the prefault hook relies on the map path
reporting the mapping that actually covers the requested GPA.

Patch 3 adds the RISC-V arch hook for KVM_PRE_FAULT_MEMORY. Patch 4 adds
Sv57 indexing support to the RISC-V KVM selftest helpers, and patch 5
enables the generic pre_fault_memory_test for RISC-V.

Patch 1 folds the previously posted standalone G-stage table overwrite
fix into this series:

  https://lore.kernel.org/kvm-riscv/20260814105752.565325-1-jinyu.tang@linux.dev/T/#u

Changes in v4:
- Fold the standalone G-stage non-leaf overwrite fix into this series.
- Avoid THP adjustment when the original faulting GPA already has an
  existing 4K G-stage leaf.
- Return an error for HWPOISON instead of reporting success without a
  visible G-stage mapping. (Sashiko)

Changes in v3:
- Retry internally when the map path returns success without a visible
  G-stage mapping, instead of exposing -EAGAIN. (Sashiko)

Changes in v2:
- Drop the per-test guest_modes_append_default() call from
  pre_fault_memory_test.c. (Sean)

Jinyu Tang (5):
  KVM: riscv: Avoid overwriting G-stage tables with huge leaves
  KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves
  KVM: riscv: Implement KVM_PRE_FAULT_MEMORY
  KVM: selftests: Add RISC-V Sv57 page table indexing
  KVM: selftests: Enable pre_fault_memory_test for RISC-V

 arch/riscv/kvm/Kconfig                        |  1 +
 arch/riscv/kvm/gstage.c                       |  9 ++
 arch/riscv/kvm/mmu.c                          | 83 ++++++++++++++++++-
 arch/riscv/kvm/vm.c                           |  1 +
 tools/testing/selftests/kvm/Makefile.kvm      |  1 +
 .../selftests/kvm/include/riscv/processor.h   |  6 ++
 .../selftests/kvm/lib/riscv/processor.c       |  2 +
 7 files changed, 100 insertions(+), 3 deletions(-)

-- 
2.43.0

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v4 1/5] KVM: riscv: Avoid overwriting G-stage tables with huge leaves
  2026-08-15  9:55 [PATCH v4 0/5] KVM: riscv: Add KVM_PRE_FAULT_MEMORY support Jinyu Tang
@ 2026-08-15 10:00   ` Jinyu Tang
  2026-08-15 10:04 ` [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves Jinyu Tang
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:00 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang, Sashiko

RISC-V KVM can overwrite an existing G-stage table entry when
installing a huge leaf mapping. If the target huge range already has a
lower-level page table, kvm_riscv_gstage_set_pte() can replace the
non-leaf entry with a leaf PTE and disconnect the lower-level page
table.

Reject replacing a valid table entry with a leaf PTE. If huge-page
installation hits such a conflict, fall back to a 4K mapping for the
original faulting GPA in the MMU fault path, where the original GPA and
HFN are still available.

Suggested-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 9d05c1fee837 ("RISC-V: KVM: Implement stage2 page table programming")
Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 arch/riscv/kvm/gstage.c |  6 ++++++
 arch/riscv/kvm/mmu.c    | 24 +++++++++++++++++++++++-
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index e5002cb9cbef..54d45addf18f 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -174,6 +174,12 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage,
 
 	if (pte_val(*ptep) != pte_val(map->pte)) {
 		bool was_invalid = !pte_val(*ptep);
+
+		/* Avoid replacing an existing lower-level table with a leaf mapping. */
+		if (!gstage_pte_leaf(ptep) && !was_invalid &&
+		    gstage_pte_leaf(&map->pte))
+			return -EEXIST;
+
 		set_pte(ptep, map->pte);
 		if (gstage_pte_leaf(ptep) &&
 		    !(was_invalid && riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC)))
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 6035b5ec9503..bfd6168ebe30 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -625,10 +625,11 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 		      struct kvm_gstage_mapping *out_map)
 {
 	int ret;
-	kvm_pfn_t hfn;
+	kvm_pfn_t fault_hfn, hfn;
 	bool is_hugetlb;
 	bool writable;
 	unsigned int vma_pageshift;
+	gpa_t fault_gpa = gpa;
 	gfn_t gfn = gpa >> PAGE_SHIFT;
 	struct vm_area_struct *vma;
 	struct kvm *kvm = vcpu->kvm;
@@ -709,6 +710,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 	}
 	if (is_error_noslot_pfn(hfn))
 		return -EFAULT;
+	fault_hfn = hfn + ((fault_gpa >> PAGE_SHIFT) - gfn);
 
 	/*
 	 * If logging is active then we allow writable pages only
@@ -734,9 +736,29 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 		mark_page_dirty_in_slot(kvm, memslot, gfn);
 		ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT,
 						vma_pagesize, false, true, out_map);
+		if (ret == -EEXIST) {
+			/*
+			 * Retry at 4K granularity for the original faulting GPA
+			 * when a huge leaf cannot replace an existing table.
+			 */
+			ret = kvm_riscv_gstage_map_page(&gstage, pcache, fault_gpa,
+							fault_hfn << PAGE_SHIFT,
+							PAGE_SIZE, false, true,
+							out_map);
+		}
 	} else {
 		ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT,
 						vma_pagesize, true, true, out_map);
+		if (ret == -EEXIST) {
+			/*
+			 * Retry at 4K granularity for the original faulting GPA
+			 * when a huge leaf cannot replace an existing table.
+			 */
+			ret = kvm_riscv_gstage_map_page(&gstage, pcache, fault_gpa,
+							fault_hfn << PAGE_SHIFT,
+							PAGE_SIZE, true, true,
+							out_map);
+		}
 	}
 
 	if (ret)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 1/5] KVM: riscv: Avoid overwriting G-stage tables with huge leaves
@ 2026-08-15 10:00   ` Jinyu Tang
  0 siblings, 0 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:00 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang, Sashiko

RISC-V KVM can overwrite an existing G-stage table entry when
installing a huge leaf mapping. If the target huge range already has a
lower-level page table, kvm_riscv_gstage_set_pte() can replace the
non-leaf entry with a leaf PTE and disconnect the lower-level page
table.

Reject replacing a valid table entry with a leaf PTE. If huge-page
installation hits such a conflict, fall back to a 4K mapping for the
original faulting GPA in the MMU fault path, where the original GPA and
HFN are still available.

Suggested-by: Sashiko <sashiko-bot@kernel.org>
Fixes: 9d05c1fee837 ("RISC-V: KVM: Implement stage2 page table programming")
Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 arch/riscv/kvm/gstage.c |  6 ++++++
 arch/riscv/kvm/mmu.c    | 24 +++++++++++++++++++++++-
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index e5002cb9cbef..54d45addf18f 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -174,6 +174,12 @@ int kvm_riscv_gstage_set_pte(struct kvm_gstage *gstage,
 
 	if (pte_val(*ptep) != pte_val(map->pte)) {
 		bool was_invalid = !pte_val(*ptep);
+
+		/* Avoid replacing an existing lower-level table with a leaf mapping. */
+		if (!gstage_pte_leaf(ptep) && !was_invalid &&
+		    gstage_pte_leaf(&map->pte))
+			return -EEXIST;
+
 		set_pte(ptep, map->pte);
 		if (gstage_pte_leaf(ptep) &&
 		    !(was_invalid && riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC)))
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 6035b5ec9503..bfd6168ebe30 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -625,10 +625,11 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 		      struct kvm_gstage_mapping *out_map)
 {
 	int ret;
-	kvm_pfn_t hfn;
+	kvm_pfn_t fault_hfn, hfn;
 	bool is_hugetlb;
 	bool writable;
 	unsigned int vma_pageshift;
+	gpa_t fault_gpa = gpa;
 	gfn_t gfn = gpa >> PAGE_SHIFT;
 	struct vm_area_struct *vma;
 	struct kvm *kvm = vcpu->kvm;
@@ -709,6 +710,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 	}
 	if (is_error_noslot_pfn(hfn))
 		return -EFAULT;
+	fault_hfn = hfn + ((fault_gpa >> PAGE_SHIFT) - gfn);
 
 	/*
 	 * If logging is active then we allow writable pages only
@@ -734,9 +736,29 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 		mark_page_dirty_in_slot(kvm, memslot, gfn);
 		ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT,
 						vma_pagesize, false, true, out_map);
+		if (ret == -EEXIST) {
+			/*
+			 * Retry at 4K granularity for the original faulting GPA
+			 * when a huge leaf cannot replace an existing table.
+			 */
+			ret = kvm_riscv_gstage_map_page(&gstage, pcache, fault_gpa,
+							fault_hfn << PAGE_SHIFT,
+							PAGE_SIZE, false, true,
+							out_map);
+		}
 	} else {
 		ret = kvm_riscv_gstage_map_page(&gstage, pcache, gpa, hfn << PAGE_SHIFT,
 						vma_pagesize, true, true, out_map);
+		if (ret == -EEXIST) {
+			/*
+			 * Retry at 4K granularity for the original faulting GPA
+			 * when a huge leaf cannot replace an existing table.
+			 */
+			ret = kvm_riscv_gstage_map_page(&gstage, pcache, fault_gpa,
+							fault_hfn << PAGE_SHIFT,
+							PAGE_SIZE, true, true,
+							out_map);
+		}
 	}
 
 	if (ret)
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves
  2026-08-15  9:55 [PATCH v4 0/5] KVM: riscv: Add KVM_PRE_FAULT_MEMORY support Jinyu Tang
  2026-08-15 10:00   ` Jinyu Tang
@ 2026-08-15 10:04 ` Jinyu Tang
  2026-08-15 10:22   ` sashiko-bot
  2026-08-15 10:06   ` Jinyu Tang
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:04 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang

When dirty logging is disabled after a G-stage PMD mapping has been
split, the fault path may see a 4K G-stage leaf while the backing host
page is still THP-backed. The existing kvm_riscv_gstage_map_page()
comment says that this path should update the small leaf and leave huge
mapping recovery to a later ioctl path.

However, transparent_hugepage_adjust() runs before that G-stage lookup
and rewrites the fault GPA to the PMD base. If the original fault is not
at the PMD base, the lookup can find and update the wrong 4K leaf.

Check the original fault GPA in transparent_hugepage_adjust(). If it
already has a 4K G-stage leaf, skip THP adjustment and keep handling the
fault at PAGE_SIZE granularity.

Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 arch/riscv/kvm/mmu.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index bfd6168ebe30..2fabcd409991 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -500,10 +500,21 @@ static int get_hva_mapping_size(struct kvm *kvm,
 
 static unsigned long transparent_hugepage_adjust(struct kvm *kvm,
 						 struct kvm_memory_slot *memslot,
+						 struct kvm_gstage *gstage,
 						 unsigned long hva,
 						 kvm_pfn_t *hfnp, gpa_t *gpa)
 {
 	kvm_pfn_t hfn = *hfnp;
+	u32 ptep_level;
+	pte_t *ptep;
+
+	/*
+	 * Keep the existing split G-stage leaf and update the original
+	 * faulting 4K page in the vCPU fault path.
+	 */
+	if (kvm_riscv_gstage_get_leaf(gstage, *gpa, &ptep, &ptep_level) &&
+	    !ptep_level)
+		return PAGE_SIZE;
 
 	/*
 	 * Make sure the adjustment is done only for THP pages. Also make
@@ -730,7 +741,8 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 	 * so do not promote them through the THP helper.
 	 */
 	if (!logging && !is_hugetlb && vma_pagesize == PAGE_SIZE)
-		vma_pagesize = transparent_hugepage_adjust(kvm, memslot, hva, &hfn, &gpa);
+		vma_pagesize = transparent_hugepage_adjust(kvm, memslot, &gstage,
+							   hva, &hfn, &gpa);
 
 	if (writable) {
 		mark_page_dirty_in_slot(kvm, memslot, gfn);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 3/5] KVM: riscv: Implement KVM_PRE_FAULT_MEMORY
  2026-08-15  9:55 [PATCH v4 0/5] KVM: riscv: Add KVM_PRE_FAULT_MEMORY support Jinyu Tang
@ 2026-08-15 10:06   ` Jinyu Tang
  2026-08-15 10:04 ` [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves Jinyu Tang
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:06 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang

The generic KVM_PRE_FAULT_MEMORY ioctl lets userspace populate KVM page
tables before running a vCPU over a GPA range. x86 already supports the
ioctl, but RISC-V does not expose the capability and has no arch hook.

Add the RISC-V arch hook and reuse the existing G-stage fault mapping
path with a read access. Report progress using the G-stage mapping
returned by the map path, so the ioctl can advance by the actual leaf
size that covers the requested GPA. Retry until a mapping is installed
or a signal, VM-dead request, or real error is observed.

Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 arch/riscv/kvm/Kconfig  |  1 +
 arch/riscv/kvm/gstage.c |  3 +++
 arch/riscv/kvm/mmu.c    | 45 ++++++++++++++++++++++++++++++++++++++++-
 arch/riscv/kvm/vm.c     |  1 +
 4 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e0..8ac209e8ac87 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -28,6 +28,7 @@ config KVM
 	select KVM_COMMON
 	select KVM_GENERIC_DIRTYLOG_READ_PROTECT
 	select KVM_GENERIC_HARDWARE_ENABLING
+	select KVM_GENERIC_PRE_FAULT_MEMORY
 	select KVM_MMIO
 	select VIRT_XFER_TO_GUEST_WORK
 	select SCHED_INFO
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index 54d45addf18f..dff315dfd24e 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -286,6 +286,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
 						    out_map->level, true);
 		} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {
 			kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);
+			out_map->addr = ALIGN_DOWN(gpa, page_size);
+			out_map->level = ptep_level;
+			out_map->pte = ptep_get(ptep);
 			return 0;
 		}
 	}
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 2fabcd409991..f6ca86da53eb 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -717,7 +717,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 	if (hfn == KVM_PFN_ERR_HWPOISON) {
 		send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva,
 				vma_pageshift, current);
-		return 0;
+		return -EFAULT;
 	}
 	if (is_error_noslot_pfn(hfn))
 		return -EFAULT;
@@ -782,6 +782,49 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 	return ret;
 }
 
+long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
+				    struct kvm_pre_fault_memory *range)
+{
+	struct kvm_gstage_mapping out_map = { 0 };
+	struct kvm_memory_slot *memslot;
+	unsigned long map_size;
+	unsigned long hva;
+	gpa_t end;
+	gfn_t gfn;
+	int ret;
+
+	gfn = gpa_to_gfn(range->gpa);
+	memslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+	if (!memslot)
+		return -ENOENT;
+
+	hva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);
+	if (kvm_is_error_hva(hva))
+		return -ENOENT;
+
+	for (;;) {
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
+			return -EIO;
+
+		cond_resched();
+		ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, &out_map);
+		if (ret)
+			return ret;
+
+		if (!pte_val(out_map.pte))
+			continue;
+
+		map_size = PAGE_SIZE << (out_map.level * kvm_riscv_gstage_index_bits);
+		end = out_map.addr + map_size;
+		break;
+	}
+
+	return min_t(u64, range->size, end - range->gpa);
+}
+
 int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
 {
 	struct page *pgd_page;
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index a9f083feeb76..58500a19b33b 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_MP_STATE:
 	case KVM_CAP_IMMEDIATE_EXIT:
 	case KVM_CAP_SET_GUEST_DEBUG:
+	case KVM_CAP_PRE_FAULT_MEMORY:
 		r = 1;
 		break;
 	case KVM_CAP_NR_VCPUS:
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 3/5] KVM: riscv: Implement KVM_PRE_FAULT_MEMORY
@ 2026-08-15 10:06   ` Jinyu Tang
  0 siblings, 0 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:06 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang

The generic KVM_PRE_FAULT_MEMORY ioctl lets userspace populate KVM page
tables before running a vCPU over a GPA range. x86 already supports the
ioctl, but RISC-V does not expose the capability and has no arch hook.

Add the RISC-V arch hook and reuse the existing G-stage fault mapping
path with a read access. Report progress using the G-stage mapping
returned by the map path, so the ioctl can advance by the actual leaf
size that covers the requested GPA. Retry until a mapping is installed
or a signal, VM-dead request, or real error is observed.

Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 arch/riscv/kvm/Kconfig  |  1 +
 arch/riscv/kvm/gstage.c |  3 +++
 arch/riscv/kvm/mmu.c    | 45 ++++++++++++++++++++++++++++++++++++++++-
 arch/riscv/kvm/vm.c     |  1 +
 4 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e0..8ac209e8ac87 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -28,6 +28,7 @@ config KVM
 	select KVM_COMMON
 	select KVM_GENERIC_DIRTYLOG_READ_PROTECT
 	select KVM_GENERIC_HARDWARE_ENABLING
+	select KVM_GENERIC_PRE_FAULT_MEMORY
 	select KVM_MMIO
 	select VIRT_XFER_TO_GUEST_WORK
 	select SCHED_INFO
diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
index 54d45addf18f..dff315dfd24e 100644
--- a/arch/riscv/kvm/gstage.c
+++ b/arch/riscv/kvm/gstage.c
@@ -286,6 +286,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
 						    out_map->level, true);
 		} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {
 			kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);
+			out_map->addr = ALIGN_DOWN(gpa, page_size);
+			out_map->level = ptep_level;
+			out_map->pte = ptep_get(ptep);
 			return 0;
 		}
 	}
diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
index 2fabcd409991..f6ca86da53eb 100644
--- a/arch/riscv/kvm/mmu.c
+++ b/arch/riscv/kvm/mmu.c
@@ -717,7 +717,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 	if (hfn == KVM_PFN_ERR_HWPOISON) {
 		send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva,
 				vma_pageshift, current);
-		return 0;
+		return -EFAULT;
 	}
 	if (is_error_noslot_pfn(hfn))
 		return -EFAULT;
@@ -782,6 +782,49 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
 	return ret;
 }
 
+long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
+				    struct kvm_pre_fault_memory *range)
+{
+	struct kvm_gstage_mapping out_map = { 0 };
+	struct kvm_memory_slot *memslot;
+	unsigned long map_size;
+	unsigned long hva;
+	gpa_t end;
+	gfn_t gfn;
+	int ret;
+
+	gfn = gpa_to_gfn(range->gpa);
+	memslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
+	if (!memslot)
+		return -ENOENT;
+
+	hva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);
+	if (kvm_is_error_hva(hva))
+		return -ENOENT;
+
+	for (;;) {
+		if (signal_pending(current))
+			return -EINTR;
+
+		if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
+			return -EIO;
+
+		cond_resched();
+		ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, &out_map);
+		if (ret)
+			return ret;
+
+		if (!pte_val(out_map.pte))
+			continue;
+
+		map_size = PAGE_SIZE << (out_map.level * kvm_riscv_gstage_index_bits);
+		end = out_map.addr + map_size;
+		break;
+	}
+
+	return min_t(u64, range->size, end - range->gpa);
+}
+
 int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
 {
 	struct page *pgd_page;
diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c
index a9f083feeb76..58500a19b33b 100644
--- a/arch/riscv/kvm/vm.c
+++ b/arch/riscv/kvm/vm.c
@@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
 	case KVM_CAP_MP_STATE:
 	case KVM_CAP_IMMEDIATE_EXIT:
 	case KVM_CAP_SET_GUEST_DEBUG:
+	case KVM_CAP_PRE_FAULT_MEMORY:
 		r = 1;
 		break;
 	case KVM_CAP_NR_VCPUS:
-- 
2.43.0


_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 4/5] KVM: selftests: Add RISC-V Sv57 page table indexing
  2026-08-15  9:55 [PATCH v4 0/5] KVM: riscv: Add KVM_PRE_FAULT_MEMORY support Jinyu Tang
@ 2026-08-15 10:08   ` Jinyu Tang
  2026-08-15 10:04 ` [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves Jinyu Tang
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:08 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang

RISC-V selftests can create guests with five page-table levels, for
example when the selected guest mode is Sv57. The RISC-V page-table
walker only had index arrays for levels 0 through 3, so
virt_arch_pg_map() indexed past the end of the arrays when level 4 was
used.

Add the missing L4 index mask and shift so selftests can build guest
page tables for Sv57 VMs.

Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 tools/testing/selftests/kvm/include/riscv/processor.h | 3 +++
 tools/testing/selftests/kvm/lib/riscv/processor.c     | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h
index e3acf2ae9881..abde3c71c891 100644
--- a/tools/testing/selftests/kvm/include/riscv/processor.h
+++ b/tools/testing/selftests/kvm/include/riscv/processor.h
@@ -127,6 +127,9 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handl
 
 void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler);
 
+/* L4 index Bit[56:48] */
+#define PGTBL_L4_INDEX_MASK			0x01FF000000000000ULL
+#define PGTBL_L4_INDEX_SHIFT			48
 /* L3 index Bit[47:39] */
 #define PGTBL_L3_INDEX_MASK			0x0000FF8000000000ULL
 #define PGTBL_L3_INDEX_SHIFT			39
diff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c
index ded5429f3448..b4d41a407553 100644
--- a/tools/testing/selftests/kvm/lib/riscv/processor.c
+++ b/tools/testing/selftests/kvm/lib/riscv/processor.c
@@ -43,6 +43,7 @@ static u64 pte_index_mask[] = {
 	PGTBL_L1_INDEX_MASK,
 	PGTBL_L2_INDEX_MASK,
 	PGTBL_L3_INDEX_MASK,
+	PGTBL_L4_INDEX_MASK,
 };
 
 static u32 pte_index_shift[] = {
@@ -50,6 +51,7 @@ static u32 pte_index_shift[] = {
 	PGTBL_L1_INDEX_SHIFT,
 	PGTBL_L2_INDEX_SHIFT,
 	PGTBL_L3_INDEX_SHIFT,
+	PGTBL_L4_INDEX_SHIFT,
 };
 
 static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level)
-- 
2.43.0


-- 
kvm-riscv mailing list
kvm-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/kvm-riscv

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 4/5] KVM: selftests: Add RISC-V Sv57 page table indexing
@ 2026-08-15 10:08   ` Jinyu Tang
  0 siblings, 0 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:08 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang

RISC-V selftests can create guests with five page-table levels, for
example when the selected guest mode is Sv57. The RISC-V page-table
walker only had index arrays for levels 0 through 3, so
virt_arch_pg_map() indexed past the end of the arrays when level 4 was
used.

Add the missing L4 index mask and shift so selftests can build guest
page tables for Sv57 VMs.

Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 tools/testing/selftests/kvm/include/riscv/processor.h | 3 +++
 tools/testing/selftests/kvm/lib/riscv/processor.c     | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h
index e3acf2ae9881..abde3c71c891 100644
--- a/tools/testing/selftests/kvm/include/riscv/processor.h
+++ b/tools/testing/selftests/kvm/include/riscv/processor.h
@@ -127,6 +127,9 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handl
 
 void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler);
 
+/* L4 index Bit[56:48] */
+#define PGTBL_L4_INDEX_MASK			0x01FF000000000000ULL
+#define PGTBL_L4_INDEX_SHIFT			48
 /* L3 index Bit[47:39] */
 #define PGTBL_L3_INDEX_MASK			0x0000FF8000000000ULL
 #define PGTBL_L3_INDEX_SHIFT			39
diff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c
index ded5429f3448..b4d41a407553 100644
--- a/tools/testing/selftests/kvm/lib/riscv/processor.c
+++ b/tools/testing/selftests/kvm/lib/riscv/processor.c
@@ -43,6 +43,7 @@ static u64 pte_index_mask[] = {
 	PGTBL_L1_INDEX_MASK,
 	PGTBL_L2_INDEX_MASK,
 	PGTBL_L3_INDEX_MASK,
+	PGTBL_L4_INDEX_MASK,
 };
 
 static u32 pte_index_shift[] = {
@@ -50,6 +51,7 @@ static u32 pte_index_shift[] = {
 	PGTBL_L1_INDEX_SHIFT,
 	PGTBL_L2_INDEX_SHIFT,
 	PGTBL_L3_INDEX_SHIFT,
+	PGTBL_L4_INDEX_SHIFT,
 };
 
 static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level)
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v4 5/5] KVM: selftests: Enable pre_fault_memory_test for RISC-V
  2026-08-15  9:55 [PATCH v4 0/5] KVM: riscv: Add KVM_PRE_FAULT_MEMORY support Jinyu Tang
                   ` (3 preceding siblings ...)
  2026-08-15 10:08   ` Jinyu Tang
@ 2026-08-15 10:08 ` Jinyu Tang
  4 siblings, 0 replies; 11+ messages in thread
From: Jinyu Tang @ 2026-08-15 10:08 UTC (permalink / raw)
  To: Anup Patel, Anup Patel, Atish Patra
  Cc: kvm, kvm-riscv, linux-riscv, linux-kernel, linux-kselftest,
	Paolo Bonzini, Sean Christopherson, Shuah Khan, Paul Walmsley,
	Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Andrew Jones,
	Conor Dooley, Yong-Xuan Wang, Nutty Liu, Yifei Jiang, Jinyu Tang,
	Jinyu Tang

RISC-V now supports KVM_PRE_FAULT_MEMORY, so include the generic
pre_fault_memory_test in the RISC-V KVM selftest build.

The test uses PAGE_SIZE from the architecture processor header. Define
the normal 4K RISC-V selftest page size so the generic test can build
for RISC-V.

Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Signed-off-by: Jinyu Tang <jinyu.tang@linux.dev>
---
 tools/testing/selftests/kvm/Makefile.kvm              | 1 +
 tools/testing/selftests/kvm/include/riscv/processor.h | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 6fc34e9bf8e1..ac64ac92fd4b 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -225,6 +225,7 @@ TEST_GEN_PROGS_riscv += coalesced_io_test
 TEST_GEN_PROGS_riscv += dirty_log_perf_test
 TEST_GEN_PROGS_riscv += get-reg-list
 TEST_GEN_PROGS_riscv += mmu_stress_test
+TEST_GEN_PROGS_riscv += pre_fault_memory_test
 TEST_GEN_PROGS_riscv += rseq_test
 TEST_GEN_PROGS_riscv += steal_time
 
diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h
index abde3c71c891..70487c8ed155 100644
--- a/tools/testing/selftests/kvm/include/riscv/processor.h
+++ b/tools/testing/selftests/kvm/include/riscv/processor.h
@@ -12,6 +12,9 @@
 #include <asm/vdso/processor.h>
 #include "kvm_util.h"
 
+#define PAGE_SHIFT		12
+#define PAGE_SIZE		BIT_ULL(PAGE_SHIFT)
+
 #define INSN_OPCODE_MASK	0x007c
 #define INSN_OPCODE_SHIFT	2
 #define INSN_OPCODE_SYSTEM	28
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 3/5] KVM: riscv: Implement KVM_PRE_FAULT_MEMORY
  2026-08-15 10:06   ` Jinyu Tang
  (?)
@ 2026-08-15 10:21   ` sashiko-bot
  -1 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-08-15 10:21 UTC (permalink / raw)
  To: Jinyu Tang; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Returning `-EFAULT` on `KVM_PFN_ERR_HWPOISON` in `kvm_riscv_mmu_map` breaks `KVM_RUN` execution and normal KVM signal delivery.
- [High] Incorrect map advancement calculation in `kvm_arch_vcpu_pre_fault_memory` causes skipped pages and false completion for unaligned and split huge pages.
--

--- Patch [3]: [PATCH v4 3/5] KVM: riscv: Implement KVM_PRE_FAULT_MEMORY ---
commit d1fe5f152fd69a51f4bc7397e89186eff774f433
Author: Jinyu Tang <jinyu.tang@linux.dev>
Subject: KVM: riscv: Implement KVM_PRE_FAULT_MEMORY

The patch implements the KVM_PRE_FAULT_MEMORY ioctl for RISC-V by adding the
kvm_arch_vcpu_pre_fault_memory() hook. It reuses the existing G-stage fault
mapping path to populate KVM page tables.

> diff --git a/arch/riscv/kvm/gstage.c b/arch/riscv/kvm/gstage.c
> index 54d45addf18f9..dff315dfd24ed 100644
> --- a/arch/riscv/kvm/gstage.c
> +++ b/arch/riscv/kvm/gstage.c
> @@ -286,6 +286,9 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
>  						    out_map->level, true);
>  		} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {
>  			kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);
> +			out_map->addr = ALIGN_DOWN(gpa, page_size);
> +			out_map->level = ptep_level;
> +			out_map->pte = ptep_get(ptep);
>  			return 0;
>  		}
>  	}

[ ... ]

> diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
> index 2fabcd4099919..f6ca86da53eb8 100644
> --- a/arch/riscv/kvm/mmu.c
> +++ b/arch/riscv/kvm/mmu.c
> @@ -717,7 +717,7 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
>  	if (hfn == KVM_PFN_ERR_HWPOISON) {
>  		send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva,
>  				vma_pageshift, current);
> -		return 0;
> +		return -EFAULT;

[Severity: High]
Does returning -EFAULT here break KVM's standard exit-to-userspace flow on a
signal?

If a guest accesses a poisoned memory page, returning 0 allows KVM to bubble
up 1 to kvm_arch_vcpu_ioctl_run(), which checks signal_pending() (set by
send_sig_mceerr) and correctly exits to userspace with -EINTR.

Returning -EFAULT causes the KVM_RUN ioctl to immediately fail with -EFAULT,
which can cause the VMM to crash instead of gracefully handling the Machine
Check Exception.

[ ... ]

> @@ -782,6 +782,49 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
[ ... ]
> +	for (;;) {
> +		if (signal_pending(current))
> +			return -EINTR;
> +
> +		if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
> +			return -EIO;
> +
> +		cond_resched();
> +		ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, &out_map);
> +		if (ret)
> +			return ret;
> +
> +		if (!pte_val(out_map.pte))
> +			continue;
> +
> +		map_size = PAGE_SIZE << (out_map.level * kvm_riscv_gstage_index_bits);
> +		end = out_map.addr + map_size;
> +		break;
> +	}
> +
> +	return min_t(u64, range->size, end - range->gpa);
> +}

[Severity: High]
Can end - range->gpa underflow here when handling unaligned offsets or split
huge pages?

In kvm_riscv_gstage_map_page() above, out_map.addr is aligned to the
requested huge page size rather than the actual leaf size installed. 

If out_map.level is set to the 4K leaf level, but out_map.addr is aligned to
the huge page size, and range->gpa is beyond the first 4K of that huge page,
end will be calculated as less than range->gpa.

This would cause end - range->gpa to underflow into a large u64, making
min_t() evaluate to range->size and prematurely terminating the ioctl while
skipping regions of guest physical address space.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260815095548.860018-1-jinyu.tang@linux.dev?part=3

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves
  2026-08-15 10:04 ` [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves Jinyu Tang
@ 2026-08-15 10:22   ` sashiko-bot
  0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-08-15 10:22 UTC (permalink / raw)
  To: Jinyu Tang; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Incomplete bug fix allows an infinite page fault loop when handling THP-backed memory.
--

--- Patch [2]: [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves ---
commit 6582fbd6641ea5762d156dac1847e91f07decbd7
Author: Jinyu Tang <jinyu.tang@linux.dev>

    KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves

This commit updates transparent_hugepage_adjust() to check if the original
fault GPA already has a 4K G-stage leaf. If so, it skips THP adjustment and
handles the fault at PAGE_SIZE granularity.

> diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c
> index bfd6168ebe308..2fabcd4099919 100644
> --- a/arch/riscv/kvm/mmu.c
> +++ b/arch/riscv/kvm/mmu.c
[ ... ]
> @@ -500,10 +500,21 @@ static int get_hva_mapping_size(struct kvm *kvm,
>  
>  static unsigned long transparent_hugepage_adjust(struct kvm *kvm,
>  						 struct kvm_memory_slot *memslot,
> +						 struct kvm_gstage *gstage,
>  						 unsigned long hva,
>  						 kvm_pfn_t *hfnp, gpa_t *gpa)
>  {
>  	kvm_pfn_t hfn = *hfnp;
> +	u32 ptep_level;
> +	pte_t *ptep;
> +
> +	/*
> +	 * Keep the existing split G-stage leaf and update the original
> +	 * faulting 4K page in the vCPU fault path.
> +	 */
> +	if (kvm_riscv_gstage_get_leaf(gstage, *gpa, &ptep, &ptep_level) &&
> +	    !ptep_level)
> +		return PAGE_SIZE;

[Severity: High]
This is a pre-existing issue, but does this fully prevent the infinite page
fault loop when handling THP-backed memory?

This check only validates the unaligned faulting GPA. If the faulting GPA is
unmapped, it proceeds to align the GPA to the PMD base. However, if the PMD
base itself already has a 4K leaf (e.g., from a prior access while dirty
logging was enabled), transparent_hugepage_adjust() will still return
PMD_SIZE.

When called with the aligned GPA (the PMD base), kvm_riscv_gstage_map_page()
will find the 4K leaf at the PMD base:

arch/riscv/kvm/gstage.c:kvm_riscv_gstage_map_page() {
    ...
		} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {
			kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);
			return 0;
		}
    ...
}

It updates the protection bits of the PMD base leaf and returns success
without mapping the original faulting GPA. Could this leave the originally
faulting GPA unmapped, causing the vCPU to hang in an infinite page fault
loop?

>  
>  	/*
>  	 * Make sure the adjustment is done only for THP pages. Also make

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260815095548.860018-1-jinyu.tang@linux.dev?part=2

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-08-15 10:22 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-15  9:55 [PATCH v4 0/5] KVM: riscv: Add KVM_PRE_FAULT_MEMORY support Jinyu Tang
2026-08-15 10:00 ` [PATCH v4 1/5] KVM: riscv: Avoid overwriting G-stage tables with huge leaves Jinyu Tang
2026-08-15 10:00   ` Jinyu Tang
2026-08-15 10:04 ` [PATCH v4 2/5] KVM: riscv: Avoid THP adjustment for existing 4K G-stage leaves Jinyu Tang
2026-08-15 10:22   ` sashiko-bot
2026-08-15 10:06 ` [PATCH v4 3/5] KVM: riscv: Implement KVM_PRE_FAULT_MEMORY Jinyu Tang
2026-08-15 10:06   ` Jinyu Tang
2026-08-15 10:21   ` sashiko-bot
2026-08-15 10:08 ` [PATCH v4 4/5] KVM: selftests: Add RISC-V Sv57 page table indexing Jinyu Tang
2026-08-15 10:08   ` Jinyu Tang
2026-08-15 10:08 ` [PATCH v4 5/5] KVM: selftests: Enable pre_fault_memory_test for RISC-V Jinyu Tang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.