KVM: MIPS/T&E: Use lockless GVA helpers for dyntrans
authorJames Hogan <james.hogan@imgtec.com>
Mon, 28 Nov 2016 23:13:38 +0000 (23:13 +0000)
committerJames Hogan <james.hogan@imgtec.com>
Fri, 3 Feb 2017 15:21:12 +0000 (15:21 +0000)
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
arch/mips/kvm/dyntrans.c

index 60ebf5862d2bb52168d689c690b62094d1e3ab72..f8e772564d74290d2c2043c36e2263db98c018ea 100644 (file)
@@ -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);