[RFC PATCH 11/14] KVM: guest_memfd: Add kvm_gmem_restore() for PFN mode providers
From: Fred Griffoul
Date: Tue Oct 27 2026 - 12:21:28 EST
From: Fred Griffoul <fgriffo@xxxxxxxxxxxx>
A device cannot fault, so when a PFN mode provider gives frames to pages
that had none, or makes a read-only frame writable, a subsystem that
maps the file must be told. kvm_gmem_revoke() does not fit: it would
zap the guest's and the VMM's mappings and reclaim frames that a guest
may already use.
Add kvm_gmem_restore(), the counterpart to kvm_gmem_revoke() that the
sample provider leaves for users that cannot fault. It only updates the
exports, under the shared invalidate lock.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Fred Griffoul <fgriffo@xxxxxxxxxxxx>
---
arch/x86/kvm/Makefile | 1 +
include/linux/guest_memfd.h | 37 ++++++++++++++++++++++---------------
virt/kvm/guest_memfd.c | 35 +++++++++++++++++++++++++++++++++--
3 files changed, 56 insertions(+), 17 deletions(-)
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index 25fb3d6e2032..3255280884b5 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -63,6 +63,7 @@ exports_grep_trailer := --include='*.[ch]' -nrw $(srctree)/virt/kvm $(srctree)/a
-e kvm_write_track_remove_gfn \
-e kvm_file_to_kvm_fn \
-e kvm_gmem_revoke \
+ -e kvm_gmem_restore \
-e kvm_gmem_export
# Force grep to emit a goofy group separator that can in turn be replaced with
diff --git a/include/linux/guest_memfd.h b/include/linux/guest_memfd.h
index f8e89723dd68..45572d929bd7 100644
--- a/include/linux/guest_memfd.h
+++ b/include/linux/guest_memfd.h
@@ -75,12 +75,18 @@ struct guest_memfd_provider_operations {
#ifdef CONFIG_KVM_GUEST_MEMFD
int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_pages,
const unsigned long *pfn);
+void kvm_gmem_restore(struct inode *inode, pgoff_t start, size_t nr_pages);
#else
static inline int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_pages,
const unsigned long *pfn)
{
return -EOPNOTSUPP;
}
+
+static inline void kvm_gmem_restore(struct inode *inode, pgoff_t start,
+ size_t nr_pages)
+{
+}
#endif
/*
@@ -90,14 +96,15 @@ static inline int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_
* nor fault them. It attaches to the file, looks up the frame behind each
* shared page without taking a reference, and guest_memfd calls its update()
* whenever what a lookup returns for a range may have changed: conversions,
- * punch holes, memory failure, a provider's kvm_gmem_revoke(), fallocate() and
- * pages getting their first frame. update() makes the consumer's mappings of
- * the range match what a lookup returns now: a frame, or a hole for a private
- * page or a page without a frame. It may be called for pages that did not
- * change, and for ranges that do not follow the blocks that lookups report:
- * the consumer may then remap whole blocks, also outside the range, e.g. to
- * merge them into larger mappings. A consumer that can fault may instead only
- * remove its mappings of the range.
+ * punch holes, memory failure, a provider's kvm_gmem_revoke() or
+ * kvm_gmem_restore(), fallocate() and pages getting their first frame.
+ * update() makes the consumer's mappings of the range match what a lookup
+ * returns now: a frame, or a hole for a private page or a page without a
+ * frame. It may be called for pages that did not change, and for ranges that
+ * do not follow the blocks that lookups report: the consumer may then remap
+ * whole blocks, also outside the range, e.g. to merge them into larger
+ * mappings. A consumer that can fault may instead only remove its mappings of
+ * the range.
*
* Until update() returns, the frames it replaces stay valid, so a consumer
* may switch each mapping to its new target in one step; once it returns,
@@ -106,14 +113,14 @@ static inline int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_
* beforehand.
*
* guest_memfd calls update() with its invalidate lock held, exclusive when it
- * takes frames away or pages convert, and shared when pages only gain frames
- * and on memory failure, possibly with a folio of the file locked; so update()
- * may only call kvm_gmem_export_lookup_pfn(). Under the shared lock, updates
- * may run at the same time, also for overlapping ranges. The consumer holds a
- * lock across a lookup and the mapping it installs, which update() also takes.
- * Lock order:
+ * takes frames or write access away or pages convert, and shared when pages
+ * only gain frames or write access and on memory failure, possibly with a
+ * folio of the file locked; so update() may only call
+ * kvm_gmem_export_lookup_pfn(). Under the shared lock, updates may run at the
+ * same time, also for overlapping ranges. The consumer holds a lock across a
+ * lookup and the mapping it installs, which update() also takes. Lock order:
*
- * PFN mode provider lock (across kvm_gmem_revoke())
+ * PFN mode provider lock (across kvm_gmem_revoke() and kvm_gmem_restore())
* guest_memfd invalidate lock
* consumer lock
* provider leaf lock (get_pfn(), lookup_pfn_range())
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 42bb5f263e23..37ff2fae89ae 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -431,8 +431,8 @@ static void kvm_gmem_invalidate_end(struct inode *inode, pgoff_t start,
/*
* What a lookup returns for [start, end) may have changed: exclusive lock when
- * frames were taken away or pages converted, at least shared when pages only
- * gained frames and on memory failure.
+ * frames or write access were taken away or pages converted, at least shared
+ * when pages only gained frames or write access and on memory failure.
*/
static void kvm_gmem_update_exports(struct inode *inode, pgoff_t start,
pgoff_t end)
@@ -592,6 +592,37 @@ int kvm_gmem_revoke(struct inode *inode, pgoff_t start, size_t nr_pages,
}
EXPORT_SYMBOL_GPL(kvm_gmem_revoke);
+/**
+ * kvm_gmem_restore - Tell guest_memfd that memory of a range is now available.
+ * @inode: The guest_memfd inode.
+ * @start: Starting page index within the file.
+ * @nr_pages: Number of pages.
+ *
+ * A PFN mode provider calls this when it gives frames to pages that had none,
+ * unless get_pfn() does so for the block it reports, and when it makes a
+ * read-only frame writable. Nothing is taken away, so the guest and the VMM
+ * need nothing: they fault the pages in, or get write access on their next
+ * write. The subsystems that map the file cannot, and are told to map the
+ * range again. A change that takes something away, a frame or write access,
+ * needs kvm_gmem_revoke() instead, which updates them too. Does nothing past
+ * the end of the file.
+ *
+ * Context: May sleep. Must not be called with the provider's leaf lock held.
+ */
+void kvm_gmem_restore(struct inode *inode, pgoff_t start, size_t nr_pages)
+{
+ pgoff_t max_pages = i_size_read(inode) >> PAGE_SHIFT;
+
+ if (start >= max_pages)
+ return;
+ nr_pages = min_t(size_t, nr_pages, max_pages - start);
+
+ filemap_invalidate_lock_shared(inode->i_mapping);
+ kvm_gmem_update_exports(inode, start, start + nr_pages);
+ filemap_invalidate_unlock_shared(inode->i_mapping);
+}
+EXPORT_SYMBOL_GPL(kvm_gmem_restore);
+
/*
* Zero a folio on its first use. Lookups find it from then on, with no
* reference held, so mark it dirty: memory-failure handling drops clean
--
2.47.3