Now we have everything in place to stop doing MMU reload when we switch from L1 to L2 and back. Generalize shadow_ept_mmu_update_needed() making it suitable for kvm_mmu_reset_context(). Signed-off-by: Vitaly Kuznetsov <vkuznets@xxxxxxxxxx> --- arch/x86/include/asm/kvm_host.h | 10 +++++- arch/x86/kvm/cpuid.c | 2 +- arch/x86/kvm/mmu.c | 74 +++++++++++++++++++++++++++++++---------- arch/x86/kvm/svm.c | 6 ++-- arch/x86/kvm/vmx.c | 7 ++-- arch/x86/kvm/x86.c | 14 ++++---- 6 files changed, 81 insertions(+), 32 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index fa73cf13c4d0..63ad28c40c1d 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -327,15 +327,22 @@ struct rsvd_bits_validate { /* Source data used to setup MMU */ struct kvm_mmu_sdata_cache { + unsigned long cr3; + unsigned int valid:1; + unsigned int smm:1; unsigned int ept_ad:1; unsigned int execonly:1; + unsigned int cr0_pg:1; unsigned int cr0_wp:1; unsigned int cr4_pae:1; unsigned int cr4_pse:1; unsigned int cr4_pke:1; unsigned int cr4_smap:1; unsigned int cr4_smep:1; + unsigned int cr4_la57:1; + unsigned int efer_lma:1; + unsigned int efer_nx:1; }; /* @@ -1149,7 +1156,8 @@ void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask, u64 dirty_mask, u64 nx_mask, u64 x_mask, u64 p_mask, u64 acc_track_mask, u64 me_mask); -void kvm_mmu_reset_context(struct kvm_vcpu *vcpu); +void kvm_mmu_reset_context(struct kvm_vcpu *vcpu, bool check_if_unchanged); + void kvm_mmu_slot_remove_write_access(struct kvm *kvm, struct kvm_memory_slot *memslot); void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 7e042e3d47fd..b0efd08075d8 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -142,7 +142,7 @@ int kvm_update_cpuid(struct kvm_vcpu *vcpu) /* Update physical-address width */ vcpu->arch.maxphyaddr = cpuid_query_maxphyaddr(vcpu); - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); kvm_pmu_refresh(vcpu); return 0; diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index eed1773453cd..9c08ee2e517a 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -4470,10 +4470,8 @@ static void paging32E_init_context(struct kvm_vcpu *vcpu, paging64_init_context_common(vcpu, context, PT32E_ROOT_LEVEL); } -static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu) +static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context) { - struct kvm_mmu *context = vcpu->arch.mmu; - context->base_role.word = 0; context->base_role.guest_mode = is_guest_mode(vcpu); context->base_role.smm = is_smm(vcpu); @@ -4548,21 +4546,30 @@ void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu); -static inline bool shadow_ept_mmu_update_needed(struct kvm_vcpu *vcpu, - bool execonly, bool accessed_dirty) +static inline bool mmu_update_needed(struct kvm_vcpu *vcpu, + struct kvm_mmu *context, + bool execonly, bool accessed_dirty) { - struct kvm_mmu *context = vcpu->arch.mmu; bool cr4_smep = kvm_read_cr4_bits(vcpu, X86_CR4_SMEP) != 0; bool cr4_smap = kvm_read_cr4_bits(vcpu, X86_CR4_SMAP) != 0; bool cr4_pke = kvm_read_cr4_bits(vcpu, X86_CR4_PKE) != 0; - bool cr0_wp = is_write_protection(vcpu); + bool cr4_la57 = kvm_read_cr4_bits(vcpu, X86_CR4_LA57) != 0; bool cr4_pse = is_pse(vcpu); + bool cr0_wp = is_write_protection(vcpu); + bool cr0_pg = is_paging(vcpu); + bool efer_nx = is_nx(vcpu); + bool efer_lma = is_long_mode(vcpu); + bool smm = is_smm(vcpu); bool res = false; if (!context->scache.valid) { res = true; context->scache.valid = 1; } + if (context->scache.smm != smm) { + context->scache.smm = smm; + res = true; + } if (context->scache.ept_ad != accessed_dirty) { context->scache.ept_ad = accessed_dirty; res = true; @@ -4587,10 +4594,26 @@ static inline bool shadow_ept_mmu_update_needed(struct kvm_vcpu *vcpu, res = true; context->scache.cr4_pke = cr4_pke; } + if (context->scache.cr4_la57 != cr4_la57) { + res = true; + context->scache.cr4_la57 = cr4_la57; + } if (context->scache.cr0_wp != cr0_wp) { res = true; context->scache.cr0_wp = cr0_wp; } + if (context->scache.cr0_pg != cr0_pg) { + res = true; + context->scache.cr0_pg = cr0_pg; + } + if (context->scache.efer_nx != efer_nx) { + res = true; + context->scache.efer_nx = efer_nx; + } + if (context->scache.efer_lma != efer_lma) { + res = true; + context->scache.efer_lma = efer_lma; + } return res; } @@ -4600,7 +4623,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, { struct kvm_mmu *context = vcpu->arch.mmu; - if (!shadow_ept_mmu_update_needed(vcpu, execonly, accessed_dirty)) + if (!mmu_update_needed(vcpu, context, execonly, accessed_dirty)) return; context->shadow_root_level = PT64_ROOT_4LEVEL; @@ -4627,10 +4650,8 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly, } EXPORT_SYMBOL_GPL(kvm_init_shadow_ept_mmu); -static void init_kvm_softmmu(struct kvm_vcpu *vcpu) +static void init_kvm_softmmu(struct kvm_vcpu *vcpu, struct kvm_mmu *context) { - struct kvm_mmu *context = vcpu->arch.mmu; - kvm_init_shadow_mmu(vcpu); context->set_cr3 = kvm_x86_ops->set_cr3; context->get_cr3 = get_cr3; @@ -4638,10 +4659,9 @@ static void init_kvm_softmmu(struct kvm_vcpu *vcpu) context->inject_page_fault = kvm_inject_page_fault; } -static void init_kvm_nested_mmu(struct kvm_vcpu *vcpu) +static void init_kvm_nested_mmu(struct kvm_vcpu *vcpu, + struct kvm_mmu *g_context) { - struct kvm_mmu *g_context = &vcpu->arch.nested_mmu; - g_context->get_cr3 = get_cr3; g_context->get_pdptr = kvm_pdptr_read; g_context->inject_page_fault = kvm_inject_page_fault; @@ -4681,16 +4701,34 @@ static void init_kvm_nested_mmu(struct kvm_vcpu *vcpu) update_last_nonleaf_level(vcpu, g_context); } -void kvm_mmu_reset_context(struct kvm_vcpu *vcpu) +void kvm_mmu_reset_context(struct kvm_vcpu *vcpu, bool check_if_unchanged) { + struct kvm_mmu *context = mmu_is_nested(vcpu) ? + &vcpu->arch.nested_mmu : vcpu->arch.mmu; + + if (check_if_unchanged && !mmu_update_needed(vcpu, context, 0, 0) && + context->scache.cr3 == vcpu->arch.mmu->get_cr3(vcpu)) { + /* + * Nothing changed but TLB should always be flushed, e.g. when + * we switch between L1 and L2. + */ + kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); + return; + } else if (!check_if_unchanged) { + context->scache.valid = 0; + } + kvm_mmu_unload(vcpu); if (mmu_is_nested(vcpu)) - init_kvm_nested_mmu(vcpu); + init_kvm_nested_mmu(vcpu, context); else if (tdp_enabled) - init_kvm_tdp_mmu(vcpu); + init_kvm_tdp_mmu(vcpu, context); else - init_kvm_softmmu(vcpu); + init_kvm_softmmu(vcpu, context); + + if (check_if_unchanged) + context->scache.cr3 = vcpu->arch.mmu->get_cr3(vcpu); } EXPORT_SYMBOL_GPL(kvm_mmu_reset_context); diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 3b3b9839c2b5..6c1db96971c0 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -1574,7 +1574,7 @@ static void init_vmcb(struct vcpu_svm *svm) * It also updates the guest-visible cr0 value. */ svm_set_cr0(&svm->vcpu, X86_CR0_NW | X86_CR0_CD | X86_CR0_ET); - kvm_mmu_reset_context(&svm->vcpu); + kvm_mmu_reset_context(&svm->vcpu, false); save->cr4 = X86_CR4_PAE; /* rdx = ?? */ @@ -3380,7 +3380,7 @@ static int nested_svm_vmexit(struct vcpu_svm *svm) nested_svm_unmap(page); nested_svm_uninit_mmu_context(&svm->vcpu); - kvm_mmu_reset_context(&svm->vcpu); + kvm_mmu_reset_context(&svm->vcpu, false); kvm_mmu_load(&svm->vcpu); return 0; @@ -3466,7 +3466,7 @@ static void enter_svm_guest_mode(struct vcpu_svm *svm, u64 vmcb_gpa, (void)kvm_set_cr3(&svm->vcpu, nested_vmcb->save.cr3); /* Guest paging mode is active - reset mmu */ - kvm_mmu_reset_context(&svm->vcpu); + kvm_mmu_reset_context(&svm->vcpu, false); svm->vmcb->save.cr2 = svm->vcpu.arch.cr2 = nested_vmcb->save.cr2; kvm_register_write(&svm->vcpu, VCPU_REGS_RAX, nested_vmcb->save.rax); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 3467665a75d5..a85ed004a4ba 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4696,7 +4696,7 @@ static void enter_rmode(struct kvm_vcpu *vcpu) fix_rmode_seg(VCPU_SREG_GS, &vmx->rmode.segs[VCPU_SREG_GS]); fix_rmode_seg(VCPU_SREG_FS, &vmx->rmode.segs[VCPU_SREG_FS]); - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); } static void vmx_set_efer(struct kvm_vcpu *vcpu, u64 efer) @@ -11123,6 +11123,8 @@ static bool nested_cr3_valid(struct kvm_vcpu *vcpu, unsigned long val) static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool nested_ept, u32 *entry_failure_code) { + bool mmu_reset_force = false; + if (cr3 != kvm_read_cr3(vcpu) || (!nested_ept && pdptrs_changed(vcpu))) { if (!nested_cr3_valid(vcpu, cr3)) { *entry_failure_code = ENTRY_FAIL_DEFAULT; @@ -11135,6 +11137,7 @@ static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool ne */ if (!is_long_mode(vcpu) && is_pae(vcpu) && is_paging(vcpu) && !nested_ept) { + mmu_reset_force = true; if (!load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3)) { *entry_failure_code = ENTRY_FAIL_PDPTE; return 1; @@ -11145,7 +11148,7 @@ static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool ne __set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail); } - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, !mmu_reset_force); return 0; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 5510a7f50195..3288a7e303ec 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -695,7 +695,7 @@ int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) } if ((cr0 ^ old_cr0) & update_bits) - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); if (((cr0 ^ old_cr0) & X86_CR0_CD) && kvm_arch_has_noncoherent_dma(vcpu->kvm) && @@ -836,7 +836,7 @@ int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) if (((cr4 ^ old_cr4) & pdptr_bits) || (!(cr4 & X86_CR4_PCIDE) && (old_cr4 & X86_CR4_PCIDE))) - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); if ((cr4 ^ old_cr4) & (X86_CR4_OSXSAVE | X86_CR4_PKE)) kvm_update_cpuid(vcpu); @@ -1162,7 +1162,7 @@ static int set_efer(struct kvm_vcpu *vcpu, u64 efer) /* Update reserved bits */ if ((efer ^ old_efer) & EFER_NX) - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); return 0; } @@ -5898,7 +5898,7 @@ static void kvm_smm_changed(struct kvm_vcpu *vcpu) kvm_make_request(KVM_REQ_EVENT, vcpu); } - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); } static void kvm_set_hflags(struct kvm_vcpu *vcpu, unsigned emul_flags) @@ -7156,7 +7156,7 @@ static void enter_smm(struct kvm_vcpu *vcpu) kvm_x86_ops->set_efer(vcpu, 0); kvm_update_cpuid(vcpu); - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); } static void process_smi(struct kvm_vcpu *vcpu) @@ -8058,7 +8058,7 @@ static int __set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) srcu_read_unlock(&vcpu->kvm->srcu, idx); if (mmu_reset_needed) - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); max_bits = KVM_NR_INTERRUPTS; pending_vec = find_first_bit( @@ -8333,7 +8333,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) kvm_vcpu_mtrr_init(vcpu); vcpu_load(vcpu); kvm_vcpu_reset(vcpu, false); - kvm_mmu_reset_context(vcpu); + kvm_mmu_reset_context(vcpu, false); vcpu_put(vcpu); return 0; } -- 2.14.4