[PATCH RFC v3 37/88] KVM: guest_memfd: Support reclaim during hole punch in PFN mode
From: Ackerley Tng via B4 Relay
Date: Fri Nov 06 2026 - 19:49:03 EST
From: Ackerley Tng <ackerleytng@xxxxxxxxxx>
In PFN mode, guest_memfd does not allocate or insert folios into the page
cache (inode->i_mapping). Calling truncate_inode_pages_range() is therefore
unnecessary.
Instead, call unmap_mapping_range() when in PFN mode to unmap any host
userspace PTEs over the punched range, while retaining
kvm_gmem_invalidate_start() and kvm_gmem_invalidate_end() to zap stage-2
KVM SPTEs.
Reclaim any confidential architecture state (e.g. SEV-SNP RMP) on provider
PFNs across the punched range via kvm_gmem_pfn_reclaim(), querying
.lookup_pfn_range() to skip unallocated holes without allocating memory.
Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
---
virt/kvm/guest_memfd.c | 38 ++++++++++++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 9e8204f5c2588..567735a564c7a 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -149,7 +149,7 @@ static int gmem_provider_get_pfn(struct gmem_inode *gi, pgoff_t index,
return 0;
}
-#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT
+#if defined(CONFIG_HAVE_KVM_ARCH_GMEM_CONVERT) || defined(CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM)
static int gmem_provider_lookup_pfn_range(struct gmem_inode *gi, pgoff_t index,
kvm_pfn_t *pfn, size_t *nr_pages)
{
@@ -385,6 +385,35 @@ static void kvm_gmem_invalidate_end(struct inode *inode, pgoff_t start,
__kvm_gmem_invalidate_end(f, start, end);
}
+#ifdef CONFIG_HAVE_KVM_ARCH_GMEM_RECLAIM
+static void kvm_gmem_pfn_reclaim(struct gmem_inode *gi, pgoff_t start, pgoff_t end)
+{
+ pgoff_t index;
+
+ for (index = start; index < end; ) {
+ size_t step = 1;
+ size_t nr_pages;
+ kvm_pfn_t pfn;
+ int r;
+
+ r = gmem_provider_lookup_pfn_range(gi, index, &pfn, &nr_pages);
+ if (!r || r == -ENOENT) {
+ step = min_t(size_t, nr_pages, end - index);
+ if (!r)
+ kvm_arch_gmem_reclaim(pfn, step);
+ } else {
+ WARN_ONCE(1, "unexpected lookup_pfn_range error %d at index 0x%lx\n",
+ r, index);
+ }
+
+ index += step;
+ cond_resched();
+ }
+}
+#else
+static void kvm_gmem_pfn_reclaim(struct gmem_inode *gi, pgoff_t start, pgoff_t end) {}
+#endif
+
static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len)
{
enum kvm_gfn_range_filter filter = kvm_gmem_get_all_gfns_filter(inode);
@@ -406,7 +435,12 @@ static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len)
kvm_gmem_invalidate_start(inode, start, end, filter);
- truncate_inode_pages_range(inode->i_mapping, offset, offset + len - 1);
+ if (kvm_gmem_is_pfn_mode(GMEM_I(inode))) {
+ unmap_mapping_range(inode->i_mapping, offset, len, 0);
+ kvm_gmem_pfn_reclaim(GMEM_I(inode), start, end);
+ } else {
+ truncate_inode_pages_range(inode->i_mapping, offset, offset + len - 1);
+ }
kvm_gmem_invalidate_end(inode, start, end);
--
2.56.0.455.g6c5a503b38-goog