From: David Woodhouse <[email protected]>

Add kvm_gmem_invalidate_range(): a guest_memfd backing calls it to zap
the guest secondary-MMU mapping for a gfn range so the next access
re-faults through ops->get_pfn().  Mirrors the existing internal
__kvm_gmem_invalidate_start() for a single bound slot, bracketing the
unmap with the mmu_invalidate window so a racing vCPU fault retries
rather than installing a stale mapping.

The existing ops->invalidate callback is KVM -> implementation
(cleanup); this new symbol is the implementation -> KVM path, useful
for an overcommit manager or any subsystem that reclaims pages from a
running guest.  Export it for module use and add it to the KVM
external-export allowlist.

Signed-off-by: David Woodhouse (Kiro) <[email protected]>
---
 arch/x86/kvm/Makefile    |  3 ++-
 include/linux/kvm_host.h |  9 +++++++++
 virt/kvm/guest_memfd.c   | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index 77337c37324b..cb963d6ecaab 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -63,7 +63,8 @@ exports_grep_trailer := --include='*.[ch]' -nrw 
$(srctree)/virt/kvm $(srctree)/a
                                -e kvm_write_track_remove_gfn \
                                -e kvm_get_kvm \
                                -e kvm_get_kvm_safe \
-                               -e kvm_put_kvm
+                               -e kvm_put_kvm \
+                               -e kvm_gmem_invalidate_range
 
 # Force grep to emit a goofy group separator that can in turn be replaced with
 # the above newline macro (newlines in Make are a nightmare).  Note, grep only
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index b33d2f475042..04fa0cb126f6 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -701,6 +701,15 @@ static inline bool is_kvm_gmem_file(struct file *file)
        return file && file->f_op == &kvm_gmem_fops;
 }
 
+/*
+ * Outbound (implementation -> KVM) revocation: zap the guest's secondary MMU
+ * (NPT/EPT) mapping for a gfn range so the next guest access re-faults
+ * through the ops' get_pfn().  This is the reverse of ->invalidate (which is
+ * KVM -> implementation) and is what a backing that manages its own memory
+ * (e.g. an overcommit manager) uses to reclaim a page from a running guest.
+ */
+void kvm_gmem_invalidate_range(struct kvm *kvm, gfn_t start, gfn_t end);
+
 #endif /* CONFIG_KVM_GUEST_MEMFD */
 
 static inline bool kvm_slot_dirty_track_enabled(const struct kvm_memory_slot 
*slot)
diff --git a/virt/kvm/guest_memfd.c b/virt/kvm/guest_memfd.c
index 2b277468a12f..6c13742de068 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -738,6 +738,45 @@ int kvm_gmem_create(struct kvm *kvm, struct 
kvm_create_guest_memfd *args)
        return __kvm_gmem_create(kvm, size, flags);
 }
 
+/*
+ * Outbound (implementation -> KVM) revocation: an implementation zaps the
+ * guest's secondary MMU mapping for a gfn range so the next guest access
+ * re-faults through ops->get_pfn().  Mirrors __kvm_gmem_invalidate_start()
+ * for a single bound slot, bracketing the unmap with the mmu_invalidate
+ * window so a racing vCPU fault retries rather than installing a stale
+ * mapping.
+ */
+void kvm_gmem_invalidate_range(struct kvm *kvm, gfn_t start, gfn_t end)
+{
+       struct kvm_memory_slot *slot;
+       bool flush = false;
+       int idx;
+
+       idx = srcu_read_lock(&kvm->srcu);
+
+       slot = gfn_to_memslot(kvm, start);
+       if (slot) {
+               struct kvm_gfn_range gfn_range = {
+                       .slot           = slot,
+                       .start          = start,
+                       .end            = min(end, slot->base_gfn + 
slot->npages),
+                       .may_block      = true,
+                       .attr_filter    = KVM_FILTER_SHARED | 
KVM_FILTER_PRIVATE,
+               };
+
+               KVM_MMU_LOCK(kvm);
+               kvm_mmu_invalidate_start(kvm);
+               flush = kvm_mmu_unmap_gfn_range(kvm, &gfn_range);
+               if (flush)
+                       kvm_flush_remote_tlbs(kvm);
+               kvm_mmu_invalidate_end(kvm);
+               KVM_MMU_UNLOCK(kvm);
+       }
+
+       srcu_read_unlock(&kvm->srcu, idx);
+}
+EXPORT_SYMBOL_GPL(kvm_gmem_invalidate_range);
+
 static const struct kvm_gmem_ops *kvm_gmem_get_ops(struct file *file)
 {
        if (!is_kvm_gmem_file(file))
-- 
2.54.0

Reply via email to