summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Hogan <james.hogan@imgtec.com>2016-11-29 02:13:38 +0300
committerJames Hogan <james.hogan@imgtec.com>2017-02-03 18:21:12 +0300
commit4b21e8abf959ca66c27f0656bf294fe69d3f2254 (patch)
treee14640e08e5ce3ed9c24e0018c73cca17d69f852
parent1880afd6057f34586919715e8ffe9c5858f4a326 (diff)
downloadlinux-4b21e8abf959ca66c27f0656bf294fe69d3f2254.tar.xz
KVM: MIPS/T&E: Use lockless GVA helpers for dyntrans
Use the lockless GVA helpers to implement the dynamic translation of guest instructions. This will allow it to handle asynchronous TLB flushes when they are implemented. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: "Radim Krčmář" <rkrcmar@redhat.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: linux-mips@linux-mips.org Cc: kvm@vger.kernel.org
-rw-r--r--arch/mips/kvm/dyntrans.c26
1 files changed, 24 insertions, 2 deletions
diff --git a/arch/mips/kvm/dyntrans.c b/arch/mips/kvm/dyntrans.c
index 60ebf5862d2b..f8e772564d74 100644
--- a/arch/mips/kvm/dyntrans.c
+++ b/arch/mips/kvm/dyntrans.c
@@ -33,10 +33,32 @@ static int kvm_mips_trans_replace(struct kvm_vcpu *vcpu, u32 *opc,
unsigned long vaddr = (unsigned long)opc;
int err;
+retry:
+ /* The GVA page table is still active so use the Linux TLB handlers */
+ kvm_trap_emul_gva_lockless_begin(vcpu);
err = put_user(replace.word, opc);
+ kvm_trap_emul_gva_lockless_end(vcpu);
+
if (unlikely(err)) {
- kvm_err("%s: Invalid address: %p\n", __func__, opc);
- return err;
+ /*
+ * We write protect clean pages in GVA page table so normal
+ * Linux TLB mod handler doesn't silently dirty the page.
+ * Its also possible we raced with a GVA invalidation.
+ * Try to force the page to become dirty.
+ */
+ err = kvm_trap_emul_gva_fault(vcpu, vaddr, true);
+ if (unlikely(err)) {
+ kvm_info("%s: Address unwriteable: %p\n",
+ __func__, opc);
+ return -EFAULT;
+ }
+
+ /*
+ * Try again. This will likely trigger a TLB refill, which will
+ * fetch the new dirty entry from the GVA page table, which
+ * should then succeed.
+ */
+ goto retry;
}
__local_flush_icache_user_range(vaddr, vaddr + 4);