[RFC PATCH v1 07/10] KVM: guest_memfd: Implement custom truncation function

From: Ackerley Tng

Date: Mon Feb 23 2026 - 02:05:56 EST


Implement custom truncation function for guest_memfd, and replace calls to
truncate_inode_pages_range() with calls to this custom truncation function.

The custom truncation function removes a lot of the generality supported by
truncate_inode_pages_range() not required by guest_memfd, such as

+ sub-PAGE_SIZE truncations
+ Support for writeback

In a later patch, guest_memfd use this custom truncation function to handle
updating of i_blocks and i_bytes in the inode during truncation.

Signed-off-by: Ackerley Tng <ackerleytng@xxxxxxxxxx>
---
virt/kvm/guest_memfd.c | 43 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 41 insertions(+), 2 deletions(-)

diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 57dec458bfa77..e6c66ab7062b3 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -247,6 +247,45 @@ static void kvm_gmem_invalidate_end(struct inode *inode, pgoff_t start,
__kvm_gmem_invalidate_end(f, start, end);
}

+static void kvm_gmem_truncate_folio(struct folio *folio)
+{
+ folio_lock(folio);
+
+ if (folio_mapped(folio))
+ unmap_mapping_folio(folio);
+
+ /*
+ * guest_memfd doesn't need writeback, skip anything to do with
+ * writeback and just clear the dirty flag.
+ */
+ folio_clear_dirty(folio);
+ filemap_remove_folio(folio);
+
+ folio_unlock(folio);
+}
+
+static void kvm_gmem_truncate_range(struct inode *inode, pgoff_t start,
+ size_t nr_pages)
+
+{
+ struct folio_batch fbatch;
+ pgoff_t next;
+ pgoff_t last;
+ int i;
+
+ last = start + nr_pages - 1;
+
+ folio_batch_init(&fbatch);
+ next = start;
+ while (filemap_get_folios(inode->i_mapping, &next, last, &fbatch)) {
+ for (i = 0; i < folio_batch_count(&fbatch); ++i)
+ kvm_gmem_truncate_folio(fbatch.folios[i]);
+
+ folio_batch_release(&fbatch);
+ cond_resched();
+ }
+}
+
static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len)
{
pgoff_t start = offset >> PAGE_SHIFT;
@@ -260,7 +299,7 @@ static long kvm_gmem_punch_hole(struct inode *inode, loff_t offset, loff_t len)

kvm_gmem_invalidate_begin(inode, start, end);

- truncate_inode_pages_range(inode->i_mapping, offset, offset + len - 1);
+ kvm_gmem_truncate_range(inode, offset, len >> PAGE_SHIFT);

kvm_gmem_invalidate_end(inode, start, end);

@@ -984,7 +1023,7 @@ static void kvm_gmem_evict_inode(struct inode *inode)

truncate_inode_pages_final_prepare(mapping);

- truncate_inode_pages_range(mapping, 0, inode->i_size);
+ kvm_gmem_truncate_range(inode, 0, inode->i_size >> PAGE_SHIFT);

clear_inode(inode);
}
--
2.53.0.345.g96ddfc5eaa-goog