[RFC v2 PATCH 11/13] KVM: Add kvm_map_gfn_range

From: Chao Peng
Date: Fri Nov 19 2021 - 08:50:09 EST


This may be used in the fallocate callback for memfd based memory
to setup the mapping for KVM second MMU when the pages are allocated
in the memory backing store.

Signed-off-by: Yu Zhang <yu.c.zhang@xxxxxxxxxxxxxxx>
Signed-off-by: Chao Peng <chao.p.peng@xxxxxxxxxxxxxxx>
---
arch/x86/kvm/mmu/mmu.c | 47 ++++++++++++++++++++++++++++++++++++++++
include/linux/kvm_host.h | 2 ++
virt/kvm/kvm_main.c | 5 +++++
3 files changed, 54 insertions(+)

diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index cd5d1f923694..5c475a161a3c 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -1951,6 +1951,53 @@ static __always_inline bool kvm_handle_gfn_range(struct kvm *kvm,
return ret;
}

+bool kvm_map_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ struct kvm_vcpu *vcpu;
+ kvm_pfn_t pfn;
+ gfn_t gfn;
+ int idx;
+ bool ret = true;
+
+ /* Need vcpu context for kvm_mmu_do_page_fault. */
+ vcpu = kvm_get_vcpu(kvm, 0);
+ if (mutex_lock_killable(&vcpu->mutex))
+ return false;
+
+ vcpu_load(vcpu);
+ idx = srcu_read_lock(&kvm->srcu);
+
+ kvm_mmu_reload(vcpu);
+
+ gfn = range->start;
+ while (gfn < range->end) {
+ if (signal_pending(current)) {
+ ret = false;
+ break;
+ }
+
+ if (need_resched())
+ cond_resched();
+
+ pfn = kvm_mmu_do_page_fault(vcpu, gfn << PAGE_SHIFT,
+ PFERR_WRITE_MASK | PFERR_USER_MASK,
+ false);
+ if (is_error_noslot_pfn(pfn) || kvm->vm_bugged) {
+ ret = false;
+ break;
+ }
+
+ gfn++;
+ }
+
+ srcu_read_unlock(&kvm->srcu, idx);
+ vcpu_put(vcpu);
+
+ mutex_unlock(&vcpu->mutex);
+
+ return ret;
+}
+
bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
{
bool flush = false;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index f0fd32f6eab3..d841ed877b4b 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -237,6 +237,8 @@ struct kvm_gfn_range {
pte_t pte;
bool may_block;
};
+
+bool kvm_map_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range);
bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range);
bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range);
bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range);
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 65055ac460eb..492c1a99ec63 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -471,6 +471,11 @@ static void kvm_mmu_notifier_invalidate_range(struct mmu_notifier *mn,
srcu_read_unlock(&kvm->srcu, idx);
}

+bool __weak kvm_map_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ return false;
+}
+
typedef bool (*gfn_handler_t)(struct kvm *kvm, struct kvm_gfn_range *range);

typedef void (*on_lock_fn_t)(struct kvm *kvm, unsigned long start,
--
2.17.1