On Sat, Aug 08, 2026 at 08:29:15AM +0800, Yan Zhao wrote:
> On Fri, Aug 07, 2026 at 02:52:50PM -0700, Ackerley Tng via B4 Relay wrote:
> > +static bool kvm_gmem_is_safe_for_conversion(struct inode *inode, pgoff_t
> > start,
> > + size_t nr_pages, pgoff_t *err_index)
> > +{
> > + struct address_space *mapping = inode->i_mapping;
> > + const int filemap_get_folios_refcount = 1;
> > + pgoff_t last = start + nr_pages - 1;
> > + struct folio_batch fbatch;
> > + bool safe = true;
> > + pgoff_t next;
> > + int i;
> > +
> > + folio_batch_init(&fbatch);
> > +
> > + next = start;
> > + while (safe && filemap_get_folios(mapping, &next, last, &fbatch)) {
> > + for (i = 0; i < folio_batch_count(&fbatch); ++i) {
> > + struct folio *folio = fbatch.folios[i];
> > +
> > + if (folio_ref_count(folio) !=
> > + folio_nr_pages(folio) +
> > filemap_get_folios_refcount) {
> > + safe = false;
> > + *err_index = max(start, folio->index);
> > + break;
> > + }
> > + }
> > +
> > + folio_batch_release(&fbatch);
> > + cond_resched();
> > + }
> > +
> > + return safe;
> > +}
> > +
> > static int __kvm_gmem_set_attributes(struct inode *inode, pgoff_t start,
> > - size_t nr_pages, uint64_t attrs)
> > + size_t nr_pages, uint64_t attrs,
> > + pgoff_t *err_index)
> > {
> > bool to_private = attrs & KVM_MEMORY_ATTRIBUTE_PRIVATE;
> > struct address_space *mapping = inode->i_mapping;
> > @@ -542,8 +576,21 @@ static int __kvm_gmem_set_attributes(struct inode
> > *inode, pgoff_t start,
> >
> > mas_init(&mas, mt, start);
> > r = kvm_gmem_mas_preallocate(&mas, attrs, start, nr_pages);
> > - if (r)
> > + if (r) {
> > + *err_index = start;
> > goto out;
> > + }
> > +
> > + if (to_private) {
> > + unmap_mapping_pages(mapping, start, nr_pages, false);
> > +
> > + if (!kvm_gmem_is_safe_for_conversion(inode, start, nr_pages,
> > + err_index)) {
> Note: conversion failures could occur if another vCPU is attempting to map a
> GFN
> within this range.
>
> CPU 0 (setting attributes) CPU 1 (attempting to map)
> -------------------------- --------------------
> A: mmu_invalidate_retry_gfn_unsafe
> filemap_invalidate_lock_shared
> __kvm_gmem_get_pfn ==> folio refcount++
> filemap_invalidate_unlock_shared
>
> filemap_invalidate_lock
> filemap_get_folios
> check folio_ref_count(folio) ==> Not match !!
> filemap_invalidate_unlock
>
> B: read_lock(&vcpu->kvm->mmu_lock);
> is_page_fault_stale
> kvm_mmu_finish_page_fault ==>folio
> recount--
> read_unlock(&vcpu->kvm->mmu_lock);
>
>
> Retrying in kvm_gmem_is_safe_for_conversion() or moving the invocation of
> kvm_mmu_invalidate_start() + kvm_mmu_invalidate_range_add() to an earlier
> position does not help as long as CPU 1 stays at stage A.
>
> So, should we avoid this failure?
> e.g., by moving filemap_invalidate_unlock_shared() from stage A to after
> stage B?
Or what about having KVM always treat gmem page as non-refcounted, and have
kvm_gmem_get_pfn() put folio refcount before releasing the filemap invalidate
lock?
Below patch is applied and tested at the end of this series.
>From 8c2f29bc15bceb6a8fa103cf2585ec11354fd74e Mon Sep 17 00:00:00 2001
From: Yan Zhao <[email protected]>
Date: Mon, 10 Aug 2026 06:24:52 +0800
Subject: [PATCH] KVM: guest_memfd: Return gmem page as non-refcounted
Have kvm_gmem_get_pfn() put gmem page refcount before releasing filemap
invalidate lock and return the gmem page as non-refcounted. This avoids
gmem memory attribute conversion failure caused by temporarily holding gmem
page after faulting and before completing mapping.
guest_memfd always holds gmem page in filemap cache. TDX does not increment
gmem page refcount when having gmem pages mapped in S-EPT. Additionally,
as gmem pages are not swappable, setting dirty or accessed bit is not
necessary. Therefore, there's no need to treat gmem pages as refcounted
pages.
Signed-off-by: Yan Zhao <[email protected]>
---
virt/kvm/guest_memfd.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 2115e73e455a..e357b4ffa777 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -1332,11 +1332,10 @@ int kvm_gmem_get_pfn(struct kvm *kvm, struct
kvm_memory_slot *slot,
#endif
folio_unlock(folio);
+ folio_put(folio);
if (!r)
- *page = folio_file_page(folio, index);
- else
- folio_put(folio);
+ *page = NULL;
out:
filemap_invalidate_unlock_shared(file_inode(file)->i_mapping);
--
2.43.2