On 8/8/2026 5:52 AM, Ackerley Tng via B4 Relay wrote:
> From: Ackerley Tng <[email protected]>
> 
> A guest_memfd folio is safe for conversion if guest_memfd holds the last
> references on it. Any other references on the folio may indicate another
> user, and guest_memfd cannot convert it to private if there may be an
> existing host user.
> 
> A folio will have extra refcounts if it is present in a per-CPU lru_add
> fbatch. guest_memfd does not actually participate in LRU, but
> freshly-allocated folios are still added to the lru_add fbatch for batch
> LRU statistics processing.
> 
> This one known "usage" of the folio is handled by draining the lru_add
> fbatch. After draining, if the refcount is still elevated, then there's
> truly some other user of this page, and the page is not safe for
> conversion.
> 
> If the page may be dma pinned, DMA is obviously using it and hence not safe
> for conversions. If the page is still mapped after guest_memfd tried to
> unmap it earlier in the conversion process, it is also obviously not safe
> for conversion. Exit early to avoid unnecessary draining in these 2 cases.
> 
> Provide a drain status to only drain once ever while processing a batch of
> folios.
> 
> Acked-by: Vlastimil Babka (SUSE) <[email protected]>
> Suggested-by: David Hildenbrand <[email protected]>
> Signed-off-by: Ackerley Tng <[email protected]>

With the updated version of change log,

Reviewed-by: Binbin Wu <[email protected]>

One nit below.

[...]

> +static bool __folio_safe_for_conversion(struct folio *folio,
> +                                     enum lru_cache_drained *drained)
> +{
> +     const int filemap_get_folios_refcount = 1;
> +

Nit:
After adding the helper, the use of filemap_get_folios_refcount seems
less obvious. It introduces unnecessary tight coupling with the caller,
though it has only one caller currently.

Is it better to pass the value as extra_refs, similar to what
lru_cache_drain_for_folio() does? 


> +     if (folio_maybe_dma_pinned(folio) || folio_mapped(folio))
> +             return false;
> +
> +     lru_cache_drain_for_folio(folio, filemap_get_folios_refcount,
> +                               drained);
> +
> +     return folio_ref_count(folio) ==
> +            folio_nr_pages(folio) + filemap_get_folios_refcount;
> +}
> +
>  static bool kvm_gmem_is_safe_for_conversion(struct inode *inode, pgoff_t 
> start,
>                                           size_t nr_pages, pgoff_t *err_index)
>  {
> +     enum lru_cache_drained drained = LRU_CACHE_NOT_DRAINED;
>       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;
> @@ -560,9 +576,8 @@ static bool kvm_gmem_is_safe_for_conversion(struct inode 
> *inode, pgoff_t start,
>               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;
> +                     safe = __folio_safe_for_conversion(folio, &drained);
> +                     if (!safe) {
>                               *err_index = max(start, folio->index);
>                               break;
>                       }
> 


Reply via email to