On 8/7/26 23:52, Ackerley Tng via B4 Relay wrote:
> From: Ackerley Tng <[email protected]>
> 
> When converting memory to private in guest_memfd, it is necessary to ensure
> that the pages are not currently being accessed by any other part of the
> kernel or userspace to avoid any current user writing to guest private
> memory.
> 
> guest_memfd checks for unexpected refcounts to determine whether a page is
> still in use. The only expected refcounts after unmapping the range
> requested for conversion are those that are held by guest_memfd itself.
> 
> Update the kvm_memory_attributes2 structure to include an error_offset
> field. This allows KVM to report the exact offset where a conversion
> failed to userspace. If the safety check fails, return -EAGAIN and copy
> the error_offset back to userspace so that it can potentially retry the
> operation or handle the failure gracefully.
> 
> Update documentation to document the error_offset field and the possible
> -EAGAIN error.
> 
> Suggested-by: David Hildenbrand <[email protected]>
> Co-developed-by: Vishal Annapurve <[email protected]>
> Signed-off-by: Vishal Annapurve <[email protected]>
> Reviewed-by: Fuad Tabba <[email protected]>
> Tested-by: Shivank Garg <[email protected]>
> Signed-off-by: Ackerley Tng <[email protected]>
> ---

[...]

>  #define KVM_MEMORY_ATTRIBUTE_PRIVATE           (1ULL << 3)
> diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
> index 3783e63476569..13c3989136f67 100644
> --- a/virt/kvm/guest_memfd.c
> +++ b/virt/kvm/guest_memfd.c
> @@ -524,8 +524,42 @@ 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_is_safe_for_conversion(struct inode *inode, pgoff_t 
> start,
> +                                         size_t nr_pages, pgoff_t *err_index)

I would focus on the "to_private" aspect or abstract it to
"kvm_gmem_mem_has_unexpected_refs" or sth like that.

> +{
> +     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) {

I'd rather add a comment than have this filemap_get_folios_refcount.

/*
 * We expect one reference per folio-page in the pagecache and one
 * reference from filemap_get_folios().
 */
if (folio_ref_count(folio) != folio_nr_pages(folio) + 1)

> +                             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) {

I'd add a comment here for the "why are we unmapping".

-- 
Cheers,

David

Reply via email to