Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- 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.
- ---
- Unlike other intercepts, debug register intercepts will be modified
- in hot paths if the guest OS is bad or otherwise gets tricked into
- doing so.
- Avoid calling recalc_intercepts 16 times for 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.
- ---
- When preparing the VMCS02, the CPU-based execution controls is computed
- by vmx_exec_control. Turn off DR access exits there, too, if the
- KVM_DEBUGREG_WONT_EXIT bit is set in switch_db_regs.
- ---
- When not running in guest-debug mode, the guest controls the debug
- registers and 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.
- After this patch, VMX- and SVM-specific code can set a flag in
- switch_db_regs, telling vcpu_enter_guest that on the next exit the debug
- registers might be dirty and need to be reloaded (syncing will be taken
- care of by a new callback in kvm_x86_ops). This flag can be set on the
- first access to a debug registers, so that multiple accesses to the
- debug registers only cause one vmexit.
- Note that since the guest will be able to read debug registers and
- enable breakpoints in DR7, we need to ensure that they are synchronized
- on entry to the guest---including DR6 that was not synced before.
- ---
- Currently, this works even if the bit is not in "min", because the bit is always
- set in MSR_IA32_VMX_ENTRY_CTLS. Mention it for the sake of documentation, and
- to avoid surprises if we later switch to MSR_IA32_VMX_TRUE_ENTRY_CTLS.
- ---
- --- a/arch/x86/include/asm/kvm_host.h 2014-03-31 03:40:15.000000000 +0000
- +++ b/arch/x86/include/asm/kvm_host.h 2014-06-03 23:12:45.827059438 +0000
- @@ -337,6 +337,11 @@
- u64 reprogram_pmi;
- };
- +enum {
- + KVM_DEBUGREG_BP_ENABLED = 1,
- + KVM_DEBUGREG_WONT_EXIT = 2,
- +};
- +
- struct kvm_vcpu_arch {
- /*
- * rip and regs accesses must go through
- @@ -464,7 +469,7 @@
- struct mtrr_state_type mtrr_state;
- u32 pat;
- - int switch_db_regs;
- + unsigned switch_db_regs;
- unsigned long db[KVM_NR_DB_REGS];
- unsigned long dr6;
- unsigned long dr7;
- @@ -702,6 +707,7 @@
- void (*set_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt);
- u64 (*get_dr6)(struct kvm_vcpu *vcpu);
- void (*set_dr6)(struct kvm_vcpu *vcpu, unsigned long value);
- + void (*sync_dirty_debug_regs)(struct kvm_vcpu *vcpu);
- void (*set_dr7)(struct kvm_vcpu *vcpu, unsigned long value);
- void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg);
- unsigned long (*get_rflags)(struct kvm_vcpu *vcpu);
- --- a/arch/x86/kvm/svm.c 2014-03-31 03:40:15.000000000 +0000
- +++ b/arch/x86/kvm/svm.c 2014-06-03 23:03:43.500052410 +0000
- @@ -303,23 +303,38 @@
- return vmcb->control.intercept_cr & (1U << bit);
- }
- -static inline void set_dr_intercept(struct vcpu_svm *svm, int bit)
- -{
- - struct vmcb *vmcb = get_host_vmcb(svm);
- -
- - vmcb->control.intercept_dr |= (1U << bit);
- -
- - recalc_intercepts(svm);
- -}
- -
- -static inline void clr_dr_intercept(struct vcpu_svm *svm, int bit)
- -{
- - struct vmcb *vmcb = get_host_vmcb(svm);
- -
- - vmcb->control.intercept_dr &= ~(1U << bit);
- -
- - recalc_intercepts(svm);
- -}
- +static inline void set_dr_intercepts(struct vcpu_svm *svm)
- + {
- + struct vmcb *vmcb = get_host_vmcb(svm);
- +
- + vmcb->control.intercept_dr = (1 << INTERCEPT_DR0_READ)
- + | (1 << INTERCEPT_DR1_READ)
- + | (1 << INTERCEPT_DR2_READ)
- + | (1 << INTERCEPT_DR3_READ)
- + | (1 << INTERCEPT_DR4_READ)
- + | (1 << INTERCEPT_DR5_READ)
- + | (1 << INTERCEPT_DR6_READ)
- + | (1 << INTERCEPT_DR7_READ)
- + | (1 << INTERCEPT_DR0_WRITE)
- + | (1 << INTERCEPT_DR1_WRITE)
- + | (1 << INTERCEPT_DR2_WRITE)
- + | (1 << INTERCEPT_DR3_WRITE)
- + | (1 << INTERCEPT_DR4_WRITE)
- + | (1 << INTERCEPT_DR5_WRITE)
- + | (1 << INTERCEPT_DR6_WRITE)
- + | (1 << INTERCEPT_DR7_WRITE);
- +
- + recalc_intercepts(svm);
- + }
- +
- +static inline void clr_dr_intercepts(struct vcpu_svm *svm)
- + {
- + struct vmcb *vmcb = get_host_vmcb(svm);
- +
- + vmcb->control.intercept_dr = 0;
- +
- + recalc_intercepts(svm);
- + }
- static inline void set_exception_intercept(struct vcpu_svm *svm, int bit)
- {
- @@ -1080,23 +1095,7 @@
- set_cr_intercept(svm, INTERCEPT_CR4_WRITE);
- set_cr_intercept(svm, INTERCEPT_CR8_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR0_READ);
- - set_dr_intercept(svm, INTERCEPT_DR1_READ);
- - set_dr_intercept(svm, INTERCEPT_DR2_READ);
- - set_dr_intercept(svm, INTERCEPT_DR3_READ);
- - set_dr_intercept(svm, INTERCEPT_DR4_READ);
- - set_dr_intercept(svm, INTERCEPT_DR5_READ);
- - set_dr_intercept(svm, INTERCEPT_DR6_READ);
- - set_dr_intercept(svm, INTERCEPT_DR7_READ);
- -
- - set_dr_intercept(svm, INTERCEPT_DR0_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR1_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR2_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR3_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR4_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR5_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR6_WRITE);
- - set_dr_intercept(svm, INTERCEPT_DR7_WRITE);
- + set_dr_intercepts(svm);
- set_exception_intercept(svm, PF_VECTOR);
- set_exception_intercept(svm, UD_VECTOR);
- @@ -1684,6 +1683,21 @@
- mark_dirty(svm->vmcb, VMCB_DR);
- }
- +static void svm_sync_dirty_debug_regs(struct kvm_vcpu *vcpu)
- +{
- + struct vcpu_svm *svm = to_svm(vcpu);
- +
- + get_debugreg(vcpu->arch.db[0], 0);
- + get_debugreg(vcpu->arch.db[1], 1);
- + get_debugreg(vcpu->arch.db[2], 2);
- + get_debugreg(vcpu->arch.db[3], 3);
- + vcpu->arch.dr6 = svm_get_dr6(vcpu);
- + vcpu->arch.dr7 = svm->vmcb->save.dr7;
- +
- + vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_WONT_EXIT;
- + set_dr_intercepts(svm);
- +}
- +
- static void svm_set_dr7(struct kvm_vcpu *vcpu, unsigned long value)
- {
- struct vcpu_svm *svm = to_svm(vcpu);
- @@ -2973,6 +2987,16 @@
- int reg, dr;
- unsigned long val;
- int err;
- + if (svm->vcpu.guest_debug == 0) {
- + /*
- + * No more DR vmexits; force a reload of the debug registers
- + * and reenter on this instruction. The next vmexit will
- + * retrieve the full state of the debug registers.
- + */
- + clr_dr_intercepts(svm);
- + svm->vcpu.arch.switch_db_regs |= KVM_DEBUGREG_WONT_EXIT;
- + return 1;
- + }
- if (!boot_cpu_has(X86_FEATURE_DECODEASSISTS))
- return emulate_on_interception(svm);
- @@ -4302,6 +4326,7 @@
- .get_dr6 = svm_get_dr6,
- .set_dr6 = svm_set_dr6,
- .set_dr7 = svm_set_dr7,
- + .sync_dirty_debug_regs = svm_sync_dirty_debug_regs,
- .cache_reg = svm_cache_reg,
- .get_rflags = svm_get_rflags,
- .set_rflags = svm_set_rflags,
- --- a/arch/x86/kvm/vmx.c 2014-03-31 03:40:15.000000000 +0000
- +++ b/arch/x86/kvm/vmx.c 2014-06-03 22:54:01.928044874 +0000
- @@ -2832,7 +2832,7 @@
- vmx_capability.ept, vmx_capability.vpid);
- }
- - min = 0;
- + min = VM_EXIT_SAVE_DEBUG_CONTROLS;
- #ifdef CONFIG_X86_64
- min |= VM_EXIT_HOST_ADDR_SPACE_SIZE;
- #endif
- @@ -2853,7 +2853,7 @@
- !(_vmexit_control & VM_EXIT_ACK_INTR_ON_EXIT))
- _pin_based_exec_control &= ~PIN_BASED_POSTED_INTR;
- - min = 0;
- + min = VM_ENTRY_LOAD_DEBUG_CONTROLS;
- opt = VM_ENTRY_LOAD_IA32_PAT;
- if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_ENTRY_CTLS,
- &_vmentry_control) < 0)
- @@ -4223,6 +4223,10 @@
- static u32 vmx_exec_control(struct vcpu_vmx *vmx)
- {
- u32 exec_control = vmcs_config.cpu_based_exec_ctrl;
- +
- + if (vmx->vcpu.arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)
- + exec_control &= ~CPU_BASED_MOV_DR_EXITING;
- +
- if (!vm_need_tpr_shadow(vmx->vcpu.kvm)) {
- exec_control &= ~CPU_BASED_TPR_SHADOW;
- #ifdef CONFIG_X86_64
- @@ -5101,6 +5105,21 @@
- return 1;
- }
- }
- + if (vcpu->guest_debug == 0) {
- + u32 cpu_based_vm_exec_control;
- +
- + cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
- + cpu_based_vm_exec_control &= ~CPU_BASED_MOV_DR_EXITING;
- + vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
- +
- + /*
- + * No more DR vmexits; force a reload of the debug registers
- + * and reenter on this instruction. The next vmexit will
- + * retrieve the full state of the debug registers.
- + */
- + vcpu->arch.switch_db_regs |= KVM_DEBUGREG_WONT_EXIT;
- + return 1;
- + }
- exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
- dr = exit_qualification & DEBUG_REG_ACCESS_NUM;
- @@ -5128,6 +5147,24 @@
- {
- }
- +static void vmx_sync_dirty_debug_regs(struct kvm_vcpu *vcpu)
- +{
- + u32 cpu_based_vm_exec_control;
- +
- + get_debugreg(vcpu->arch.db[0], 0);
- + get_debugreg(vcpu->arch.db[1], 1);
- + get_debugreg(vcpu->arch.db[2], 2);
- + get_debugreg(vcpu->arch.db[3], 3);
- + get_debugreg(vcpu->arch.dr6, 6);
- + vcpu->arch.dr7 = vmcs_readl(GUEST_DR7);
- +
- + vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_WONT_EXIT;
- +
- + cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
- + cpu_based_vm_exec_control |= CPU_BASED_MOV_DR_EXITING;
- + vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
- +}
- +
- static void vmx_set_dr7(struct kvm_vcpu *vcpu, unsigned long val)
- {
- vmcs_writel(GUEST_DR7, val);
- @@ -8573,6 +8610,7 @@
- .get_dr6 = vmx_get_dr6,
- .set_dr6 = vmx_set_dr6,
- .set_dr7 = vmx_set_dr7,
- + .sync_dirty_debug_regs = vmx_sync_dirty_debug_regs,
- .cache_reg = vmx_cache_reg,
- .get_rflags = vmx_get_rflags,
- .set_rflags = vmx_set_rflags,
- --- a/arch/x86/kvm/x86.c 2014-03-31 03:40:15.000000000 +0000
- +++ b/arch/x86/kvm/x86.c 2014-06-03 23:10:14.892057482 +0000
- @@ -753,7 +753,9 @@
- else
- dr7 = vcpu->arch.dr7;
- kvm_x86_ops->set_dr7(vcpu, dr7);
- - vcpu->arch.switch_db_regs = (dr7 & DR7_BP_EN_MASK);
- + vcpu->arch.switch_db_regs &= ~KVM_DEBUGREG_BP_ENABLED;
- + if (dr7 & DR7_BP_EN_MASK)
- + vcpu->arch.switch_db_regs |= KVM_DEBUGREG_BP_ENABLED;
- }
- static int __kvm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long val)
- @@ -5992,12 +5994,28 @@
- set_debugreg(vcpu->arch.eff_db[1], 1);
- set_debugreg(vcpu->arch.eff_db[2], 2);
- set_debugreg(vcpu->arch.eff_db[3], 3);
- + set_debugreg(vcpu->arch.dr6, 6);
- }
- trace_kvm_entry(vcpu->vcpu_id);
- kvm_x86_ops->run(vcpu);
- /*
- + * Do this here before restoring debug registers on the host. And
- + * since we do this before handling the vmexit, a DR access vmexit
- + * can (a) read the correct value of the debug registers, (b) set
- + * KVM_DEBUGREG_WONT_EXIT again.
- + */
- + if (unlikely(vcpu->arch.switch_db_regs & KVM_DEBUGREG_WONT_EXIT)) {
- + int i;
- +
- + WARN_ON(vcpu->guest_debug & KVM_GUESTDBG_USE_HW_BP);
- + kvm_x86_ops->sync_dirty_debug_regs(vcpu);
- + for (i = 0; i < KVM_NR_DB_REGS; i++)
- + vcpu->arch.eff_db[i] = vcpu->arch.db[i];
- + }
- +
- + /*
- * If the guest has used debug registers, at least dr7
- * will be disabled while returning to the host.
- * If we don't have active breakpoints in the host, we don't
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement