On Tue, Sep 01, 2026, Fuad Tabba wrote:
> > diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> > index 803c7cdbbe0f6..fe02c47c85fb5 100644
> > --- a/virt/kvm/guest_memfd.c
> > +++ b/virt/kvm/guest_memfd.c
> > @@ -538,8 +538,46 @@ static int kvm_gmem_mas_preallocate(struct ma_state
> > *mas, u64 attributes,
> > return mas_preallocate(mas, xa_mk_value(attributes), GFP_KERNEL);
> > }
> >
> > +static bool kvm_gmem_has_outstanding_references(struct inode *inode,
> > + pgoff_t start, size_t
> > nr_pages,
> > + pgoff_t *err_index)
> > +{
> > + struct address_space *mapping = inode->i_mapping;
> > + pgoff_t last = start + nr_pages - 1;
> > + bool has_outstanding = false;
> > + struct folio_batch fbatch;
> > + pgoff_t next;
> > + int i;
> > +
> > + folio_batch_init(&fbatch);
> > +
> > + next = start;
> > + while (has_outstanding && filemap_get_folios(mapping, &next, last,
> > &fbatch)) {
>
> has_outstanding starts as false, so the loop never runs and the function
> always returns false. The outstanding-reference check is dead at this
> patch, so a to-private conversion would not be rejected even when a page
> still has an outstanding reference.
>
> It's fixed later in "KVM: guest_memfd: Handle lru_add fbatch refcounts
> during conversion safety check", which changes the condition to
> !has_outstanding. I think that fix belongs in this patch, so the check
> works when it is introduced and the series bisects cleanly.
Why even bother with has_outstanding? Avoiding it requires copy+pasting
folio_batch_release(), but it's less code and IMO the end result is a lot easier
to follow:
struct address_space *mapping = inode->i_mapping;
pgoff_t last = start + nr_pages - 1;
struct folio_batch fbatch;
pgoff_t next;
int i;
folio_batch_init(&fbatch);
next = start;
while (filemap_get_folios(mapping, &next, last, &fbatch)) {
for (i = 0; i < folio_batch_count(&fbatch); ++i) {
struct folio *folio = fbatch.folios[i];
/*
* Outstanding references are anything other than those
* from the page cache, plus 1 temporary reference held
* by filemap_get_folios() in the folio batch.
*/
if (folio_ref_count(folio) != folio_nr_pages(folio) +
1) {
*err_index = max(start, folio->index);
folio_batch_release(&fbatch);
return true;
}
}
folio_batch_release(&fbatch);
cond_resched();
}
return false;
and then we end up with:
enum lru_cache_drained drained = LRU_CACHE_NOT_DRAINED;
struct address_space *mapping = inode->i_mapping;
pgoff_t last = start + nr_pages - 1;
struct folio_batch fbatch;
pgoff_t next;
int i;
folio_batch_init(&fbatch);
next = start;
while (filemap_get_folios(mapping, &next, last, &fbatch)) {
for (i = 0; i < folio_batch_count(&fbatch); ++i) {
struct folio *folio = fbatch.folios[i];
if (__folio_has_outstanding_references(folio,
&drained)) {
*err_index = max(start, folio->index);
folio_batch_release(&fbatch);
return true;
}
}
folio_batch_release(&fbatch);
cond_resched();
}
return false;