KVM: MIPS: Invalidate TLB by regenerating ASIDs
authorJames Hogan <james.hogan@imgtec.com>
Thu, 15 Sep 2016 23:06:43 +0000 (00:06 +0100)
committerJames Hogan <james.hogan@imgtec.com>
Thu, 29 Sep 2016 11:40:12 +0000 (12:40 +0100)
Invalidate host TLB mappings when the guest ASID is changed by
regenerating ASIDs, rather than flushing the entire host TLB except
entries in the guest KSeg0 range.

For the guest kernel mode ASID we regenerate on the spot when the guest
ASID is changed, as that will always take place while the guest is in
kernel mode.

However when the guest invalidates TLB entries the ASID will often by
changed temporarily as part of writing EntryHi without the guest
returning to user mode in between. We therefore regenerate the user mode
ASID lazily before entering the guest in user mode, if and only if the
guest ASID has actually changed since the last guest user mode entry.

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/include/asm/kvm_host.h
arch/mips/kvm/emulate.c
arch/mips/kvm/mips.c
arch/mips/kvm/mmu.c

index 4d7e0e466b5a02ddcc3c1d4f2a2938234dcb762b..a5685c1adba2d846bcc64bc6d711cb29df142e03 100644 (file)
@@ -328,6 +328,9 @@ struct kvm_vcpu_arch {
        u32 guest_kernel_asid[NR_CPUS];
        struct mm_struct guest_kernel_mm, guest_user_mm;
 
+       /* Guest ASID of last user mode execution */
+       unsigned int last_user_gasid;
+
        int last_sched_cpu;
 
        /* WAIT executed */
index 43853ec6e160262d36c64b31b2383848f794db0f..8dc9e64346e61d6a28d9dff91d4fc5351e4de35c 100644 (file)
@@ -1170,15 +1170,23 @@ enum emulation_result kvm_mips_emulate_CP0(union mips_instruction inst,
                                                        & KVM_ENTRYHI_ASID,
                                                nasid);
 
+                                       /*
+                                        * Regenerate/invalidate kernel MMU
+                                        * context.
+                                        * The user MMU context will be
+                                        * regenerated lazily on re-entry to
+                                        * guest user if the guest ASID actually
+                                        * changes.
+                                        */
                                        preempt_disable();
-                                       /* Blow away the shadow host TLBs */
-                                       kvm_mips_flush_host_tlb(1);
                                        cpu = smp_processor_id();
+                                       kvm_get_new_mmu_context(&vcpu->arch.guest_kernel_mm,
+                                                               cpu, vcpu);
+                                       vcpu->arch.guest_kernel_asid[cpu] =
+                                               vcpu->arch.guest_kernel_mm.context.asid[cpu];
                                        for_each_possible_cpu(i)
-                                               if (i != cpu) {
-                                                       vcpu->arch.guest_user_asid[i] = 0;
+                                               if (i != cpu)
                                                        vcpu->arch.guest_kernel_asid[i] = 0;
-                                               }
                                        preempt_enable();
                                }
                                kvm_write_c0_guest_entryhi(cop0,
index a6ea084b4d9d5fe500d219cc3d03901f143b4388..ad1b15ba590705dd8f9fed822b5366f273a4157a 100644 (file)
@@ -411,6 +411,31 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
        return -ENOIOCTLCMD;
 }
 
+/* Must be called with preemption disabled, just before entering guest */
+static void kvm_mips_check_asids(struct kvm_vcpu *vcpu)
+{
+       struct mips_coproc *cop0 = vcpu->arch.cop0;
+       int cpu = smp_processor_id();
+       unsigned int gasid;
+
+       /*
+        * Lazy host ASID regeneration for guest user mode.
+        * If the guest ASID has changed since the last guest usermode
+        * execution, regenerate the host ASID so as to invalidate stale TLB
+        * entries.
+        */
+       if (!KVM_GUEST_KERNEL_MODE(vcpu)) {
+               gasid = kvm_read_c0_guest_entryhi(cop0) & KVM_ENTRYHI_ASID;
+               if (gasid != vcpu->arch.last_user_gasid) {
+                       kvm_get_new_mmu_context(&vcpu->arch.guest_user_mm, cpu,
+                                               vcpu);
+                       vcpu->arch.guest_user_asid[cpu] =
+                               vcpu->arch.guest_user_mm.context.asid[cpu];
+                       vcpu->arch.last_user_gasid = gasid;
+               }
+       }
+}
+
 int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
 {
        int r = 0;
@@ -438,6 +463,9 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
        htw_stop();
 
        trace_kvm_enter(vcpu);
+
+       kvm_mips_check_asids(vcpu);
+
        r = vcpu->arch.vcpu_run(run, vcpu);
        trace_kvm_out(vcpu);
 
@@ -1551,6 +1579,8 @@ skip_emul:
        if (ret == RESUME_GUEST) {
                trace_kvm_reenter(vcpu);
 
+               kvm_mips_check_asids(vcpu);
+
                /*
                 * If FPU / MSA are enabled (i.e. the guest's FPU / MSA context
                 * is live), restore FCR31 / MSACSR.
index c1f8758f5323c13e0c468d1d627ac1833df0e4f2..8e1f2bffcf0fb778d4e448f65baf6d7cbe1024bc 100644 (file)
@@ -260,9 +260,13 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 
        if ((vcpu->arch.guest_user_asid[cpu] ^ asid_cache(cpu)) &
                                                asid_version_mask(cpu)) {
+               u32 gasid = kvm_read_c0_guest_entryhi(vcpu->arch.cop0) &
+                               KVM_ENTRYHI_ASID;
+
                kvm_get_new_mmu_context(&vcpu->arch.guest_user_mm, cpu, vcpu);
                vcpu->arch.guest_user_asid[cpu] =
                    vcpu->arch.guest_user_mm.context.asid[cpu];
+               vcpu->arch.last_user_gasid = gasid;
                newasid++;
 
                kvm_debug("[%d]: cpu_context: %#lx\n", cpu,