aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorYang Zhang <yang.z.zhang@Intel.com>2013-04-11 07:25:10 -0400
committerMarcelo Tosatti <mtosatti@redhat.com>2013-04-16 15:32:39 -0400
commita547c6db4d2f16ba5ce8e7054bffad6acc248d40 (patch)
tree589147910818e8dc61e1904bc839e624eb1f9076 /arch
parent2c2bf01136971c33e3b3fabce23925f372c1017e (diff)
KVM: VMX: Enable acknowledge interupt on vmexit
The "acknowledge interrupt on exit" feature controls processor behavior for external interrupt acknowledgement. When this control is set, the processor acknowledges the interrupt controller to acquire the interrupt vector on VM exit. After enabling this feature, an interrupt which arrived when target cpu is running in vmx non-root mode will be handled by vmx handler instead of handler in idt. Currently, vmx handler only fakes an interrupt stack and jump to idt table to let real handler to handle it. Further, we will recognize the interrupt and only delivery the interrupt which not belong to current vcpu through idt table. The interrupt which belonged to current vcpu will be handled inside vmx handler. This will reduce the interrupt handle cost of KVM. Also, interrupt enable logic is changed if this feature is turnning on: Before this patch, hypervior call local_irq_enable() to enable it directly. Now IF bit is set on interrupt stack frame, and will be enabled on a return from interrupt handler if exterrupt interrupt exists. If no external interrupt, still call local_irq_enable() to enable it. Refer to Intel SDM volum 3, chapter 33.2. Signed-off-by: Yang Zhang <yang.z.zhang@Intel.com> Reviewed-by: Gleb Natapov <gleb@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/include/asm/kvm_host.h1
-rw-r--r--arch/x86/kvm/svm.c6
-rw-r--r--arch/x86/kvm/vmx.c58
-rw-r--r--arch/x86/kvm/x86.c4
4 files changed, 64 insertions, 5 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 82f1dc67782f..68d438630dd3 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -727,6 +727,7 @@ struct kvm_x86_ops {
727 int (*check_intercept)(struct kvm_vcpu *vcpu, 727 int (*check_intercept)(struct kvm_vcpu *vcpu,
728 struct x86_instruction_info *info, 728 struct x86_instruction_info *info,
729 enum x86_intercept_stage stage); 729 enum x86_intercept_stage stage);
730 void (*handle_external_intr)(struct kvm_vcpu *vcpu);
730}; 731};
731 732
732struct kvm_arch_async_pf { 733struct kvm_arch_async_pf {
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 7a46c1f46861..2f8fe3f06837 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -4233,6 +4233,11 @@ out:
4233 return ret; 4233 return ret;
4234} 4234}
4235 4235
4236static void svm_handle_external_intr(struct kvm_vcpu *vcpu)
4237{
4238 local_irq_enable();
4239}
4240
4236static struct kvm_x86_ops svm_x86_ops = { 4241static struct kvm_x86_ops svm_x86_ops = {
4237 .cpu_has_kvm_support = has_svm, 4242 .cpu_has_kvm_support = has_svm,
4238 .disabled_by_bios = is_disabled, 4243 .disabled_by_bios = is_disabled,
@@ -4328,6 +4333,7 @@ static struct kvm_x86_ops svm_x86_ops = {
4328 .set_tdp_cr3 = set_tdp_cr3, 4333 .set_tdp_cr3 = set_tdp_cr3,
4329 4334
4330 .check_intercept = svm_check_intercept, 4335 .check_intercept = svm_check_intercept,
4336 .handle_external_intr = svm_handle_external_intr,
4331}; 4337};
4332 4338
4333static int __init svm_init(void) 4339static int __init svm_init(void)
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 71755573b7ca..7a7605f0444b 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -378,6 +378,7 @@ struct vcpu_vmx {
378 struct shared_msr_entry *guest_msrs; 378 struct shared_msr_entry *guest_msrs;
379 int nmsrs; 379 int nmsrs;
380 int save_nmsrs; 380 int save_nmsrs;
381 unsigned long host_idt_base;
381#ifdef CONFIG_X86_64 382#ifdef CONFIG_X86_64
382 u64 msr_host_kernel_gs_base; 383 u64 msr_host_kernel_gs_base;
383 u64 msr_guest_kernel_gs_base; 384 u64 msr_guest_kernel_gs_base;
@@ -2627,7 +2628,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
2627#ifdef CONFIG_X86_64 2628#ifdef CONFIG_X86_64
2628 min |= VM_EXIT_HOST_ADDR_SPACE_SIZE; 2629 min |= VM_EXIT_HOST_ADDR_SPACE_SIZE;
2629#endif 2630#endif
2630 opt = VM_EXIT_SAVE_IA32_PAT | VM_EXIT_LOAD_IA32_PAT; 2631 opt = VM_EXIT_SAVE_IA32_PAT | VM_EXIT_LOAD_IA32_PAT |
2632 VM_EXIT_ACK_INTR_ON_EXIT;
2631 if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_EXIT_CTLS, 2633 if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_EXIT_CTLS,
2632 &_vmexit_control) < 0) 2634 &_vmexit_control) < 0)
2633 return -EIO; 2635 return -EIO;
@@ -3879,7 +3881,7 @@ static void vmx_disable_intercept_msr_write_x2apic(u32 msr)
3879 * Note that host-state that does change is set elsewhere. E.g., host-state 3881 * Note that host-state that does change is set elsewhere. E.g., host-state
3880 * that is set differently for each CPU is set in vmx_vcpu_load(), not here. 3882 * that is set differently for each CPU is set in vmx_vcpu_load(), not here.
3881 */ 3883 */
3882static void vmx_set_constant_host_state(void) 3884static void vmx_set_constant_host_state(struct vcpu_vmx *vmx)
3883{ 3885{
3884 u32 low32, high32; 3886 u32 low32, high32;
3885 unsigned long tmpl; 3887 unsigned long tmpl;
@@ -3907,6 +3909,7 @@ static void vmx_set_constant_host_state(void)
3907 3909
3908 native_store_idt(&dt); 3910 native_store_idt(&dt);
3909 vmcs_writel(HOST_IDTR_BASE, dt.address); /* 22.2.4 */ 3911 vmcs_writel(HOST_IDTR_BASE, dt.address); /* 22.2.4 */
3912 vmx->host_idt_base = dt.address;
3910 3913
3911 vmcs_writel(HOST_RIP, vmx_return); /* 22.2.5 */ 3914 vmcs_writel(HOST_RIP, vmx_return); /* 22.2.5 */
3912 3915
@@ -4039,7 +4042,7 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
4039 4042
4040 vmcs_write16(HOST_FS_SELECTOR, 0); /* 22.2.4 */ 4043 vmcs_write16(HOST_FS_SELECTOR, 0); /* 22.2.4 */
4041 vmcs_write16(HOST_GS_SELECTOR, 0); /* 22.2.4 */ 4044 vmcs_write16(HOST_GS_SELECTOR, 0); /* 22.2.4 */
4042 vmx_set_constant_host_state(); 4045 vmx_set_constant_host_state(vmx);
4043#ifdef CONFIG_X86_64 4046#ifdef CONFIG_X86_64
4044 rdmsrl(MSR_FS_BASE, a); 4047 rdmsrl(MSR_FS_BASE, a);
4045 vmcs_writel(HOST_FS_BASE, a); /* 22.2.4 */ 4048 vmcs_writel(HOST_FS_BASE, a); /* 22.2.4 */
@@ -6399,6 +6402,52 @@ static void vmx_complete_atomic_exit(struct vcpu_vmx *vmx)
6399 } 6402 }
6400} 6403}
6401 6404
6405static void vmx_handle_external_intr(struct kvm_vcpu *vcpu)
6406{
6407 u32 exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
6408
6409 /*
6410 * If external interrupt exists, IF bit is set in rflags/eflags on the
6411 * interrupt stack frame, and interrupt will be enabled on a return
6412 * from interrupt handler.
6413 */
6414 if ((exit_intr_info & (INTR_INFO_VALID_MASK | INTR_INFO_INTR_TYPE_MASK))
6415 == (INTR_INFO_VALID_MASK | INTR_TYPE_EXT_INTR)) {
6416 unsigned int vector;
6417 unsigned long entry;
6418 gate_desc *desc;
6419 struct vcpu_vmx *vmx = to_vmx(vcpu);
6420#ifdef CONFIG_X86_64
6421 unsigned long tmp;
6422#endif
6423
6424 vector = exit_intr_info & INTR_INFO_VECTOR_MASK;
6425 desc = (gate_desc *)vmx->host_idt_base + vector;
6426 entry = gate_offset(*desc);
6427 asm volatile(
6428#ifdef CONFIG_X86_64
6429 "mov %%" _ASM_SP ", %[sp]\n\t"
6430 "and $0xfffffffffffffff0, %%" _ASM_SP "\n\t"
6431 "push $%c[ss]\n\t"
6432 "push %[sp]\n\t"
6433#endif
6434 "pushf\n\t"
6435 "orl $0x200, (%%" _ASM_SP ")\n\t"
6436 __ASM_SIZE(push) " $%c[cs]\n\t"
6437 "call *%[entry]\n\t"
6438 :
6439#ifdef CONFIG_X86_64
6440 [sp]"=&r"(tmp)
6441#endif
6442 :
6443 [entry]"r"(entry),
6444 [ss]"i"(__KERNEL_DS),
6445 [cs]"i"(__KERNEL_CS)
6446 );
6447 } else
6448 local_irq_enable();
6449}
6450
6402static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx) 6451static void vmx_recover_nmi_blocking(struct vcpu_vmx *vmx)
6403{ 6452{
6404 u32 exit_intr_info; 6453 u32 exit_intr_info;
@@ -7041,7 +7090,7 @@ static void prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
7041 * Other fields are different per CPU, and will be set later when 7090 * Other fields are different per CPU, and will be set later when
7042 * vmx_vcpu_load() is called, and when vmx_save_host_state() is called. 7091 * vmx_vcpu_load() is called, and when vmx_save_host_state() is called.
7043 */ 7092 */
7044 vmx_set_constant_host_state(); 7093 vmx_set_constant_host_state(vmx);
7045 7094
7046 /* 7095 /*
7047 * HOST_RSP is normally set correctly in vmx_vcpu_run() just before 7096 * HOST_RSP is normally set correctly in vmx_vcpu_run() just before
@@ -7718,6 +7767,7 @@ static struct kvm_x86_ops vmx_x86_ops = {
7718 .set_tdp_cr3 = vmx_set_cr3, 7767 .set_tdp_cr3 = vmx_set_cr3,
7719 7768
7720 .check_intercept = vmx_check_intercept, 7769 .check_intercept = vmx_check_intercept,
7770 .handle_external_intr = vmx_handle_external_intr,
7721}; 7771};
7722 7772
7723static int __init vmx_init(void) 7773static int __init vmx_init(void)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index ae9744d03c83..1562671a8e18 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5820,7 +5820,9 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
5820 5820
5821 vcpu->mode = OUTSIDE_GUEST_MODE; 5821 vcpu->mode = OUTSIDE_GUEST_MODE;
5822 smp_wmb(); 5822 smp_wmb();
5823 local_irq_enable(); 5823
5824 /* Interrupt is enabled by handle_external_intr() */
5825 kvm_x86_ops->handle_external_intr(vcpu);
5824 5826
5825 ++vcpu->stat.exits; 5827 ++vcpu->stat.exits;
5826 5828