Thread (85 messages) flat view 85 messages, 8 authors, 10d ago

Re: [PATCH v12 15/45] KVM: guest_memfd: Ensure pages are not in use before conversion

From: Sean Christopherson <seanjc@google.com>
Date: 2026-09-01 18:35:17
Also in: kvm, linux-coco, linux-doc, linux-kselftest, linux-mm, lkml

On Tue, Sep 01, 2026, Fuad Tabba wrote:
quoted
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;
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help