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]>
---
 include/linux/kvm_host.h |  9 +++++++
 virt/kvm/guest_memfd.c   | 52 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)

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 057f90eb835a..1da22da0a6ec 100644
--- a/virt/kvm/guest_memfd.c
+++ b/virt/kvm/guest_memfd.c
@@ -738,6 +738,58 @@ 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;
+       struct kvm_memslot_iter iter;
+       bool flush = false;
+       int as_id;
+       int idx;
+
+       idx = srcu_read_lock(&kvm->srcu);
+
+       /*
+        * Walk every address space (SMM has its own on x86) and every
+        * intersecting memslot.  A single invalidation may span several
+        * slots and holes: skip the holes, clamp to each slot's range.
+        */
+       KVM_MMU_LOCK(kvm);
+       kvm_mmu_invalidate_start(kvm);
+
+       for (as_id = 0; as_id < kvm_arch_nr_memslot_as_ids(kvm); as_id++) {
+               struct kvm_memslots *slots = __kvm_memslots(kvm, as_id);
+
+               kvm_for_each_memslot_in_gfn_range(&iter, slots, start, end) {
+                       struct kvm_gfn_range gfn_range;
+
+                       slot = iter.slot;
+                       gfn_range.slot          = slot;
+                       gfn_range.start         = max(start, slot->base_gfn);
+                       gfn_range.end           = min(end, slot->base_gfn + 
slot->npages);
+                       gfn_range.may_block     = true;
+                       gfn_range.attr_filter   = KVM_FILTER_SHARED | 
KVM_FILTER_PRIVATE;
+
+                       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.55.0


Reply via email to