Lazy fpu for svm/npt
v2 cover Lazy fpu for svm/npt
This patchset (on top of the previous cr0 patchset) brings lazy fpu to npt. For the cases where guest and host cr0 match (the majority) it will disable intercepts for cr0.ts once the guest fpu is loaded, so the guest can to its own lazy fpu without trapping.
v2:
Refactored last patch into three independent patches; no code changes.
Avi Kivity (6):
KVM: Set cr0.et when the guest writes cr0
KVM: SVM: Fix SVM_CR0_SELECTIVE_MASK
KVM: SVM: Initialize fpu_active in init_vmcb()
KVM: SVM: Restore unconditional cr0 intercept under npt
KVM: SVM: Selective cr0 intercept
KVM: SVM: Lazy fpu with npt
arch/x86/include/asm/svm.h | 2 +- arch/x86/kvm/svm.c | 73 +++++++++++++++++++++---------------------- arch/x86/kvm/x86.c | 2 + 3 files changed, 39 insertions(+), 38 deletions(-)
PATCH v2 1/6 KVM: Set cr0.et when the guest writes cr0
Follow the hardware.
--- arch/x86/kvm/x86.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
arch/x86/kvm/x86.c +2 -0
@@ -428,6 +428,8 @@ out:void kvm_set_cr0 (struct kvm_vcpu *vcpu,unsigned long cr0) { + cr0 |=X86_CR0_ET ; +if (cr0 &CR0_RESERVED_BITS ) {printk (KERN_DEBUG "set_cr0: 0x%lx #GP, reserved bits 0x%lx\n" , cr0,kvm_read_cr0 (vcpu));
Signature · 1 line
1.6.5.3
PATCH v2 2/6 KVM: SVM: Fix SVM_CR0_SELECTIVE_MASK
In reply to Avi KivityInstead of selecting TS and MP as the comments say, the macro included TS and PE. Luckily the macro is unused now, but fix in order to save a few hours of debugging from anyone who attempts to use it.
--- arch/x86/include/asm/svm.h | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
arch/x86/include/asm/svm.h +1 -1
@@ -313,7 +313,7 @@ struct __attribute__ ((__packed__)) vmcb {#define SVM_EXIT_ERR -1 -#define SVM_CR0_SELECTIVE_MASK (1 << 3 | 1)/* TS and MP */ +#define SVM_CR0_SELECTIVE_MASK (X86_CR0_TS | X86_CR0_MP)#define SVM_VMLOAD ".byte 0x0f, 0x01, 0xda"#define SVM_VMRUN ".byte 0x0f, 0x01, 0xd8"
Signature · 1 line
1.6.5.3
PATCH v2 3/6 KVM: SVM: Initialize fpu_active in init_vmcb()
In reply to Avi Kivityinit_vmcb() sets up the intercepts as if the fpu is active, so initialize it there. This avoids an INIT from setting up intercepts inconsistent with fpu_active.
--- arch/x86/kvm/svm.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
arch/x86/kvm/svm.c +2 -1
@@ -540,6 +540,8 @@ static void init_vmcb(struct vcpu_svm *svm)struct vmcb_control_area *control = &svm->vmcb->control;struct vmcb_save_area *save = &svm->vmcb->save; + svm->vcpu.fpu_active =1 ; + control->intercept_cr_read =INTERCEPT_CR0_MASK |INTERCEPT_CR3_MASK |INTERCEPT_CR4_MASK ;
@@ -730,7 +732,6 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)init_vmcb (svm);fx_init (&svm->vcpu); - svm->vcpu.fpu_active =1 ; svm->vcpu.arch.apic_base =0xfee00000 |MSR_IA32_APICBASE_ENABLE ;if (kvm_vcpu_is_bsp (&svm->vcpu)) svm->vcpu.arch.apic_base |=MSR_IA32_APICBASE_BSP ;
Signature · 1 line
1.6.5.3
PATCH v2 4/6 KVM: SVM: Restore unconditional cr0 intercept under npt
In reply to Avi KivityCurrently we don't intercept cr0 at all when npt is enabled. This improves performance but requires us to activate the fpu at all times.
Remove this behaviour in preparation for adding selective cr0 intercepts.
--- arch/x86/kvm/svm.c | 30 ++++++++---------------------- 1 files changed, 8 insertions(+), 22 deletions(-)
arch/x86/kvm/svm.c +8 -22
@@ -643,10 +643,8 @@ static void init_vmcb(struct vcpu_svm *svm) control->intercept &= ~((1ULL <<INTERCEPT_TASK_SWITCH ) | (1ULL <<INTERCEPT_INVLPG )); control->intercept_exceptions &= ~(1 <<PF_VECTOR ); - control->intercept_cr_read &= ~(INTERCEPT_CR0_MASK | -INTERCEPT_CR3_MASK ); - control->intercept_cr_write &= ~(INTERCEPT_CR0_MASK | -INTERCEPT_CR3_MASK ); + control->intercept_cr_read &= ~INTERCEPT_CR3_MASK ; + control->intercept_cr_write &= ~INTERCEPT_CR3_MASK ; save->g_pat =0x0007040600070406ULL ; save->cr3 =0 ; save->cr4 =0 ;
@@ -982,12 +980,11 @@ static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) } }#endif -if (npt_enabled) - goto set; - vcpu->arch.cr0 = cr0; - cr0 |=X86_CR0_PG |X86_CR0_WP ; -set: + +if (!npt_enabled) + cr0 |=X86_CR0_PG |X86_CR0_WP ; +/* * re-enable caching here because the QEMU bios * does not do it - this results in some delay at
@@ -2383,21 +2380,10 @@ static int handle_exit(struct kvm_vcpu *vcpu)svm_complete_interrupts (svm); -if (npt_enabled) { -int mmu_reload =0 ; -if ((kvm_read_cr0_bits (vcpu,X86_CR0_PG ) ^ svm->vmcb->save.cr0) - &X86_CR0_PG ) { -svm_set_cr0 (vcpu, svm->vmcb->save.cr0); - mmu_reload =1 ; - } +if (!(svm->vmcb->control.intercept_cr_write &INTERCEPT_CR0_MASK )) vcpu->arch.cr0 = svm->vmcb->save.cr0; +if (npt_enabled) vcpu->arch.cr3 = svm->vmcb->save.cr3; -if (mmu_reload) { -kvm_mmu_reset_context (vcpu); -kvm_mmu_load (vcpu); - } - } -if (svm->vmcb->control.exit_code ==SVM_EXIT_ERR ) { kvm_run->exit_reason =KVM_EXIT_FAIL_ENTRY ;
Signature · 1 line
1.6.5.3
PATCH v2 5/6 KVM: SVM: Selective cr0 intercept
In reply to Avi KivityIf two conditions apply:
- no bits outside TS and EM differ between the host and guest cr0
- the fpu is active
then we can activate the selective cr0 write intercept and drop the unconditional cr0 read and write intercept, and allow the guest to run with the host fpu state. This reduces cr0 exits due to guest fpu management while the guest fpu is loaded.
--- arch/x86/kvm/svm.c | 32 ++++++++++++++++++++++++++------ 1 files changed, 26 insertions(+), 6 deletions(-)
arch/x86/kvm/svm.c +26 -6
@@ -571,6 +571,7 @@ static void init_vmcb(struct vcpu_svm *svm) control->intercept = (1ULL <<INTERCEPT_INTR ) | (1ULL <<INTERCEPT_NMI ) | (1ULL <<INTERCEPT_SMI ) | + (1ULL <<INTERCEPT_SELECTIVE_CR0 ) | (1ULL <<INTERCEPT_CPUID ) | (1ULL <<INTERCEPT_INVD ) | (1ULL << INTERCEPT_HLT) |
@@ -963,6 +964,27 @@ static void svm_decache_cr4_guest_bits(struct kvm_vcpu *vcpu) { } +static void update_cr0_intercept (struct vcpu_svm *svm) +{ +ulong gcr0 = svm->vcpu.arch.cr0; +u64 *hcr0 = &svm->vmcb->save.cr0; + +if (!svm->vcpu.fpu_active) + *hcr0 |=SVM_CR0_SELECTIVE_MASK ; +else + *hcr0 = (*hcr0 & ~SVM_CR0_SELECTIVE_MASK ) + | (gcr0 &SVM_CR0_SELECTIVE_MASK ); + + +if (gcr0 == *hcr0 && svm->vcpu.fpu_active) { + svm->vmcb->control.intercept_cr_read &= ~INTERCEPT_CR0_MASK ; + svm->vmcb->control.intercept_cr_write &= ~INTERCEPT_CR0_MASK ; + }else { + svm->vmcb->control.intercept_cr_read |=INTERCEPT_CR0_MASK ; + svm->vmcb->control.intercept_cr_write |=INTERCEPT_CR0_MASK ; + } +} +static void svm_set_cr0 (struct kvm_vcpu *vcpu,unsigned long cr0) {struct vcpu_svm *svm =to_svm (vcpu);
@@ -992,6 +1014,7 @@ static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)*/ cr0 &= ~(X86_CR0_CD |X86_CR0_NW ); svm->vmcb->save.cr0 = cr0; +update_cr0_intercept (svm); }static void svm_set_cr4 (struct kvm_vcpu *vcpu,unsigned long cr4)
@@ -1237,11 +1260,8 @@ static int ud_interception(struct vcpu_svm *svm)static int nm_interception (struct vcpu_svm *svm) { svm->vmcb->control.intercept_exceptions &= ~(1 <<NM_VECTOR ); -if (!kvm_read_cr0_bits (&svm->vcpu,X86_CR0_TS )) - svm->vmcb->save.cr0 &= ~X86_CR0_TS ; -else - svm->vmcb->save.cr0 |=X86_CR0_TS ; svm->vcpu.fpu_active =1 ; +update_cr0_intercept (svm);return 1 ; }
@@ -2294,7 +2314,7 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm) = { [SVM_EXIT_READ_CR3] = emulate_on_interception, [SVM_EXIT_READ_CR4] = emulate_on_interception, [SVM_EXIT_READ_CR8] = emulate_on_interception, -/* for now: */ + [SVM_EXIT_CR0_SEL_WRITE] = emulate_on_interception, [SVM_EXIT_WRITE_CR0] = emulate_on_interception, [SVM_EXIT_WRITE_CR3] = emulate_on_interception, [SVM_EXIT_WRITE_CR4] = emulate_on_interception,
@@ -2912,8 +2932,8 @@ static void svm_fpu_deactivate(struct kvm_vcpu *vcpu)return ; } +update_cr0_intercept (svm); svm->vmcb->control.intercept_exceptions |=1 <<NM_VECTOR ; - svm->vmcb->save.cr0 |=X86_CR0_TS ; }static struct kvm_x86_ops svm_x86_ops = {
Signature · 1 line
1.6.5.3
PATCH v2 6/6 KVM: SVM: Lazy fpu with npt
In reply to Avi KivityNow that we can allow the guest to play with cr0 when the fpu is loaded, we can enable lazy fpu when npt is in use.
--- arch/x86/kvm/svm.c | 8 -------- 1 files changed, 0 insertions(+), 8 deletions(-)
arch/x86/kvm/svm.c +0 -8
@@ -2586,8 +2586,6 @@ static void svm_flush_tlb(struct kvm_vcpu *vcpu)static void svm_prepare_guest_switch (struct kvm_vcpu *vcpu) { -if (npt_enabled) - vcpu->fpu_active =1 ; }static inline void sync_cr8_to_lapic (struct kvm_vcpu *vcpu)
@@ -2926,12 +2924,6 @@ static void svm_fpu_deactivate(struct kvm_vcpu *vcpu) {struct vcpu_svm *svm =to_svm (vcpu); -if (npt_enabled) { -/* hack: npt requires active fpu at this time */ - vcpu->fpu_active =1 ; -return ; - } -update_cr0_intercept (svm); svm->vmcb->control.intercept_exceptions |=1 <<NM_VECTOR ; }
Signature · 1 line
1.6.5.3
Quoted 4 lines · Avi Kivity
This patchset (on top of the previous cr0 patchset) brings lazy fpu to npt. For the cases where guest and host cr0 match (the majority) it will disable intercepts for cr0.ts once the guest fpu is loaded, so the guest can to its own lazy fpu without trapping.
Looks good to me.
Quoted 11 lines · Joerg Roedel
Quoted 5 lines · Avi Kivity
This patchset (on top of the previous cr0 patchset) brings lazy fpu to npt. For the cases where guest and host cr0 match (the majority) it will disable intercepts for cr0.ts once the guest fpu is loaded, so the guest can to its own lazy fpu without trapping.
Looks good to me.
Acked-by Joerg Roedel<[email protected]>
Now merged, along with the earlier vmx-related bits.
Signature · 1 line
error compiling committee.c: too many arguments to function