* [PATCH 0/4] Stop returning struct page from guest_memfd PFN lookup
@ 2026-08-18 8:12 Ackerley Tng
2026-08-18 8:12 ` [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Ackerley Tng @ 2026-08-18 8:12 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.
Holding a page refcount across fault handling is problematic for guest_memfd.
In-place memory conversions between confidential computing shared and private
states inspect folio refcounts to ensure exclusive ownership by guest_memfd. A
concurrent guest page fault taking a reference on the folio causes conversions
to fail due to an elevated refcount.
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.
I really want in-place conversions to merge in time for 7.4 and so I went
ahead to try this, building off Sean's sample code [1].
The main difference from [1] is that I think sev_handle_rmp_fault()
currently doesn't correctly handle the case where a racing truncation
removed the page from the RMP table. With that fixed, I think
sev_handle_rmp_fault() doesn't need to adopt the MMU invalidation protocol.
I also split the patch up so it's easier to review :)
[1] https://lore.kernel.org/all/an5RJYTwlYeym--O@google.com/
Signed-off-by: Ackerley Tng <ackerleytng@google.com>
---
Ackerley Tng (3):
KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure
KVM: SEV: Drop page refcount early during RMP fault handling
KVM: SEV: Drop page refcount early in VMSA reload
Sean Christopherson (1):
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 | 17 ++++++-----------
include/linux/kvm_host.h | 6 ++----
virt/kvm/guest_memfd.c | 9 ++-------
6 files changed, 15 insertions(+), 27 deletions(-)
---
base-commit: 1b731e5ded480bd1e5546aed35584238661ce72e
change-id: 20260818-gmem-no-return-page-614927a29f97
Best regards,
--
Ackerley Tng <ackerleytng@google.com>
^ permalink raw reply [flat|nested] 11+ messages in thread* [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure 2026-08-18 8:12 [PATCH 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng @ 2026-08-18 8:12 ` Ackerley Tng 2026-08-18 8:23 ` sashiko-bot 2026-08-18 8:12 ` [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng ` (2 subsequent siblings) 3 siblings, 1 reply; 11+ messages in thread From: Ackerley Tng @ 2026-08-18 8:12 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") 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.699.gb54405d56f-goog ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure 2026-08-18 8:12 ` [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng @ 2026-08-18 8:23 ` sashiko-bot 0 siblings, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-08-18 8:23 UTC (permalink / raw) To: Ackerley Tng; +Cc: Marc Zyngier, kvmarm, Oliver Upton, kvm Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [Medium] Unaligned GFN range in RMP fault handler causes erroneous invalidation of adjacent memory -- --- Patch [1]: [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure --- commit de1b2dc6f988d4cc54c9ff854b0263fe60c82d44 Author: Ackerley Tng <ackerleytng@google.com> KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure This commit modifies KVM's handling of RMP faults during a PSMASH operation. It updates the RMP entry lookup condition to treat an unassigned entry as an expected race when re-checking after a failed PSMASH, preventing unnecessary warning logs if another process transitions the page to shared. > 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", > gpa, pfn, ret); > } > > kvm_zap_gfn_range(kvm, gfn, gfn + PTRS_PER_PMD); [Severity: Medium] This is a pre-existing issue, but does this correctly align the guest frame number before zapping the range? Looking at sev_handle_rmp_fault(), the gfn is calculated from the unaligned faulting guest physical address. Since gfn isn't masked to a 2MB boundary, does adding PTRS_PER_PMD cause the zap range to cross into and erroneously invalidate up to 511 pages of the adjacent 2MB region? Additionally, if the NPT mappings were already 4K, would this unaligned gfn cause the function to miss zapping the start of the intended 2MB region? > out: > trace_kvm_rmp_fault(vcpu, gpa, pfn, error_code, rmp_level, ret); > } -- Sashiko AI review · https://sashiko.dev/#/patchset/20260818-gmem-no-return-page-v1-0-4f8d939efdbc@google.com?part=1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling 2026-08-18 8:12 [PATCH 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng 2026-08-18 8:12 ` [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng @ 2026-08-18 8:12 ` Ackerley Tng 2026-08-18 8:12 ` Yan Zhao 2026-08-18 8:26 ` sashiko-bot 2026-08-18 8:12 ` [PATCH 3/4] KVM: SEV: Drop page refcount early in VMSA reload Ackerley Tng 2026-08-18 8:12 ` [PATCH 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng 3 siblings, 2 replies; 11+ messages in thread From: Ackerley Tng @ 2026-08-18 8:12 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 retrieves the PFN for a private GPA from guest_memfd. The page reference taken during PFN lookup is not needed for the remainder of the handler, as checking the RMP entry, splitting the 2MB page via PSMASH, and zapping shadow page tables only operate on PFNs. Drop the page reference immediately after retrieving the PFN instead of holding it across the entire handler. A later patch will follow up with completely not returning refcounted pages from kvm_gmem_get_pfn(). No functional change intended. Signed-off-by: Ackerley Tng <ackerleytng@google.com> --- arch/x86/kvm/svm/sev.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index b2738362a928b..19c31fa944e34 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -5036,12 +5036,13 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) 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; + return; } /* @@ -5088,8 +5089,6 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) 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.699.gb54405d56f-goog ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling 2026-08-18 8:12 ` [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng @ 2026-08-18 8:12 ` Yan Zhao 2026-08-18 9:04 ` Ackerley Tng 2026-08-18 8:26 ` sashiko-bot 1 sibling, 1 reply; 11+ messages in thread From: Yan Zhao @ 2026-08-18 8:12 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, Fuad Tabba, Edgecombe, Rick P, Vishal Annapurve, kvm, linux-kernel, linux-arm-kernel, kvmarm On Tue, Aug 18, 2026 at 08:12:36AM +0000, Ackerley Tng wrote: > When handling an RMP fault, KVM retrieves the PFN for a private GPA from > guest_memfd. The page reference taken during PFN lookup is not needed for > the remainder of the handler, as checking the RMP entry, splitting the > 2MB page via PSMASH, and zapping shadow page tables only operate on PFNs. > > Drop the page reference immediately after retrieving the PFN instead of > holding it across the entire handler. > > A later patch will follow up with completely not returning refcounted pages > from kvm_gmem_get_pfn(). > > No functional change intended. > > Signed-off-by: Ackerley Tng <ackerleytng@google.com> > --- > arch/x86/kvm/svm/sev.c | 5 ++--- > 1 file changed, 2 insertions(+), 3 deletions(-) > > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c > index b2738362a928b..19c31fa944e34 100644 > --- a/arch/x86/kvm/svm/sev.c > +++ b/arch/x86/kvm/svm/sev.c > @@ -5036,12 +5036,13 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) > 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; > + return; > } Hi Ackerley, You missed the parts in [1] which save mmu_seq before invoking kvm_gmem_get_pfn(), + mmu_seq = kvm->mmu_invalidate_seq; + smp_rmb(); and check mmu_invalidate_retry_gfn() before snp_rmptable_psmash(pfn): + scoped_guard(read_lock)(&kvm->mmu_lock) { + if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) goto out; [1] https://lore.kernel.org/all/an5RJYTwlYeym--O@google.com/ > /* > @@ -5088,8 +5089,6 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) > 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.699.gb54405d56f-goog > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling 2026-08-18 8:12 ` Yan Zhao @ 2026-08-18 9:04 ` Ackerley Tng 0 siblings, 0 replies; 11+ messages in thread From: Ackerley Tng @ 2026-08-18 9:04 UTC (permalink / raw) To: Yan Zhao 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, Fuad Tabba, Edgecombe, Rick P, Vishal Annapurve, kvm, linux-kernel, linux-arm-kernel, kvmarm Yan Zhao <yan.y.zhao@intel.com> writes: > On Tue, Aug 18, 2026 at 08:12:36AM +0000, Ackerley Tng wrote: >> When handling an RMP fault, KVM retrieves the PFN for a private GPA from >> guest_memfd. The page reference taken during PFN lookup is not needed for >> the remainder of the handler, as checking the RMP entry, splitting the >> 2MB page via PSMASH, and zapping shadow page tables only operate on PFNs. >> >> Drop the page reference immediately after retrieving the PFN instead of >> holding it across the entire handler. >> >> A later patch will follow up with completely not returning refcounted pages >> from kvm_gmem_get_pfn(). >> >> No functional change intended. >> >> Signed-off-by: Ackerley Tng <ackerleytng@google.com> >> --- >> arch/x86/kvm/svm/sev.c | 5 ++--- >> 1 file changed, 2 insertions(+), 3 deletions(-) >> >> diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c >> index b2738362a928b..19c31fa944e34 100644 >> --- a/arch/x86/kvm/svm/sev.c >> +++ b/arch/x86/kvm/svm/sev.c >> @@ -5036,12 +5036,13 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) >> 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; >> + return; >> } > Hi Ackerley, > > You missed the parts in [1] which save mmu_seq before invoking kvm_gmem_get_pfn(), > + mmu_seq = kvm->mmu_invalidate_seq; > + smp_rmb(); > > and check mmu_invalidate_retry_gfn() before snp_rmptable_psmash(pfn): > > + scoped_guard(read_lock)(&kvm->mmu_lock) { > + if (mmu_invalidate_retry_gfn(kvm, mmu_seq, gfn)) > goto out; > > [1] https://lore.kernel.org/all/an5RJYTwlYeym--O@google.com/ I thought it wasn't necessary after the fix in patch 1 but the maintainer is always right, lol and Sashiko is right too. I'm preparing a v2. >> /* >> @@ -5088,8 +5089,6 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) >> 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.699.gb54405d56f-goog >> ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling 2026-08-18 8:12 ` [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng 2026-08-18 8:12 ` Yan Zhao @ 2026-08-18 8:26 ` sashiko-bot 1 sibling, 0 replies; 11+ messages in thread From: sashiko-bot @ 2026-08-18 8:26 UTC (permalink / raw) To: Ackerley Tng; +Cc: Oliver Upton, kvmarm, Marc Zyngier, kvm Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Critical] Dropping the page reference early exposes the physical frame (PFN) to concurrent freeing and reallocation before hardware RMP operations (PSMASH) execute, leading to cross-VM memory corruption. -- --- Patch [2]: [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling --- commit b722a12d672bd9f9b3789be73ead1445ff086955 Author: Ackerley Tng <ackerleytng@google.com> KVM: SEV: Drop page refcount early during RMP fault handling This patch modifies the RMP fault handler to drop the page reference retrieved from guest_memfd early. The intention is to avoid holding the reference throughout the rest of the handler, as the subsequent hardware operations and page table zapping only require the physical frame number. > diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c > index b2738362a928b..19c31fa944e34 100644 > --- a/arch/x86/kvm/svm/sev.c > +++ b/arch/x86/kvm/svm/sev.c [ ... ] > @@ -5036,12 +5036,13 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) > gpa); > return; > } > + kvm_release_page_unused(page); > > ret = snp_lookup_rmpentry(pfn, &assigned, &rmp_level); [Severity: Critical] Does dropping this page reference early expose the physical frame to concurrent freeing and reallocation before the hardware operations execute? Since sev_handle_rmp_fault() does not hold kvm->mmu_lock, if the page is unpinned here, couldn't a concurrent fallocate(FALLOC_FL_PUNCH_HOLE) on the guest_memfd unmap and free the page back to the host? If the host then reallocates this physical page to a different VM as a private 2MB hugepage, its reverse map entry would be assigned to the new VM. When this vCPU resumes, it could call snp_lookup_rmpentry(), see the page is assigned and 2MB, and proceed to execute snp_rmptable_psmash(): if (rmp_level == PG_LEVEL_4K) goto out; ret = snp_rmptable_psmash(pfn); Could this blindly shatter the other VM's 2MB entry into 4KB entries and break hardware memory isolation? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260818-gmem-no-return-page-v1-0-4f8d939efdbc@google.com?part=2 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/4] KVM: SEV: Drop page refcount early in VMSA reload 2026-08-18 8:12 [PATCH 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng 2026-08-18 8:12 ` [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng 2026-08-18 8:12 ` [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng @ 2026-08-18 8:12 ` Ackerley Tng 2026-08-18 8:12 ` [PATCH 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng 3 siblings, 0 replies; 11+ messages in thread From: Ackerley Tng @ 2026-08-18 8:12 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. 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 19c31fa944e34..3fc582fa343cc 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.699.gb54405d56f-goog ^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup 2026-08-18 8:12 [PATCH 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng ` (2 preceding siblings ...) 2026-08-18 8:12 ` [PATCH 3/4] KVM: SEV: Drop page refcount early in VMSA reload Ackerley Tng @ 2026-08-18 8:12 ` Ackerley Tng 2026-08-18 8:34 ` Yan Zhao 3 siblings, 1 reply; 11+ messages in thread From: Ackerley Tng @ 2026-08-18 8:12 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. Holding a page reference across fault handling is problematic for guest_memfd. In-place memory conversions between confidential computing shared and private states inspect folio refcounts to ensure exclusive ownership by guest_memfd. A concurrent guest page fault taking a reference on the folio causes conversions to fail due to an elevated refcount. 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. Furthermore, removing struct page from the guest_memfd PFN lookup moves KVM closer toward supporting memory backends that are not backed by struct page. Drop the folio reference immediately before returning from the guest_memfd PFN lookup, and stop returning the struct page pointer. For ARM, initialize the local page pointer to NULL so that the shared cleanup path that releases fault-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> 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, 11 insertions(+), 22 deletions(-) diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 6c941aaa10c63..e5d637a5ec558 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1613,7 +1613,7 @@ 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 page *page = NULL; struct kvm *kvm = s2fd->vcpu->kvm; void *memcache = NULL; kvm_pfn_t pfn; @@ -1641,7 +1641,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); 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 3fc582fa343cc..0a324e5b556b1 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); /* @@ -5002,7 +5000,6 @@ 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; - struct page *page; bool assigned; kvm_pfn_t pfn; gfn_t gfn; @@ -5029,13 +5026,12 @@ void sev_handle_rmp_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u64 error_code) return; } - 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.699.gb54405d56f-goog ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup 2026-08-18 8:12 ` [PATCH 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng @ 2026-08-18 8:34 ` Yan Zhao 2026-08-18 9:44 ` Ackerley Tng 0 siblings, 1 reply; 11+ messages in thread From: Yan Zhao @ 2026-08-18 8:34 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, Fuad Tabba, Edgecombe, Rick P, Vishal Annapurve, kvm, linux-kernel, linux-arm-kernel, kvmarm Hi Ackerley, > diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c > index 6c941aaa10c63..e5d637a5ec558 100644 > --- a/arch/arm64/kvm/mmu.c > +++ b/arch/arm64/kvm/mmu.c > @@ -1613,7 +1613,7 @@ 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; Just drop the page should be ok, since > + struct page *page = NULL; > struct kvm *kvm = s2fd->vcpu->kvm; > void *memcache = NULL; > kvm_pfn_t pfn; > @@ -1641,7 +1641,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); here missed a hunk: out_unlock: - kvm_release_faultin_page(kvm, page, !!ret, prot & KVM_PGTABLE_PROT_W); kvm_fault_unlock(kvm); I will fully test this series tomorrow. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup 2026-08-18 8:34 ` Yan Zhao @ 2026-08-18 9:44 ` Ackerley Tng 0 siblings, 0 replies; 11+ messages in thread From: Ackerley Tng @ 2026-08-18 9:44 UTC (permalink / raw) To: Yan Zhao 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, Fuad Tabba, Edgecombe, Rick P, Vishal Annapurve, kvm, linux-kernel, linux-arm-kernel, kvmarm Yan Zhao <yan.y.zhao@intel.com> writes: > Hi Ackerley, > >> diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c >> index 6c941aaa10c63..e5d637a5ec558 100644 >> --- a/arch/arm64/kvm/mmu.c >> +++ b/arch/arm64/kvm/mmu.c >> @@ -1613,7 +1613,7 @@ 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; > Just drop the page should be ok, since >> + struct page *page = NULL; >> struct kvm *kvm = s2fd->vcpu->kvm; >> void *memcache = NULL; >> kvm_pfn_t pfn; >> @@ -1641,7 +1641,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); > here missed a hunk: > out_unlock: > - kvm_release_faultin_page(kvm, page, !!ret, prot & KVM_PGTABLE_PROT_W); > kvm_fault_unlock(kvm); > > I will fully test this series tomorrow. Makes sense. gmem_abort() is purely gmem and there's non-gmem path to fill the page pointer in, so the above is correct. I'll not be respinning again today, sorry about the flurry of emails! ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-08-18 9:44 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-18 8:12 [PATCH 0/4] Stop returning struct page from guest_memfd PFN lookup Ackerley Tng 2026-08-18 8:12 ` [PATCH 1/4] KVM: SEV: Treat unassigned RMP entry as benign race on PSMASH failure Ackerley Tng 2026-08-18 8:23 ` sashiko-bot 2026-08-18 8:12 ` [PATCH 2/4] KVM: SEV: Drop page refcount early during RMP fault handling Ackerley Tng 2026-08-18 8:12 ` Yan Zhao 2026-08-18 9:04 ` Ackerley Tng 2026-08-18 8:26 ` sashiko-bot 2026-08-18 8:12 ` [PATCH 3/4] KVM: SEV: Drop page refcount early in VMSA reload Ackerley Tng 2026-08-18 8:12 ` [PATCH 4/4] KVM: guest_memfd: Stop returning struct page from PFN lookup Ackerley Tng 2026-08-18 8:34 ` Yan Zhao 2026-08-18 9:44 ` Ackerley Tng
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.