Re: [PATCH v10 11/41] KVM: guest_memfd: Ensure pages are not in use before conversion
From: Yan Zhao
Date: Sun Aug 09 2026 - 18:33:12 EST
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.