diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2014-02-21 04:32:27 -0500 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2014-03-11 05:46:03 -0400 |
commit | 81908bf44340eb5ebc9969f67e6c8be0c92f2857 (patch) | |
tree | 67fb9e8f536d8319c54f9e6af1d6cbaf844e6673 | |
parent | c77fb5fe6f031bee9403397ae7b94ea22ea19aa7 (diff) |
KVM: vmx: Allow the guest to run with dirty debug registers
When not running in guest-debug mode (i.e. the guest controls the debug
registers, having to take an exit for each DR access is a waste of time.
If the guest gets into a state where each context switch causes DR to be
saved and restored, this can take away as much as 40% of the execution
time from the guest.
If the guest is running with vcpu->arch.db == vcpu->arch.eff_db, we
can let it write freely to the debug registers and reload them on the
next exit. We still need to exit on the first access, so that the
KVM_DEBUGREG_WONT_EXIT flag is set in switch_db_regs; after that, further
accesses to the debug registers will not cause a vmexit.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r-- | arch/x86/kvm/vmx.c | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index b2a913bb07e0..a9940ec9e215 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <asm/i387.h> | 43 | #include <asm/i387.h> |
44 | #include <asm/xcr.h> | 44 | #include <asm/xcr.h> |
45 | #include <asm/perf_event.h> | 45 | #include <asm/perf_event.h> |
46 | #include <asm/debugreg.h> | ||
46 | #include <asm/kexec.h> | 47 | #include <asm/kexec.h> |
47 | 48 | ||
48 | #include "trace.h" | 49 | #include "trace.h" |
@@ -2850,7 +2851,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf) | |||
2850 | vmx_capability.ept, vmx_capability.vpid); | 2851 | vmx_capability.ept, vmx_capability.vpid); |
2851 | } | 2852 | } |
2852 | 2853 | ||
2853 | min = 0; | 2854 | min = VM_EXIT_SAVE_DEBUG_CONTROLS; |
2854 | #ifdef CONFIG_X86_64 | 2855 | #ifdef CONFIG_X86_64 |
2855 | min |= VM_EXIT_HOST_ADDR_SPACE_SIZE; | 2856 | min |= VM_EXIT_HOST_ADDR_SPACE_SIZE; |
2856 | #endif | 2857 | #endif |
@@ -5084,6 +5085,22 @@ static int handle_dr(struct kvm_vcpu *vcpu) | |||
5084 | } | 5085 | } |
5085 | } | 5086 | } |
5086 | 5087 | ||
5088 | if (vcpu->guest_debug == 0) { | ||
5089 | u32 cpu_based_vm_exec_control; | ||
5090 | |||
5091 | cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); | ||
5092 | cpu_based_vm_exec_control &= ~CPU_BASED_MOV_DR_EXITING; | ||
5093 | vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); | ||
5094 | |||
5095 | /* | ||
5096 | * No more DR vmexits; force a reload of the debug registers | ||
5097 | * and reenter on this instruction. The next vmexit will | ||
5098 | * retrieve the full state of the debug registers. | ||
5099 | */ | ||
5100 | vcpu->arch.switch_db_regs |= KVM_DEBUGREG_WONT_EXIT; | ||
5101 | return 1; | ||
5102 | } | ||
5103 | |||
5087 | exit_qualification = vmcs_readl(EXIT_QUALIFICATION); | 5104 | exit_qualification = vmcs_readl(EXIT_QUALIFICATION); |
5088 | dr = exit_qualification & DEBUG_REG_ACCESS_NUM; | 5105 | dr = exit_qualification & DEBUG_REG_ACCESS_NUM; |
5089 | reg = DEBUG_REG_ACCESS_REG(exit_qualification); | 5106 | reg = DEBUG_REG_ACCESS_REG(exit_qualification); |
@@ -5110,6 +5127,24 @@ static void vmx_set_dr6(struct kvm_vcpu *vcpu, unsigned long val) | |||
5110 | { | 5127 | { |
5111 | } | 5128 | } |
5112 | 5129 | ||
5130 | static void vmx_sync_dirty_debug_regs(struct kvm_vcpu *vcpu) | ||
5131 | { | ||
5132 | u32 cpu_based_vm_exec_control; | ||
5133 | |||
5134 | get_debugreg(vcpu->arch.db[0], 0); | ||
5135 | get_debugreg(vcpu->arch.db[1], 1); | ||
5136 | get_debugreg(vcpu->arch.db[2], 2); | ||
5137 | get_debugreg(vcpu->arch.db[3], 3); | ||
5138 | get_debugreg(vcpu->arch.dr6, 6); | ||
5139 | vcpu->arch.dr7 = vmcs_readl(GUEST_DR7); | ||
5140 | |||
5141 | vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_WONT_EXIT; | ||
5142 | |||
5143 | cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); | ||
5144 | cpu_based_vm_exec_control |= CPU_BASED_MOV_DR_EXITING; | ||
5145 | vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); | ||
5146 | } | ||
5147 | |||
5113 | static void vmx_set_dr7(struct kvm_vcpu *vcpu, unsigned long val) | 5148 | static void vmx_set_dr7(struct kvm_vcpu *vcpu, unsigned long val) |
5114 | { | 5149 | { |
5115 | vmcs_writel(GUEST_DR7, val); | 5150 | vmcs_writel(GUEST_DR7, val); |
@@ -8628,6 +8663,7 @@ static struct kvm_x86_ops vmx_x86_ops = { | |||
8628 | .get_dr6 = vmx_get_dr6, | 8663 | .get_dr6 = vmx_get_dr6, |
8629 | .set_dr6 = vmx_set_dr6, | 8664 | .set_dr6 = vmx_set_dr6, |
8630 | .set_dr7 = vmx_set_dr7, | 8665 | .set_dr7 = vmx_set_dr7, |
8666 | .sync_dirty_debug_regs = vmx_sync_dirty_debug_regs, | ||
8631 | .cache_reg = vmx_cache_reg, | 8667 | .cache_reg = vmx_cache_reg, |
8632 | .get_rflags = vmx_get_rflags, | 8668 | .get_rflags = vmx_get_rflags, |
8633 | .set_rflags = vmx_set_rflags, | 8669 | .set_rflags = vmx_set_rflags, |