[PATCH v9 15/41] KVM: guest_memfd: Handle lru_add fbatch refcounts during conversion safety check

From: Ackerley Tng via B4 Relay

Date: Tue Jul 28 2026 - 20:59:16 EST


From: Ackerley Tng <ackerleytng@xxxxxxxxxx>

When checking if a guest_memfd folio is safe for conversion, its refcount
is examined. A folio may be present in a per-CPU lru_add fbatch, which
temporarily increases its refcount. This can lead to a false positive,
incorrectly indicating that the folio is in use and preventing the
conversion, even if it is otherwise safe.

The conversion process might not be on the same CPU that holds the folio in
its fbatch. Hence, use lru_add_drain_progressive() to progressively drain
lru_add fbatches.

guest_memfd folios are unevictable, so they can only reside in the lru_add
fbatch. If the folio's refcount is still unsafe after draining, then the
conversion is truly unsafe and has to be aborted.

Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
---
mm/swap.c | 2 ++
virt/kvm/guest_memfd.c | 12 ++++++++++--
2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/mm/swap.c b/mm/swap.c
index 0f9465d31fe52..4427d76c88d6d 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -37,6 +37,7 @@
#include <linux/page_idle.h>
#include <linux/local_lock.h>
#include <linux/buffer_head.h>
+#include <linux/kvm_types.h>

#include "internal.h"

@@ -964,6 +965,7 @@ bool lru_add_drain_progressive(int *drain_state)
}
return false;
}
+EXPORT_SYMBOL_FOR_KVM(lru_add_drain_progressive);

atomic_t lru_disable_count = ATOMIC_INIT(0);

diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 928fc7f4a01e4..505bb6747620b 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -8,6 +8,7 @@
#include <linux/mempolicy.h>
#include <linux/pseudo_fs.h>
#include <linux/pagemap.h>
+#include <linux/swap.h>

#include "kvm_mm.h"

@@ -554,6 +555,7 @@ static bool kvm_gmem_is_safe_for_conversion(struct inode *inode, pgoff_t start,
const int filemap_get_folios_refcount = 1;
pgoff_t last = start + nr_pages - 1;
struct folio_batch fbatch;
+ int drain_state = 0;
bool safe = true;
pgoff_t next;
int i;
@@ -565,9 +567,15 @@ 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];
+ int expected_refcount = folio_nr_pages(folio) +
+ filemap_get_folios_refcount;

- if (folio_ref_count(folio) !=
- folio_nr_pages(folio) + filemap_get_folios_refcount) {
+ while (folio_may_be_lru_cached(folio) &&
+ folio_ref_count(folio) != expected_refcount &&
+ lru_add_drain_progressive(&drain_state))
+ ;
+
+ if (folio_ref_count(folio) != expected_refcount) {
safe = false;
*err_index = max(start, folio->index);
break;

--
2.55.0.508.g3f0d502094-goog