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;

Reply via email to