[RFC PATCH 5/6] KVM: ARM: implement kvm_*_incoherent_memory_regions

Andrew Jones
2015-03-06T18:46:39Z

Add the kvm_*_incoherent_memory_regions calls to arm's
kvm_arch_vcpu_ioctl_run and implement the corresponding
arch flush/invalidate functions.

Signed-off-by: Andrew Jones <drjones@redhat.com>
---
arch/arm/include/uapi/asm/kvm.h | 1 +
arch/arm/kvm/arm.c | 4 +++
arch/arm/kvm/mmu.c | 54 +++++++++++++++++++++++++++++++++++++++
arch/arm64/include/uapi/asm/kvm.h | 1 +
4 files changed, 60 insertions(+)

diff --git a/arch/arm/include/uapi/asm/kvm.h b/arch/arm/include/uapi/asm/kvm.h
index 09ee408c1a676..cb0898a995c4f 100644
--- a/arch/arm/include/uapi/asm/kvm.h
+++ b/arch/arm/include/uapi/asm/kvm.h
@@ -26,6 +26,7 @@
#define __KVM_HAVE_GUEST_DEBUG
#define __KVM_HAVE_IRQ_LINE
#define __KVM_HAVE_READONLY_MEM
+#define __KVM_HAVE_INCOHERENT_MEM

#define KVM_REG_SIZE(id) \
(1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index c9e6ef1f7403a..789c03c84e7c0 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -486,6 +486,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
return ret;
}

+ kvm_flush_incoherent_memory_regions(vcpu->kvm);
+
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved);

@@ -556,6 +558,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
ret = handle_exit(vcpu, run, ret);
}

+ kvm_invalidate_incoherent_memory_regions(vcpu->kvm);
+
if (vcpu->sigset_active)
sigprocmask(SIG_SETMASK, &sigsaved, NULL);
return ret;
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 2f3a6581b9200..2f45db9cd436a 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -1161,6 +1161,24 @@ static void coherent_cache_guest_page(struct kvm_vcpu *vcpu, pfn_t pfn,
__coherent_cache_guest_page(pfn, size, need_flush, false);
}

+static void coherent_cache_memslot(struct kvm_memory_slot *slot, bool flush)
+{
+ gfn_t gfn, end = slot->base_gfn + slot->npages;
+ pfn_t pfn;
+
+ for (gfn = slot->base_gfn; gfn < end; ++gfn) {
+ pfn = gfn_to_pfn_memslot(slot, gfn);
+ if (is_error_pfn(pfn)) {
+ pr_err("%s: Bad pfn: gfn=%llx, pfn=%llx, "
+ "userspace_addr=%lx\n", __func__,
+ gfn, pfn, slot->userspace_addr);
+ continue;
+ }
+ __coherent_cache_guest_page(pfn, PAGE_SIZE, flush, !flush);
+ kvm_release_pfn_clean(pfn);
+ }
+}
+
static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
struct kvm_memory_slot *memslot, unsigned long hva,
unsigned long fault_status)
@@ -1802,6 +1820,42 @@ int kvm_arch_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot,
return 0;
}

+void kvm_arch_flush_incoherent(struct kvm *kvm, struct kvm_memory_slot *slot)
+{
+ if (slot->flags & KVM_MEM_READONLY) {
+ /*
+ * Readonly memory shouldn't be changing, and we do a
+ * clean+invalidate for KVM_MEM_INCOHERENT memory when
+ * faulting it in. So, there's nothing to do now.
+ */
+ return;
+ }
+
+ /*
+ * Ideally, we would further filter out all pages not touched by
+ * userspace on the last exit. No way to know those though, unless
+ * we force userspace to fault on all pages in the incoherent
+ * memory regions, but even then, I don't see any sane way for
+ * do_wp_page to handle the faults without modification. So, sigh...
+ */
+
+ coherent_cache_memslot(slot, true);
+}
+
+void kvm_arch_invalidate_incoherent(struct kvm *kvm, struct kvm_memory_slot *slot)
+{
+ if (slot->flags & KVM_MEM_LOG_DIRTY_PAGES) {
+ /*
+ * We fault each write when logging is enabled, and do a
+ * clean+invalidate on KVM_MEM_INCOHERENT memory while
+ * handling the fault. So, there's nothing to do now.
+ */
+ return;
+ }
+
+ coherent_cache_memslot(slot, false);
+}
+
void kvm_arch_memslots_updated(struct kvm *kvm)
{
}
diff --git a/arch/arm64/include/uapi/asm/kvm.h b/arch/arm64/include/uapi/asm/kvm.h
index 8e38878c87c61..29ddf77958c2a 100644
--- a/arch/arm64/include/uapi/asm/kvm.h
+++ b/arch/arm64/include/uapi/asm/kvm.h
@@ -38,6 +38,7 @@
#define __KVM_HAVE_GUEST_DEBUG
#define __KVM_HAVE_IRQ_LINE
#define __KVM_HAVE_READONLY_MEM
+#define __KVM_HAVE_INCOHERENT_MEM

#define KVM_REG_SIZE(id) \
(1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
--
1.8.3.1