* [PATCH v3 0/4] Stop returning struct page from guest_memfd PFN lookup
@ 2026-08-20 23:32 Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Ackerley Tng @ 2026-08-20 23:32 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ashish Kalra,
Michael Roth, Brijesh Singh, Marc Zyngier, Oliver Upton,
Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, David Hildenbrand, Fuad Tabba,
Yan Zhao, Edgecombe, Rick P, Vishal Annapurve
Cc: kvm, linux-kernel, linux-arm-kernel, kvmarm, Ackerley Tng
KVM currently expects kvm_gmem_get_pfn() to return a refcounted struct
page. Callers (such as x86 TDP MMU, arm64 Stage-2 fault handler, and SEV-SNP
VMSA / RMP handlers) hold this refcount across page fault handling.
CoCo shared-to-private conversion handling must inspect folio refcounts to
ensure exclusive ownership by guest_memfd. A concurrent guest page fault
taking a temporary reference on the folio causes conversions to fail due to
an elevated refcount.
While this refcount is also taken on host userspace page faults, that
refcount is taken on behalf of the host userspace page tables. This
refcount will be dropped when conversions unmaps the page. Either way, once
there's an mmap() or userspace mapping, the pages are open to way more
refcounts, transient or not. This series focuses on just dropping refcounts
before handing KVM a page.
guest_memfd already notifies KVM of page invalidations, so callers within
KVM only need to respect the MMU invalidation protocol to safely rely on
guest_memfd for page presence.
guest_memfd already notifies KVM of page invalidations, so users of guest_memfd
within KVM only need to respect the MMU invalidation protocol to safely rely on
guest_memfd to ensure page presence.
This series first prepares the SEV-SNP handlers by treating unassigned RMP
entries as benign races on PSMASH failure (which can occur on concurrent
truncation) and dropping page references early in the RMP fault and VMSA reload
paths. It then updates kvm_gmem_get_pfn() to drop the folio reference internally
and stop returning a struct page pointer across x86 and arm64.
Removing struct page from kvm_gmem_get_pfn() also moves KVM closer toward
supporting memory backends that are not backed by struct page.
This is built off Sean's sample code [1].
[1] https://lore.kernel.org/all/an5RJYTwlYeym--O@google.com/
Thank you everybody for your quick reviews and testing, I really appreciate
it!
Changes from v2:
+ Picked up Reviewed-bys and Tested-bys
+ Addressed comments
v2: https://patch.msgid.link/20260818-gmem-no-return-page-v2-0-5298f42d49bb@google.com
v1: https://patch.msgid.link/20260818-gmem-no-return-page-v1-0-4f8d939efdbc@google.com
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
Ackerley Tng (2):
KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure
KVM: SEV: Drop page refcount early in VMSA reload
Sean Christopherson (2):
KVM: SEV: Drop page refcount early during RMP fault handling
KVM: guest_memfd: Stop returning struct page from PFN lookup
arch/arm64/kvm/mmu.c | 4 +---
arch/arm64/kvm/nested.c | 4 ++--
arch/x86/kvm/mmu/mmu.c | 2 +-
arch/x86/kvm/svm/sev.c | 53 ++++++++++++++++++++++++++++--------------------
include/linux/kvm_host.h | 6 ++----
virt/kvm/guest_memfd.c | 9 ++------
6 files changed, 39 insertions(+), 39 deletions(-)
---
base-commit: 1b731e5ded480bd1e5546aed35584238661ce72e
change-id: 20260818-gmem-no-return-page-614927a29f97
Best regards,
--
Ackerley Tng <ackerleytng@google.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure
2026-08-20 23:32 [PATCH v3 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng
@ 2026-08-20 23:32 ` Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Ackerley Tng @ 2026-08-20 23:32 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ashish Kalra,
Michael Roth, Brijesh Singh, Marc Zyngier, Oliver Upton,
Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, David Hildenbrand, Fuad Tabba,
Yan Zhao, Edgecombe, Rick P, Vishal Annapurve
Cc: kvm, linux-kernel, linux-arm-kernel, kvmarm, Ackerley Tng
When handling an RMP fault, KVM attempts to split a 2MB page via PSMASH.
If PSMASH fails, the only expected return value is FAIL_BADADDR, which does
not distinguish the reason for the bad address. Hence, another RMP entry
lookup is required to determine whether the failure was benign.
Specifically, KVM re-checks the RMP entry to determine if another CPU raced
and already smashed the entry into 4KB pages.
A concurrent operation (such as guest_memfd truncation or hole punching)
can also race and transition the page to shared, removing the page from the
RMP table and causing PSMASH to fail. This can happen even if the page is
still referenced by KVM, because guest_memfd reclaim transitions the RMP
entry to shared when the folio is removed from the page cache.
Treat an unassigned RMP entry as an expected race when re-checking after a
failed PSMASH, and skip logging an error warning.
Fixes: c63cf135cc99 ("KVM: SEV: Add support to handle RMP nested page faults")
Reviewed-by: Michael Roth <michael.roth@amd.com>
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
arch/x86/kvm/svm/sev.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index fcb41dfde4c02..b2738362a928b 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5074,10 +5074,11 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
/*
* Look it up again. If it's 4K now then the PSMASH may have
* raced with another process and the issue has already resolved
- * itself.
+ * itself. If it's not assigned, then this must have raced with
+ * another process that made this page shared.
*/
if (!snp_lookup_rmpentry(pfn, &assigned, &rmp_level) &&
- assigned && rmp_level == PG_LEVEL_4K)
+ ((assigned && rmp_level == PG_LEVEL_4K) || !assigned))
goto out;
pr_warn_ratelimited("SEV: Unable to split RMP entry for GPA 0x%llx PFN 0x%llx ret %d\n",
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 2/4] KVM: SEV: Drop page refcount early during RMP fault handling
2026-08-20 23:32 [PATCH v3 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng
@ 2026-08-20 23:32 ` Ackerley Tng
2026-08-21 21:50 ` Michael Roth
2026-08-20 23:32 ` [PATCH v3 3/4] KVM: SEV: Drop page refcount early in VMSA reload Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng
3 siblings, 1 reply; 7+ messages in thread
From: Ackerley Tng @ 2026-08-20 23:32 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ashish Kalra,
Michael Roth, Brijesh Singh, Marc Zyngier, Oliver Upton,
Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, David Hildenbrand, Fuad Tabba,
Yan Zhao, Edgecombe, Rick P, Vishal Annapurve
Cc: kvm, linux-kernel, linux-arm-kernel, kvmarm, Ackerley Tng
From: Sean Christopherson <seanjc@google.com>
When handling an RMP fault, KVM retrieves the PFN for a private GPA from
guest_memfd.
Drop the page reference immediately after retrieving the PFN instead of
holding it across the entire handler, and adopt the KVM MMU invalidation
protocol.
To avoid wrongly warning about not finding an assigned RMP entry if an
invalidation had taken place, check for invalidations before warning.
When the RMP level is 4K, the function exits. That doesn't need checking
for invalidations, since if it is 4K and there was an invalidation, not
psmashing and not zapping is the right thing to do.
If the RMP level is 2M (the only other option), use the invalidation
protocol before attempting to psmash. This ensures that if the page is
truncated and freed, and then re-allocated to another SNP VM (the RMP entry
is now assigned, but to another SNP VM), psmashing would be correctly
skipped.
A later patch will follow up with completely not returning refcounted pages
from kvm_gmem_get_pfn().
Signed-off-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Michael Roth <michael.roth@amd.com>
Co-developed-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
arch/x86/kvm/svm/sev.c | 47 ++++++++++++++++++++++++++++++-----------------
1 file changed, 30 insertions(+), 17 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index b2738362a928b..563870342a2ba 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -5003,6 +5003,7 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
struct kvm_memory_slot *slot;
struct kvm *kvm = vcpu->kvm;
int order, rmp_level, ret;
+ unsigned long mmu_seq;
struct page *page;
bool assigned;
kvm_pfn_t pfn;
@@ -5030,18 +5031,26 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
return;
}
+ mmu_seq = kvm->mmu_invalidate_seq;
+ smp_rmb();
+
ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &page, &order);
if (ret) {
pr_warn_ratelimited("SEV: Unexpected RMP fault, no backing page for private GPA 0x%llx\n",
gpa);
return;
}
+ kvm_release_page_unused(page);
ret = snp_lookup_rmpentry(pfn, &assigned, &rmp_level);
if (ret || !assigned) {
- pr_warn_ratelimited("SEV: Unexpected RMP fault, no assigned RMP entry found for GPA 0x%llx PFN 0x%llx error %d\n",
- gpa, pfn, ret);
- goto out_no_trace;
+ guard(read_lock)(&kvm->mmu_lock);
+
+ if (!mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn))
+ pr_warn_ratelimited("SEV: Unexpected RMP fault, no assigned RMP entry found for GPA 0x%llx PFN 0x%llx error %d\n",
+ gpa, pfn, ret);
+
+ return;
}
/*
@@ -5069,27 +5078,31 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
if (rmp_level == PG_LEVEL_4K)
goto out;
- ret = snp_rmptable_psmash(pfn);
- if (ret) {
- /*
- * Look it up again. If it's 4K now then the PSMASH may have
- * raced with another process and the issue has already resolved
- * itself. If it's not assigned, then this must have raced with
- * another process that made this page shared.
- */
- if (!snp_lookup_rmpentry(pfn, &assigned, &rmp_level) &&
- ((assigned && rmp_level == PG_LEVEL_4K) || !assigned))
+ scoped_guard(read_lock, &kvm->mmu_lock) {
+ if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn))
goto out;
- pr_warn_ratelimited("SEV: Unable to split RMP entry for GPA 0x%llx PFN 0x%llx ret %d\n",
- gpa, pfn, ret);
+ ret = snp_rmptable_psmash(pfn);
+ if (ret) {
+ /*
+ * Look it up again. If it's 4K now then the PSMASH may
+ * have raced with another process and the issue has
+ * already resolved itself. If it's not assigned, then
+ * this must have raced with another process that made
+ * this page shared.
+ */
+ if (!snp_lookup_rmpentry(pfn, &assigned, &rmp_level) &&
+ ((assigned && rmp_level == PG_LEVEL_4K) || !assigned))
+ goto out;
+
+ pr_warn_ratelimited("SEV: Unable to split RMP entry for GPA 0x%llx PFN 0x%llx ret %d\n",
+ gpa, pfn, ret);
+ }
}
kvm_zap_gfn_range(kvm, gfn, gfn + PTRS_PER_PMD);
out:
trace_kvm_rmp_fault(vcpu, gpa, pfn, error_code, rmp_level, ret);
-out_no_trace:
- kvm_release_page_unused(page);
}
static bool is_pfn_range_shared(kvm_pfn_t start, kvm_pfn_t end)
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 3/4] KVM: SEV: Drop page refcount early in VMSA reload
2026-08-20 23:32 [PATCH v3 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng
@ 2026-08-20 23:32 ` Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng
3 siblings, 0 replies; 7+ messages in thread
From: Ackerley Tng @ 2026-08-20 23:32 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ashish Kalra,
Michael Roth, Brijesh Singh, Marc Zyngier, Oliver Upton,
Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, David Hildenbrand, Fuad Tabba,
Yan Zhao, Edgecombe, Rick P, Vishal Annapurve
Cc: kvm, linux-kernel, linux-arm-kernel, kvmarm, Ackerley Tng
When reloading the guest VMSA for an SEV-SNP vCPU, KVM retrieves the PFN
from guest_memfd.
Drop the page reference immediately after retrieving the PFN instead of
holding it across MMU lock acquisition in preparation for a follow-up patch
to stop returning page pointers from guest_memfd PFN lookups.
This is safe because the page's validity and presence are governed by KVM's
MMU invalidation protocol rather than the page reference.
No functional change intended.
Reviewed-by: Michael Roth <michael.roth@amd.com>
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
arch/x86/kvm/svm/sev.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 563870342a2ba..0375ef709ee2c 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4062,6 +4062,7 @@ static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
*/
if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, &page, NULL))
return;
+ kvm_release_page_clean(page);
read_lock(&kvm->mmu_lock);
/*
@@ -4076,8 +4077,6 @@ static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
else
svm->vmcb->control.vmsa_pa = pfn_to_hpa(pfn);
read_unlock(&kvm->mmu_lock);
-
- kvm_release_page_clean(page);
}
/*
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v3 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup
2026-08-20 23:32 [PATCH v3 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng
` (2 preceding siblings ...)
2026-08-20 23:32 ` [PATCH v3 3/4] KVM: SEV: Drop page refcount early in VMSA reload Ackerley Tng
@ 2026-08-20 23:32 ` Ackerley Tng
2026-08-21 8:08 ` Fuad Tabba
3 siblings, 1 reply; 7+ messages in thread
From: Ackerley Tng @ 2026-08-20 23:32 UTC (permalink / raw)
To: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ashish Kalra,
Michael Roth, Brijesh Singh, Marc Zyngier, Oliver Upton,
Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, David Hildenbrand, Fuad Tabba,
Yan Zhao, Edgecombe, Rick P, Vishal Annapurve
Cc: kvm, linux-kernel, linux-arm-kernel, kvmarm, Ackerley Tng
From: Sean Christopherson <seanjc@google.com>
KVM currently expects guest_memfd PFN lookups to return a refcounted struct
page, which callers hold across fault handling.
Drop the page's refcount before returning from kvm_gmem_get_pfn() to
prepare for the in-place conversions series.
CoCo shared-to-private conversion handling must inspect folio refcounts to
ensure exclusive ownership by guest_memfd. A concurrent guest page fault
taking a temporary reference on the folio causes conversions to fail due to
an elevated refcount.
While this refcount is also taken on host userspace page faults, that
refcount is taken on behalf of the host userspace page tables. This
refcount will be dropped when conversions unmaps the page. Either way, once
there's an mmap() or userspace mapping, the pages are open to way more
refcounts, transient or not. This patch focuses on just dropping refcounts
before handing KVM a page.
guest_memfd already notifies KVM of page invalidations, so callers within
KVM only need to respect the MMU invalidation protocol to safely rely on
guest_memfd for page presence.
Since the page refcounts are dropped, don't return the struct page pointer.
Not returning the struct page from the guest_memfd PFN lookup moves KVM
closer toward supporting memory backends that are not backed by struct
page.
Here are some notes on the cleanup in the callers of kvm_gmem_get_pfn():
kvm_release_faultin_page() in ARM's gmem_abort() originally also serves to
set the page dirty and accessed under some conditions. The dirty and
accessed flags don't matter for guest_memfd anyway, so it is safe to just
drop the call to kvm_release_faultin_page().
For ARM's kvm_translate_vncr(), the local page pointer must be initialized
to NULL so that the shared cleanup path that releases faulted-in pages
safely no-ops for guest_memfd.
For x86, no additional changes are required in the MMU fault path because
the page fault tracking structure is zero-initialized at the start of page
fault handling, ensuring the refcounted page pointer is already NULL.
Reported-by: Yan Zhao <yan.y.zhao@intel.com>
Closes: https://lore.kernel.org/all/anZ4W9o5pTWIEgMY@yzhao56-desk.sh.intel.com/
Signed-off-by: Sean Christopherson <seanjc@google.com>
Co-developed-by: Yan Zhao <yan.y.zhao@intel.com>
Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Michael Roth <michael.roth@amd.com>
Tested-by: Michael Roth <michael.roth@amd.com>
Tested-by: Yan Zhao <yan.y.zhao@intel.com>
Co-developed-by: Ackerley Tng <ackerleytng@google.com>
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
arch/arm64/kvm/mmu.c | 4 +---
arch/arm64/kvm/nested.c | 4 ++--
arch/x86/kvm/mmu/mmu.c | 2 +-
arch/x86/kvm/svm/sev.c | 8 ++------
include/linux/kvm_host.h | 6 ++----
virt/kvm/guest_memfd.c | 9 ++-------
6 files changed, 10 insertions(+), 23 deletions(-)
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 6c941aaa10c63..d5aa197d2cbfd 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1613,7 +1613,6 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
unsigned long mmu_seq;
- struct page *page;
struct kvm *kvm = s2fd->vcpu->kvm;
void *memcache = NULL;
kvm_pfn_t pfn;
@@ -1641,7 +1640,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
/* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */
smp_rmb();
- ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, gfn, &pfn, &page, NULL);
+ ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, gfn, &pfn, NULL);
if (ret) {
kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE,
write_fault, exec_fault, false);
@@ -1681,7 +1680,6 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
}
out_unlock:
- kvm_release_faultin_page(kvm, page, !!ret, prot & KVM_PGTABLE_PROT_W);
kvm_fault_unlock(kvm);
if ((prot & KVM_PGTABLE_PROT_W) && !ret)
diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
index fb54f6dad995c..43523bb17621a 100644
--- a/arch/arm64/kvm/nested.c
+++ b/arch/arm64/kvm/nested.c
@@ -1360,7 +1360,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
bool write_fault, writable;
unsigned long mmu_seq;
struct vncr_tlb *vt;
- struct page *page;
+ struct page *page = NULL;
u64 va, pfn, gfn;
int ret;
@@ -1411,7 +1411,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
if (is_error_noslot_pfn(pfn) || (write_fault && !writable))
return -EFAULT;
} else {
- ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL);
+ ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, NULL);
if (ret) {
kvm_prepare_memory_fault_exit(vcpu, vt->wr.pa, PAGE_SIZE,
write_fault, false, false);
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index c519e8e8d646f..129d403308051 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -4604,7 +4604,7 @@ static int kvm_mmu_faultin_pfn_gmem(struct kvm_vcpu *vcpu,
}
r = kvm_gmem_get_pfn(vcpu->kvm, fault->slot, fault->gfn, &fault->pfn,
- &fault->refcounted_page, &max_order);
+ &max_order);
if (r) {
kvm_mmu_prepare_memory_fault_exit(vcpu, fault);
return r;
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 0375ef709ee2c..0c91c904573cc 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -4016,7 +4016,6 @@ static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
struct kvm *kvm = vcpu->kvm;
gfn_t gfn = gpa_to_gfn(gpa);
unsigned long mmu_seq;
- struct page *page;
kvm_pfn_t pfn;
lockdep_assert_held(&svm->sev_es.snp_vmsa_mutex);
@@ -4060,9 +4059,8 @@ static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
* The new VMSA will be private memory guest memory, so retrieve the
* PFN from the gmem backend.
*/
- if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, &page, NULL))
+ if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, NULL))
return;
- kvm_release_page_clean(page);
read_lock(&kvm->mmu_lock);
/*
@@ -5003,7 +5001,6 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
struct kvm *kvm = vcpu->kvm;
int order, rmp_level, ret;
unsigned long mmu_seq;
- struct page *page;
bool assigned;
kvm_pfn_t pfn;
gfn_t gfn;
@@ -5033,13 +5030,12 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
mmu_seq = kvm->mmu_invalidate_seq;
smp_rmb();
- ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &page, &order);
+ ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &order);
if (ret) {
pr_warn_ratelimited("SEV: Unexpected RMP fault, no backing page for private GPA 0x%llx\n",
gpa);
return;
}
- kvm_release_page_unused(page);
ret = snp_lookup_rmpentry(pfn, &assigned, &rmp_level);
if (ret || !assigned) {
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 03bfc92864b6e..502465119ca0c 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2586,13 +2586,11 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn)
#ifdef CONFIG_KVM_GUEST_MEMFD
int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
- gfn_t gfn, kvm_pfn_t *pfn, struct page **page,
- int *max_order);
+ gfn_t gfn, kvm_pfn_t *pfn, int *max_order);
#else
static inline int kvm_gmem_get_pfn(struct kvm *kvm,
struct kvm_memory_slot *slot, gfn_t gfn,
- kvm_pfn_t *pfn, struct page **page,
- int *max_order)
+ kvm_pfn_t *pfn, int *max_order)
{
KVM_BUG_ON(1, kvm);
return -EIO;
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index b596486d184ca..589762140c3ef 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -751,8 +751,7 @@ static struct folio *__kvm_gmem_get_pfn(struct file *file,
}
int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
- gfn_t gfn, kvm_pfn_t *pfn, struct page **page,
- int *max_order)
+ gfn_t gfn, kvm_pfn_t *pfn, int *max_order)
{
pgoff_t index = kvm_gmem_get_index(slot, gfn);
struct folio *folio;
@@ -780,11 +779,7 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
#endif
folio_unlock(folio);
-
- if (!r)
- *page = folio_file_page(folio, index);
- else
- folio_put(folio);
+ folio_put(folio);
return r;
}
--
2.55.0.766.g2966f0265a-goog
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup
2026-08-20 23:32 ` [PATCH v3 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng
@ 2026-08-21 8:08 ` Fuad Tabba
0 siblings, 0 replies; 7+ messages in thread
From: Fuad Tabba @ 2026-08-21 8:08 UTC (permalink / raw)
To: Ackerley Tng
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ashish Kalra,
Michael Roth, Brijesh Singh, Marc Zyngier, Oliver Upton,
Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu,
Catalin Marinas, Will Deacon, David Hildenbrand, Yan Zhao,
Edgecombe, Rick P, Vishal Annapurve, kvm, linux-kernel,
linux-arm-kernel, kvmarm
On Fri, 21 Aug 2026 at 00:32, Ackerley Tng <ackerleytng@google.com> wrote:
>
> From: Sean Christopherson <seanjc@google.com>
>
> KVM currently expects guest_memfd PFN lookups to return a refcounted struct
> page, which callers hold across fault handling.
>
> Drop the page's refcount before returning from kvm_gmem_get_pfn() to
> prepare for the in-place conversions series.
>
> CoCo shared-to-private conversion handling must inspect folio refcounts to
> ensure exclusive ownership by guest_memfd. A concurrent guest page fault
> taking a temporary reference on the folio causes conversions to fail due to
> an elevated refcount.
>
> While this refcount is also taken on host userspace page faults, that
> refcount is taken on behalf of the host userspace page tables. This
> refcount will be dropped when conversions unmaps the page. Either way, once
> there's an mmap() or userspace mapping, the pages are open to way more
> refcounts, transient or not. This patch focuses on just dropping refcounts
> before handing KVM a page.
>
> guest_memfd already notifies KVM of page invalidations, so callers within
> KVM only need to respect the MMU invalidation protocol to safely rely on
> guest_memfd for page presence.
>
> Since the page refcounts are dropped, don't return the struct page pointer.
>
> Not returning the struct page from the guest_memfd PFN lookup moves KVM
> closer toward supporting memory backends that are not backed by struct
> page.
>
> Here are some notes on the cleanup in the callers of kvm_gmem_get_pfn():
>
> kvm_release_faultin_page() in ARM's gmem_abort() originally also serves to
> set the page dirty and accessed under some conditions. The dirty and
> accessed flags don't matter for guest_memfd anyway, so it is safe to just
> drop the call to kvm_release_faultin_page().
>
> For ARM's kvm_translate_vncr(), the local page pointer must be initialized
> to NULL so that the shared cleanup path that releases faulted-in pages
> safely no-ops for guest_memfd.
>
> For x86, no additional changes are required in the MMU fault path because
> the page fault tracking structure is zero-initialized at the start of page
> fault handling, ensuring the refcounted page pointer is already NULL.
>
> Reported-by: Yan Zhao <yan.y.zhao@intel.com>
> Closes: https://lore.kernel.org/all/anZ4W9o5pTWIEgMY@yzhao56-desk.sh.intel.com/
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> Co-developed-by: Yan Zhao <yan.y.zhao@intel.com>
> Signed-off-by: Yan Zhao <yan.y.zhao@intel.com>
> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
> Reviewed-by: Michael Roth <michael.roth@amd.com>
> Tested-by: Michael Roth <michael.roth@amd.com>
> Tested-by: Yan Zhao <yan.y.zhao@intel.com>
> Co-developed-by: Ackerley Tng <ackerleytng@google.com>
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>
For the arm64 part: tested under QEMU: guest boots with anonymous
memory and guest_memfd, plus the guest_memfd_test selftest.
Reviewed-by: Fuad Tabba <fuad.tabba@linux.dev>
Tested-by: Fuad Tabba < fuad.tabba@linux.dev>
Cheers,
/fuad
> ---
> arch/arm64/kvm/mmu.c | 4 +---
> arch/arm64/kvm/nested.c | 4 ++--
> arch/x86/kvm/mmu/mmu.c | 2 +-
> arch/x86/kvm/svm/sev.c | 8 ++------
> include/linux/kvm_host.h | 6 ++----
> virt/kvm/guest_memfd.c | 9 ++-------
> 6 files changed, 10 insertions(+), 23 deletions(-)
>
> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
> index 6c941aaa10c63..d5aa197d2cbfd 100644
> --- a/arch/arm64/kvm/mmu.c
> +++ b/arch/arm64/kvm/mmu.c
> @@ -1613,7 +1613,6 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_R;
> struct kvm_pgtable *pgt = s2fd->vcpu->arch.hw_mmu->pgt;
> unsigned long mmu_seq;
> - struct page *page;
> struct kvm *kvm = s2fd->vcpu->kvm;
> void *memcache = NULL;
> kvm_pfn_t pfn;
> @@ -1641,7 +1640,7 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> /* Pairs with the smp_wmb() in kvm_mmu_invalidate_end(). */
> smp_rmb();
>
> - ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, gfn, &pfn, &page, NULL);
> + ret = kvm_gmem_get_pfn(kvm, s2fd->memslot, gfn, &pfn, NULL);
> if (ret) {
> kvm_prepare_memory_fault_exit(s2fd->vcpu, s2fd->fault_ipa, PAGE_SIZE,
> write_fault, exec_fault, false);
> @@ -1681,7 +1680,6 @@ static int gmem_abort(const struct kvm_s2_fault_desc *s2fd)
> }
>
> out_unlock:
> - kvm_release_faultin_page(kvm, page, !!ret, prot & KVM_PGTABLE_PROT_W);
> kvm_fault_unlock(kvm);
>
> if ((prot & KVM_PGTABLE_PROT_W) && !ret)
> diff --git a/arch/arm64/kvm/nested.c b/arch/arm64/kvm/nested.c
> index fb54f6dad995c..43523bb17621a 100644
> --- a/arch/arm64/kvm/nested.c
> +++ b/arch/arm64/kvm/nested.c
> @@ -1360,7 +1360,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
> bool write_fault, writable;
> unsigned long mmu_seq;
> struct vncr_tlb *vt;
> - struct page *page;
> + struct page *page = NULL;
> u64 va, pfn, gfn;
> int ret;
>
> @@ -1411,7 +1411,7 @@ static int kvm_translate_vncr(struct kvm_vcpu *vcpu, bool *is_gmem)
> if (is_error_noslot_pfn(pfn) || (write_fault && !writable))
> return -EFAULT;
> } else {
> - ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, &page, NULL);
> + ret = kvm_gmem_get_pfn(vcpu->kvm, memslot, gfn, &pfn, NULL);
> if (ret) {
> kvm_prepare_memory_fault_exit(vcpu, vt->wr.pa, PAGE_SIZE,
> write_fault, false, false);
> diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
> index c519e8e8d646f..129d403308051 100644
> --- a/arch/x86/kvm/mmu/mmu.c
> +++ b/arch/x86/kvm/mmu/mmu.c
> @@ -4604,7 +4604,7 @@ static int kvm_mmu_faultin_pfn_gmem(struct kvm_vcpu *vcpu,
> }
>
> r = kvm_gmem_get_pfn(vcpu->kvm, fault->slot, fault->gfn, &fault->pfn,
> - &fault->refcounted_page, &max_order);
> + &max_order);
> if (r) {
> kvm_mmu_prepare_memory_fault_exit(vcpu, fault);
> return r;
> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> index 0375ef709ee2c..0c91c904573cc 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -4016,7 +4016,6 @@ static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
> struct kvm *kvm = vcpu->kvm;
> gfn_t gfn = gpa_to_gfn(gpa);
> unsigned long mmu_seq;
> - struct page *page;
> kvm_pfn_t pfn;
>
> lockdep_assert_held(&svm->sev_es.snp_vmsa_mutex);
> @@ -4060,9 +4059,8 @@ static void __sev_snp_reload_vmsa(struct kvm_vcpu *vcpu, gpa_t gpa)
> * The new VMSA will be private memory guest memory, so retrieve the
> * PFN from the gmem backend.
> */
> - if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, &page, NULL))
> + if (kvm_gmem_get_pfn(vcpu->kvm, slot, gfn, &pfn, NULL))
> return;
> - kvm_release_page_clean(page);
>
> read_lock(&kvm->mmu_lock);
> /*
> @@ -5003,7 +5001,6 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
> struct kvm *kvm = vcpu->kvm;
> int order, rmp_level, ret;
> unsigned long mmu_seq;
> - struct page *page;
> bool assigned;
> kvm_pfn_t pfn;
> gfn_t gfn;
> @@ -5033,13 +5030,12 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
> mmu_seq = kvm->mmu_invalidate_seq;
> smp_rmb();
>
> - ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &page, &order);
> + ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &order);
> if (ret) {
> pr_warn_ratelimited("SEV: Unexpected RMP fault, no backing page for private GPA 0x%llx\n",
> gpa);
> return;
> }
> - kvm_release_page_unused(page);
>
> ret = snp_lookup_rmpentry(pfn, &assigned, &rmp_level);
> if (ret || !assigned) {
> diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
> index 03bfc92864b6e..502465119ca0c 100644
> --- a/include/linux/kvm_host.h
> +++ b/include/linux/kvm_host.h
> @@ -2586,13 +2586,11 @@ static inline bool kvm_mem_is_private(struct kvm *kvm, gfn_t gfn)
>
> #ifdef CONFIG_KVM_GUEST_MEMFD
> int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
> - gfn_t gfn, kvm_pfn_t *pfn, struct page **page,
> - int *max_order);
> + gfn_t gfn, kvm_pfn_t *pfn, int *max_order);
> #else
> static inline int kvm_gmem_get_pfn(struct kvm *kvm,
> struct kvm_memory_slot *slot, gfn_t gfn,
> - kvm_pfn_t *pfn, struct page **page,
> - int *max_order)
> + kvm_pfn_t *pfn, int *max_order)
> {
> KVM_BUG_ON(1, kvm);
> return -EIO;
> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index b596486d184ca..589762140c3ef 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
> @@ -751,8 +751,7 @@ static struct folio *__kvm_gmem_get_pfn(struct file *file,
> }
>
> int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
> - gfn_t gfn, kvm_pfn_t *pfn, struct page **page,
> - int *max_order)
> + gfn_t gfn, kvm_pfn_t *pfn, int *max_order)
> {
> pgoff_t index = kvm_gmem_get_index(slot, gfn);
> struct folio *folio;
> @@ -780,11 +779,7 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct kvm_memory_slot *slot,
> #endif
>
> folio_unlock(folio);
> -
> - if (!r)
> - *page = folio_file_page(folio, index);
> - else
> - folio_put(folio);
> + folio_put(folio);
>
> return r;
> }
>
> --
> 2.55.0.766.g2966f0265a-goog
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/4] KVM: SEV: Drop page refcount early during RMP fault handling
2026-08-20 23:32 ` [PATCH v3 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng
@ 2026-08-21 21:50 ` Michael Roth
0 siblings, 0 replies; 7+ messages in thread
From: Michael Roth @ 2026-08-21 21:50 UTC (permalink / raw)
To: Ackerley Tng
Cc: Sean Christopherson, Paolo Bonzini, Thomas Gleixner, Ingo Molnar,
Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Ashish Kalra,
Brijesh Singh, Marc Zyngier, Oliver Upton, Joey Gouly,
Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, David Hildenbrand, Fuad Tabba, Yan Zhao,
Edgecombe, Rick P, Vishal Annapurve, kvm, linux-kernel,
linux-arm-kernel, kvmarm
On Thu, Aug 20, 2026 at 11:32:35PM +0000, Ackerley Tng wrote:
> From: Sean Christopherson <seanjc@google.com>
>
> When handling an RMP fault, KVM retrieves the PFN for a private GPA from
> guest_memfd.
>
> Drop the page reference immediately after retrieving the PFN instead of
> holding it across the entire handler, and adopt the KVM MMU invalidation
> protocol.
>
> To avoid wrongly warning about not finding an assigned RMP entry if an
> invalidation had taken place, check for invalidations before warning.
I'm ok either way, but something like the following I think would read a
bit clearer:
When handling an RMP fault, KVM retrieves the PFN for a private GPA from
guest_memfd as well as a refcounted struct page. This reference is
held for the duration of the RMP fault handling to ensure the page isn't
reallocated to another guest while processing the RMP fault.
A later patch will follow up with completely not returning refcounted pages
from kvm_gmem_get_pfn(), so prepare for that by dropping the page reference
immediately after retrieving the PFN and, in place of that, implement the
stardard KVM MMU invalidation logic to address the above scenario.
While here, also make use of this KVM MMU invalidation logic to avoid
warning about not finding an assigned RMP entry by first checking if an
invalidation had taken place since entering the RMP fault handler.
But after writing that I realize the last paragraph is pertaining to a
new hunk added in v3, which is sort of a bug fix (in the same that patch #1 is
a fix anyway), since it fixes spurious warnings that were previously
triggerable via truncate() racing with RMP faults. However, that's exactly the
same scenario patch #1 is trying to fix, except in this case the race happens
before this path reaches the PSMASH vs. after the PSMASH like patch #1
addresses.
However, unlike patch #1 where we only need to check once again
after-the-fact, it's not possible to reliably check the before case without
the MMU invalidation logic in place, so I'm okay with squashing that
additional check in here, but decoupling that change (prep-for-something-else
vs. bug-fix made possible by prep-for-something-else) and moving it to a
patch #3 would help with de-tangle things for future git blame's.
(sorry if this seems like nit-picking; this function unfortunately spends a
lot of time in the spotlight so trying to make life easier for people making
sense of it)
Thanks,
Mike
> When handling an RMP fault, KVM retrieves the PFN for a private GPA from
> guest_memfd.
>
> Drop the page reference immediately after retrieving the PFN instead of
> holding it across the entire handler, and adopt the KVM MMU invalidation
> protocol.
>
> To avoid wrongly warning about not finding an assigned RMP entry if an
> invalidation had taken place, check for invalidations before warning.
>
> When the RMP level is 4K, the function exits. That doesn't need checking
> for invalidations, since if it is 4K and there was an invalidation, not
> psmashing and not zapping is the right thing to do.
>
> If the RMP level is 2M (the only other option), use the invalidation
> protocol before attempting to psmash. This ensures that if the page is
> truncated and freed, and then re-allocated to another SNP VM (the RMP entry
> is now assigned, but to another SNP VM), psmashing would be correctly
> skipped.
>
> A later patch will follow up with completely not returning refcounted pages
> from kvm_gmem_get_pfn().
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> Reviewed-by: Michael Roth <michael.roth@amd.com>
> Co-developed-by: Ackerley Tng <ackerleytng@google.com>
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>
> ---
> arch/x86/kvm/svm/sev.c | 47 ++++++++++++++++++++++++++++++-----------------
> 1 file changed, 30 insertions(+), 17 deletions(-)
>
> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
> index b2738362a928b..563870342a2ba 100644
> --- a/arch/x86/kvm/svm/sev.c
> +++ b/arch/x86/kvm/svm/sev.c
> @@ -5003,6 +5003,7 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
> struct kvm_memory_slot *slot;
> struct kvm *kvm = vcpu->kvm;
> int order, rmp_level, ret;
> + unsigned long mmu_seq;
> struct page *page;
> bool assigned;
> kvm_pfn_t pfn;
> @@ -5030,18 +5031,26 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
> return;
> }
>
> + mmu_seq = kvm->mmu_invalidate_seq;
> + smp_rmb();
> +
> ret = kvm_gmem_get_pfn(kvm, slot, gfn, &pfn, &page, &order);
> if (ret) {
> pr_warn_ratelimited("SEV: Unexpected RMP fault, no backing page for private GPA 0x%llx\n",
> gpa);
> return;
> }
> + kvm_release_page_unused(page);
>
> ret = snp_lookup_rmpentry(pfn, &assigned, &rmp_level);
> if (ret || !assigned) {
> - pr_warn_ratelimited("SEV: Unexpected RMP fault, no assigned RMP entry found for GPA 0x%llx PFN 0x%llx error %d\n",
> - gpa, pfn, ret);
> - goto out_no_trace;
> + guard(read_lock)(&kvm->mmu_lock);
> +
> + if (!mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn))
> + pr_warn_ratelimited("SEV: Unexpected RMP fault, no assigned RMP entry found for GPA 0x%llx PFN 0x%llx error %d\n",
> + gpa, pfn, ret);
> +
> + return;
> }
>
> /*
> @@ -5069,27 +5078,31 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code)
> if (rmp_level == PG_LEVEL_4K)
> goto out;
>
> - ret = snp_rmptable_psmash(pfn);
> - if (ret) {
> - /*
> - * Look it up again. If it's 4K now then the PSMASH may have
> - * raced with another process and the issue has already resolved
> - * itself. If it's not assigned, then this must have raced with
> - * another process that made this page shared.
> - */
> - if (!snp_lookup_rmpentry(pfn, &assigned, &rmp_level) &&
> - ((assigned && rmp_level == PG_LEVEL_4K) || !assigned))
> + scoped_guard(read_lock, &kvm->mmu_lock) {
> + if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn))
> goto out;
>
> - pr_warn_ratelimited("SEV: Unable to split RMP entry for GPA 0x%llx PFN 0x%llx ret %d\n",
> - gpa, pfn, ret);
> + ret = snp_rmptable_psmash(pfn);
> + if (ret) {
> + /*
> + * Look it up again. If it's 4K now then the PSMASH may
> + * have raced with another process and the issue has
> + * already resolved itself. If it's not assigned, then
> + * this must have raced with another process that made
> + * this page shared.
> + */
> + if (!snp_lookup_rmpentry(pfn, &assigned, &rmp_level) &&
> + ((assigned && rmp_level == PG_LEVEL_4K) || !assigned))
> + goto out;
> +
> + pr_warn_ratelimited("SEV: Unable to split RMP entry for GPA 0x%llx PFN 0x%llx ret %d\n",
> + gpa, pfn, ret);
> + }
> }
>
> kvm_zap_gfn_range(kvm, gfn, gfn + PTRS_PER_PMD);
> out:
> trace_kvm_rmp_fault(vcpu, gpa, pfn, error_code, rmp_level, ret);
> -out_no_trace:
> - kvm_release_page_unused(page);
> }
>
> static bool is_pfn_range_shared(kvm_pfn_t start, kvm_pfn_t end)
>
> --
> 2.55.0.766.g2966f0265a-goog
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-08-21 21:50 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-20 23:32 [PATCH v3 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng
2026-08-21 21:50 ` Michael Roth
2026-08-20 23:32 ` [PATCH v3 3/4] KVM: SEV: Drop page refcount early in VMSA reload Ackerley Tng
2026-08-20 23:32 ` [PATCH v3 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng
2026-08-21 8:08 ` Fuad Tabba
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox