Skip to contentSkip to conversation

Lazy fpu for svm/npt

PATCH In review Avi Kivity 2 participants 9 messages Jan 10, 2010 – Jan 11, 2010

Subsystems (3)
lore
Share & tools
lore

Avi Kivity

author
#0 lore raw

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(-)
author
#1 lore raw

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
index 6e6f188..ca42ae3 100644
@@ -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
author
#2 lore raw

PATCH v2 2/6 KVM: SVM: Fix SVM_CR0_SELECTIVE_MASK

In reply to Avi Kivity

Instead 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
index 1fecb7e..38638cd 100644
@@ -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
author
#3 lore raw

PATCH v2 3/6 KVM: SVM: Initialize fpu_active in init_vmcb()

In reply to Avi Kivity

init_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
index 4ad8e2e..92f5574 100644
@@ -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
author
#4 lore raw

PATCH v2 4/6 KVM: SVM: Restore unconditional cr0 intercept under npt

In reply to Avi Kivity

Currently 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
index 92f5574..e922605 100644
@@ -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
author
#5 lore raw

PATCH v2 5/6 KVM: SVM: Selective cr0 intercept

In reply to Avi Kivity

If 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
index e922605..d969ad2 100644
@@ -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
author
#6 lore raw

PATCH v2 6/6 KVM: SVM: Lazy fpu with npt

In reply to Avi Kivity

Now 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
index d969ad2..3ac48ee 100644
@@ -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
Acked-by on the v2 series
#7 lore raw
In reply to Avi Kivity
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.

author
#8 lore raw
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.

Now merged, along with the earlier vmx-related bits.

Signature · 1 line
error compiling committee.c: too many arguments to function