Re: [PATCH v10 11/41] KVM: guest_memfd: Ensure pages are not in use before conversion
From: Yan Zhao <hidden>
Date: 2026-08-09 22:32:45
Also in:
kvm, linux-coco, linux-doc, linux-kselftest, linux-mm, lkml
Subsystem:
kernel virtual machine (kvm), the rest · Maintainers:
Paolo Bonzini, Linus Torvalds
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:quoted
+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 <redacted> 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 <redacted> --- 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