KVM: MMU: fix MTRR update
authorXiao Guangrong <guangrong.xiao@linux.intel.com>
Wed, 13 May 2015 06:42:27 +0000 (14:42 +0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 19 May 2015 18:52:42 +0000 (20:52 +0200)
Currently, whenever guest MTRR registers are changed
kvm_mmu_reset_context is called to switch to the new root shadow page
table, however, it's useless since:
1) the cache type is not cached into shadow page's attribute so that
   the original root shadow page will be reused

2) the cache type is set on the last spte, that means we should sync
   the last sptes when MTRR is changed

This patch fixs this issue by drop all the spte in the gfn range which
is being updated by MTRR

Signed-off-by: Xiao Guangrong <guangrong.xiao@linux.intel.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/mmu.c
arch/x86/kvm/mmu.h
arch/x86/kvm/x86.c

index e718c76609f96facce84d985266f6c466c5e2fb9..5bebec1191f16f43f604e3320f1c282b41985cfa 100644 (file)
@@ -4525,6 +4525,30 @@ slot_handle_leaf(struct kvm *kvm, struct kvm_memory_slot *memslot,
                                 PT_PAGE_TABLE_LEVEL, lock_flush_tlb);
 }
 
+void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end)
+{
+       struct kvm_memslots *slots;
+       struct kvm_memory_slot *memslot;
+
+       slots = kvm_memslots(kvm);
+
+       spin_lock(&kvm->mmu_lock);
+       kvm_for_each_memslot(memslot, slots) {
+               gfn_t start, end;
+
+               start = max(gfn_start, memslot->base_gfn);
+               end = min(gfn_end, memslot->base_gfn + memslot->npages);
+               if (start >= end)
+                       continue;
+
+               slot_handle_level_range(kvm, memslot, kvm_zap_rmapp,
+                               PT_PAGE_TABLE_LEVEL, PT_MAX_HUGEPAGE_LEVEL,
+                               start, end - 1, true);
+       }
+
+       spin_unlock(&kvm->mmu_lock);
+}
+
 static bool slot_rmap_write_protect(struct kvm *kvm, unsigned long *rmapp)
 {
        return __rmap_write_protect(kvm, rmapp, false);
index 72bb33f70c16342c75cf033cc80e92e86b1f35f7..398d21c0f6dd05273fea48598bef016e1c26b51c 100644 (file)
@@ -171,4 +171,5 @@ static inline bool permission_fault(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu,
 }
 
 void kvm_mmu_invalidate_zap_all_pages(struct kvm *kvm);
+void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end);
 #endif
index cde5d614ff0a3b4503321aa9c88a249e1fa429bb..bbe184f07bf9daca7f199886d634e37f11e62283 100644 (file)
@@ -1852,6 +1852,63 @@ bool kvm_mtrr_valid(struct kvm_vcpu *vcpu, u32 msr, u64 data)
 }
 EXPORT_SYMBOL_GPL(kvm_mtrr_valid);
 
+static void update_mtrr(struct kvm_vcpu *vcpu, u32 msr)
+{
+       struct mtrr_state_type *mtrr_state = &vcpu->arch.mtrr_state;
+       unsigned char mtrr_enabled = mtrr_state->enabled;
+       gfn_t start, end, mask;
+       int index;
+       bool is_fixed = true;
+
+       if (msr == MSR_IA32_CR_PAT || !tdp_enabled ||
+             !kvm_arch_has_noncoherent_dma(vcpu->kvm))
+               return;
+
+       if (!(mtrr_enabled & 0x2) && msr != MSR_MTRRdefType)
+               return;
+
+       switch (msr) {
+       case MSR_MTRRfix64K_00000:
+               start = 0x0;
+               end = 0x80000;
+               break;
+       case MSR_MTRRfix16K_80000:
+               start = 0x80000;
+               end = 0xa0000;
+               break;
+       case MSR_MTRRfix16K_A0000:
+               start = 0xa0000;
+               end = 0xc0000;
+               break;
+       case MSR_MTRRfix4K_C0000 ... MSR_MTRRfix4K_F8000:
+               index = msr - MSR_MTRRfix4K_C0000;
+               start = 0xc0000 + index * (32 << 10);
+               end = start + (32 << 10);
+               break;
+       case MSR_MTRRdefType:
+               is_fixed = false;
+               start = 0x0;
+               end = ~0ULL;
+               break;
+       default:
+               /* variable range MTRRs. */
+               is_fixed = false;
+               index = (msr - 0x200) / 2;
+               start = (((u64)mtrr_state->var_ranges[index].base_hi) << 32) +
+                      (mtrr_state->var_ranges[index].base_lo & PAGE_MASK);
+               mask = (((u64)mtrr_state->var_ranges[index].mask_hi) << 32) +
+                      (mtrr_state->var_ranges[index].mask_lo & PAGE_MASK);
+               mask |= ~0ULL << cpuid_maxphyaddr(vcpu);
+
+               end = ((start & mask) | ~mask) + 1;
+       }
+
+       if (is_fixed && !(mtrr_enabled & 0x1))
+               return;
+
+       kvm_zap_gfn_range(vcpu->kvm, gpa_to_gfn(start), gpa_to_gfn(end));
+}
+
 static int set_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
 {
        u64 *p = (u64 *)&vcpu->arch.mtrr_state.fixed_ranges;
@@ -1885,7 +1942,7 @@ static int set_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
                *pt = data;
        }
 
-       kvm_mmu_reset_context(vcpu);
+       update_mtrr(vcpu, msr);
        return 0;
 }