Instead of acking an interrupt when we *think* the guest is ready for it, and then juggling it around in subarch-specific registers if it isn't (e.g. page fault while trying to inject the interrupt), separate the injection and ack.
Subarh specific code now provides two hooks: ->queue_interrupt() will attempt to inject the interrupt, and ->interrupt_injected() will check whether this actually succeeded (upon which common code will ack the interrupt). This allows much simpler management of pending interrupts. Signed-off-by: Avi Kivity <[EMAIL PROTECTED]> --- drivers/kvm/svm.c | 113 ++++++++------------------------------------ drivers/kvm/vmx.c | 137 +++++++++-------------------------------------------- drivers/kvm/x86.c | 79 ++++++++++++++++++++++--------- drivers/kvm/x86.h | 11 ++-- 4 files changed, 103 insertions(+), 237 deletions(-) diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c index 10146a8..9fb9ee1 100644 --- a/drivers/kvm/svm.c +++ b/drivers/kvm/svm.c @@ -46,8 +46,6 @@ MODULE_LICENSE("GPL"); #define SVM_FEATURE_LBRV (1 << 1) #define SVM_DEATURE_SVML (1 << 2) -static void kvm_reput_irq(struct vcpu_svm *svm); - static inline struct vcpu_svm *to_svm(struct kvm_vcpu *vcpu) { return container_of(vcpu, struct vcpu_svm, vcpu); @@ -838,16 +836,6 @@ static int svm_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg) return -EOPNOTSUPP; } -static int svm_get_irq(struct kvm_vcpu *vcpu) -{ - struct vcpu_svm *svm = to_svm(vcpu); - u32 exit_int_info = svm->vmcb->control.exit_int_info; - - if (is_external_interrupt(exit_int_info)) - return exit_int_info & SVM_EVTINJ_VEC_MASK; - return -1; -} - static void load_host_msrs(struct kvm_vcpu *vcpu) { #ifdef CONFIG_X86_64 @@ -1245,8 +1233,6 @@ static int handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); u32 exit_code = svm->vmcb->control.exit_code; - kvm_reput_irq(svm); - if (svm->vmcb->control.exit_code == SVM_EXIT_ERR) { kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY; kvm_run->fail_entry.hardware_entry_failure_reason @@ -1304,102 +1290,43 @@ static inline void svm_inject_irq(struct vcpu_svm *svm, int irq) ((/*control->int_vector >> 4*/ 0xf) << V_INTR_PRIO_SHIFT); } -static void svm_set_irq(struct kvm_vcpu *vcpu, int irq) +static void svm_set_tpr_threshold(struct kvm_vcpu *vcpu) { - struct vcpu_svm *svm = to_svm(vcpu); - - svm_inject_irq(svm, irq); } -static void svm_intr_assist(struct kvm_vcpu *vcpu) +static bool svm_queue_interrupt(struct kvm_vcpu *vcpu, unsigned vector) { struct vcpu_svm *svm = to_svm(vcpu); struct vmcb *vmcb = svm->vmcb; - int intr_vector = -1; - struct kvm_pending_irq irq; - unsigned tpr_threshold; - - if ((vmcb->control.exit_int_info & SVM_EVTINJ_VALID) && - ((vmcb->control.exit_int_info & SVM_EVTINJ_TYPE_MASK) == 0)) { - intr_vector = vmcb->control.exit_int_info & - SVM_EVTINJ_VEC_MASK; - vmcb->control.exit_int_info = 0; - svm_inject_irq(svm, intr_vector); - return; - } if (vmcb->control.int_ctl & V_IRQ_MASK) - return; - - if (!kvm_cpu_get_interrupt(vcpu, &irq, &tpr_threshold)) - return; + return false; if (!(vmcb->save.rflags & X86_EFLAGS_IF) || (vmcb->control.int_state & SVM_INTERRUPT_SHADOW_MASK) || - (vmcb->control.event_inj & SVM_EVTINJ_VALID)) { + vcpu->exception.pending) { /* unable to deliver irq, set pending irq */ vmcb->control.intercept |= (1ULL << INTERCEPT_VINTR); svm_inject_irq(svm, 0x0); - return; - } - /* Okay, we can deliver the interrupt: grab it and update PIC state. */ - irq.ack(vcpu, irq.info); - intr_vector = irq.vector; - svm_inject_irq(svm, intr_vector); - kvm_timer_intr_post(vcpu, intr_vector); -} - -static void kvm_reput_irq(struct vcpu_svm *svm) -{ - struct vmcb_control_area *control = &svm->vmcb->control; - - if ((control->int_ctl & V_IRQ_MASK) - && !irqchip_in_kernel(svm->vcpu.kvm)) { - control->int_ctl &= ~V_IRQ_MASK; - push_irq(&svm->vcpu, control->int_vector); + return false; } - - svm->vcpu.interrupt_window_open = - !(control->int_state & SVM_INTERRUPT_SHADOW_MASK); -} - -static void svm_do_inject_vector(struct vcpu_svm *svm) -{ - struct kvm_vcpu *vcpu = &svm->vcpu; - int word_index = __ffs(vcpu->irq_summary); - int bit_index = __ffs(vcpu->irq_pending[word_index]); - int irq = word_index * BITS_PER_LONG + bit_index; - - clear_bit(bit_index, &vcpu->irq_pending[word_index]); - if (!vcpu->irq_pending[word_index]) - clear_bit(word_index, &vcpu->irq_summary); - svm_inject_irq(svm, irq); + svm_inject_irq(svm, vector); + return true; } -static void do_interrupt_requests(struct kvm_vcpu *vcpu, - struct kvm_run *kvm_run) +static bool svm_interrupt_injected(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); struct vmcb_control_area *control = &svm->vmcb->control; + bool injected; - svm->vcpu.interrupt_window_open = - (!(control->int_state & SVM_INTERRUPT_SHADOW_MASK) && - (svm->vmcb->save.rflags & X86_EFLAGS_IF)); - - if (svm->vcpu.interrupt_window_open && svm->vcpu.irq_summary) - /* - * If interrupts enabled, and not blocked by sti or mov ss. Good. - */ - svm_do_inject_vector(svm); + injected = !(control->int_ctl & V_IRQ_MASK) + && !(control->intercept & (1ULL << INTERCEPT_VINTR)) + && !(control->exit_int_info & SVM_EXITINTINFO_VALID); - /* - * Interrupts blocked. Wait for unblock. - */ - if (!svm->vcpu.interrupt_window_open && - (svm->vcpu.irq_summary || kvm_run->request_interrupt_window)) - control->intercept |= 1ULL << INTERCEPT_VINTR; - else - control->intercept &= ~(1ULL << INTERCEPT_VINTR); + if (!injected) + control->int_ctl &= ~V_IRQ_MASK; + return injected; } static int svm_set_tss_addr(struct kvm *kvm, unsigned int addr) @@ -1586,6 +1513,8 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) stgi(); svm->next_rip = 0; + svm->vcpu.interrupt_window_open = + !(svm->vmcb->control.int_state & SVM_INTERRUPT_SHADOW_MASK); } static void svm_set_cr3(struct kvm_vcpu *vcpu, unsigned long root) @@ -1676,13 +1605,11 @@ static struct kvm_x86_ops svm_x86_ops = { .handle_exit = handle_exit, .skip_emulated_instruction = skip_emulated_instruction, .patch_hypercall = svm_patch_hypercall, - .get_irq = svm_get_irq, - .set_irq = svm_set_irq, .queue_exception = svm_queue_exception, .exception_injected = svm_exception_injected, - .inject_pending_irq = svm_intr_assist, - .inject_pending_vectors = do_interrupt_requests, - + .queue_interrupt = svm_queue_interrupt, + .interrupt_injected = svm_interrupt_injected, + .set_tpr_threshold = svm_set_tpr_threshold, .set_tss_addr = svm_set_tss_addr, }; diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index b788c6b..c180c04 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c @@ -861,21 +861,6 @@ static int set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_debug_guest *dbg) return 0; } -static int vmx_get_irq(struct kvm_vcpu *vcpu) -{ - struct vcpu_vmx *vmx = to_vmx(vcpu); - u32 idtv_info_field; - - idtv_info_field = vmx->idt_vectoring_info; - if (idtv_info_field & INTR_INFO_VALID_MASK) { - if (is_external_interrupt(idtv_info_field)) - return idtv_info_field & VECTORING_INFO_VECTOR_MASK; - else - printk(KERN_DEBUG "pending exception: not handled yet\n"); - } - return -1; -} - static __init int cpu_has_kvm_support(void) { unsigned long ecx = cpuid_ecx(1); @@ -1732,48 +1717,6 @@ static void vmx_inject_irq(struct kvm_vcpu *vcpu, int irq) irq | INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK); } -static void kvm_do_inject_irq(struct kvm_vcpu *vcpu) -{ - int word_index = __ffs(vcpu->irq_summary); - int bit_index = __ffs(vcpu->irq_pending[word_index]); - int irq = word_index * BITS_PER_LONG + bit_index; - - clear_bit(bit_index, &vcpu->irq_pending[word_index]); - if (!vcpu->irq_pending[word_index]) - clear_bit(word_index, &vcpu->irq_summary); - vmx_inject_irq(vcpu, irq); -} - - -static void do_interrupt_requests(struct kvm_vcpu *vcpu, - struct kvm_run *kvm_run) -{ - u32 cpu_based_vm_exec_control; - - vcpu->interrupt_window_open = - ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) && - (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0); - - if (vcpu->interrupt_window_open && - vcpu->irq_summary && - !(vmcs_read32(VM_ENTRY_INTR_INFO_FIELD) & INTR_INFO_VALID_MASK)) - /* - * If interrupts enabled, and not blocked by sti or mov ss. Good. - */ - kvm_do_inject_irq(vcpu); - - cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); - if (!vcpu->interrupt_window_open && - (vcpu->irq_summary || kvm_run->request_interrupt_window)) - /* - * Interrupts blocked. Wait for unblock. - */ - cpu_based_vm_exec_control |= CPU_BASED_VIRTUAL_INTR_PENDING; - else - cpu_based_vm_exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING; - vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); -} - static int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr) { int ret; @@ -2218,12 +2161,12 @@ static void vmx_flush_tlb(struct kvm_vcpu *vcpu) { } -static void update_tpr_threshold(struct kvm_vcpu *vcpu, unsigned tpr_threshold) +static void update_tpr_threshold(struct kvm_vcpu *vcpu) { if (!vm_need_tpr_shadow(vcpu->kvm)) return; - vmcs_write32(TPR_THRESHOLD, tpr_threshold >> 4); + vmcs_write32(TPR_THRESHOLD, vcpu->tpr_threshold >> 4); } static void enable_irq_window(struct kvm_vcpu *vcpu) @@ -2235,65 +2178,30 @@ static void enable_irq_window(struct kvm_vcpu *vcpu) vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control); } -static void vmx_intr_assist(struct kvm_vcpu *vcpu) +static bool vmx_queue_interrupt(struct kvm_vcpu *vcpu, unsigned vector) { - struct vcpu_vmx *vmx = to_vmx(vcpu); - u32 idtv_info_field, intr_info_field; - int has_ext_irq, interrupt_window_open; - struct kvm_pending_irq irq; - int vector; - unsigned tpr_threshold; - - has_ext_irq = kvm_cpu_get_interrupt(vcpu, &irq, &tpr_threshold); - - update_tpr_threshold(vcpu, tpr_threshold); - - intr_info_field = vmcs_read32(VM_ENTRY_INTR_INFO_FIELD); - idtv_info_field = vmx->idt_vectoring_info; - if (intr_info_field & INTR_INFO_VALID_MASK) { - if (idtv_info_field & INTR_INFO_VALID_MASK) { - /* TODO: fault when IDT_Vectoring */ - printk(KERN_ERR "Fault when IDT_Vectoring\n"); - } - if (has_ext_irq) - enable_irq_window(vcpu); - return; - } - if (unlikely(idtv_info_field & INTR_INFO_VALID_MASK)) { - if ((idtv_info_field & VECTORING_INFO_TYPE_MASK) - == INTR_TYPE_EXT_INTR - && vcpu->rmode.active) { - u8 vect = idtv_info_field & VECTORING_INFO_VECTOR_MASK; - - vmx_inject_irq(vcpu, vect); - if (unlikely(has_ext_irq)) - enable_irq_window(vcpu); - return; - } - - vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field); - vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, - vmcs_read32(VM_EXIT_INSTRUCTION_LEN)); + int interrupt_window_open; - if (unlikely(idtv_info_field & INTR_INFO_DELIEVER_CODE_MASK)) - vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE, - vmcs_read32(IDT_VECTORING_ERROR_CODE)); - if (unlikely(has_ext_irq)) - enable_irq_window(vcpu); - return; - } - if (!has_ext_irq) - return; interrupt_window_open = ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) && (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0); - if (interrupt_window_open) { - irq.ack(vcpu, irq.info); - vector = irq.vector; + + if (!vcpu->exception.pending && interrupt_window_open) { vmx_inject_irq(vcpu, vector); - kvm_timer_intr_post(vcpu, vector); - } else + return true; + } else { enable_irq_window(vcpu); + return false; + } +} + +static bool vmx_interrupt_injected(struct kvm_vcpu *vcpu) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + bool valid = vmx->idt_vectoring_info & VECTORING_INFO_VALID_MASK; + unsigned type = vmx->idt_vectoring_info & VECTORING_INFO_TYPE_MASK; + + return !(valid && (type == INTR_TYPE_EXT_INTR)); } /* @@ -2597,12 +2505,11 @@ static struct kvm_x86_ops vmx_x86_ops = { .handle_exit = kvm_handle_exit, .skip_emulated_instruction = skip_emulated_instruction, .patch_hypercall = vmx_patch_hypercall, - .get_irq = vmx_get_irq, - .set_irq = vmx_inject_irq, .queue_exception = vmx_queue_exception, .exception_injected = vmx_exception_injected, - .inject_pending_irq = vmx_intr_assist, - .inject_pending_vectors = do_interrupt_requests, + .queue_interrupt = vmx_queue_interrupt, + .interrupt_injected = vmx_interrupt_injected, + .set_tpr_threshold = update_tpr_threshold, .set_tss_addr = vmx_set_tss_addr, }; diff --git a/drivers/kvm/x86.c b/drivers/kvm/x86.c index 6deb052..a196d5d 100644 --- a/drivers/kvm/x86.c +++ b/drivers/kvm/x86.c @@ -2365,9 +2365,51 @@ static void post_kvm_run_save(struct kvm_vcpu *vcpu, vcpu->irq_summary == 0); } +static void ack_vector(struct kvm_vcpu *vcpu, unsigned vector) +{ + unsigned word_index = vector / BITS_PER_LONG; + unsigned bit_index = vector % BITS_PER_LONG; + + clear_bit(bit_index, &vcpu->irq_pending[word_index]); + if (!vcpu->irq_pending[word_index]) + clear_bit(word_index, &vcpu->irq_summary); +} + +static bool get_pending_irq(struct kvm_vcpu *vcpu, struct kvm_pending_irq *irq, + unsigned *tpr_threshold) +{ + unsigned word_index, bit_index; + + if (irqchip_in_kernel(vcpu->kvm)) + return kvm_cpu_get_interrupt(vcpu, irq, tpr_threshold); + + *tpr_threshold = 0; + + if (!vcpu->irq_summary) + return false; + + word_index = __ffs(vcpu->irq_summary); + bit_index = __ffs(vcpu->irq_pending[word_index]); + irq->vector = word_index * BITS_PER_LONG + bit_index; + irq->ack = ack_vector; + irq->info = irq->vector; + return true; +} + +static void update_tpr_threshold(struct kvm_vcpu *vcpu, unsigned tpr_threshold) +{ + if (tpr_threshold != vcpu->tpr_threshold) { + vcpu->tpr_threshold = tpr_threshold; + kvm_x86_ops->set_tpr_threshold(vcpu); + } +} + static int __vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { int r; + bool irq_pending; + struct kvm_pending_irq irq; + unsigned tpr_threshold; if (unlikely(vcpu->mp_state == VCPU_MP_STATE_SIPI_RECEIVED)) { pr_debug("vcpu %d received sipi with vector # %x\n", @@ -2408,10 +2450,12 @@ again: if (vcpu->exception.pending) __queue_exception(vcpu); - else if (irqchip_in_kernel(vcpu->kvm)) - kvm_x86_ops->inject_pending_irq(vcpu); - else - kvm_x86_ops->inject_pending_vectors(vcpu, kvm_run); + + irq_pending = get_pending_irq(vcpu, &irq, &tpr_threshold); + if (irq_pending) + irq_pending = kvm_x86_ops->queue_interrupt(vcpu, irq.vector); + + update_tpr_threshold(vcpu, tpr_threshold); vcpu->guest_mode = 1; kvm_guest_enter(); @@ -2450,6 +2494,11 @@ again: if (vcpu->exception.pending && kvm_x86_ops->exception_injected(vcpu)) vcpu->exception.pending = false; + if (irq_pending && kvm_x86_ops->interrupt_injected(vcpu)) { + irq.ack(vcpu, irq.info); + kvm_timer_intr_post(vcpu, irq.vector); + } + r = kvm_x86_ops->handle_exit(kvm_run, vcpu); if (r > 0) { @@ -2623,7 +2672,6 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) { struct descriptor_table dt; - int pending_vec; vcpu_load(vcpu); @@ -2653,14 +2701,10 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, sregs->efer = vcpu->shadow_efer; sregs->apic_base = kvm_get_apic_base(vcpu); - if (irqchip_in_kernel(vcpu->kvm)) { + if (irqchip_in_kernel(vcpu->kvm)) memset(sregs->interrupt_bitmap, 0, sizeof sregs->interrupt_bitmap); - pending_vec = kvm_x86_ops->get_irq(vcpu); - if (pending_vec >= 0) - set_bit(pending_vec, - (unsigned long *)sregs->interrupt_bitmap); - } else + else memcpy(sregs->interrupt_bitmap, vcpu->irq_pending, sizeof sregs->interrupt_bitmap); @@ -2679,7 +2723,7 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) { int mmu_reset_needed = 0; - int i, pending_vec, max_bits; + int i; struct descriptor_table dt; vcpu_load(vcpu); @@ -2724,17 +2768,6 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, for (i = 0; i < ARRAY_SIZE(vcpu->irq_pending); ++i) if (vcpu->irq_pending[i]) __set_bit(i, &vcpu->irq_summary); - } else { - max_bits = (sizeof sregs->interrupt_bitmap) << 3; - pending_vec = find_first_bit( - (const unsigned long *)sregs->interrupt_bitmap, - max_bits); - /* Only pending external irq is handled here */ - if (pending_vec < max_bits) { - kvm_x86_ops->set_irq(vcpu, pending_vec); - pr_debug("Set back pending irq %d\n", - pending_vec); - } } set_segment(vcpu, &sregs->cs, VCPU_SREG_CS); diff --git a/drivers/kvm/x86.h b/drivers/kvm/x86.h index fb48b2f..52f3199 100644 --- a/drivers/kvm/x86.h +++ b/drivers/kvm/x86.h @@ -136,6 +136,8 @@ struct kvm_vcpu { struct kvm_pio_request pio; void *pio_data; + unsigned tpr_threshold; + struct kvm_queued_exception { bool pending; bool has_error_code; @@ -217,15 +219,12 @@ struct kvm_x86_ops { void (*skip_emulated_instruction)(struct kvm_vcpu *vcpu); void (*patch_hypercall)(struct kvm_vcpu *vcpu, unsigned char *hypercall_addr); - int (*get_irq)(struct kvm_vcpu *vcpu); - void (*set_irq)(struct kvm_vcpu *vcpu, int vec); void (*queue_exception)(struct kvm_vcpu *vcpu, unsigned nr, bool has_error_code, u32 error_code); bool (*exception_injected)(struct kvm_vcpu *vcpu); - void (*inject_pending_irq)(struct kvm_vcpu *vcpu); - void (*inject_pending_vectors)(struct kvm_vcpu *vcpu, - struct kvm_run *run); - + bool (*queue_interrupt)(struct kvm_vcpu *vcpu, unsigned vector); + bool (*interrupt_injected)(struct kvm_vcpu *vcpu); + void (*set_tpr_threshold)(struct kvm_vcpu *vcpu); int (*set_tss_addr)(struct kvm *kvm, unsigned int addr); }; -- 1.5.3 ------------------------------------------------------------------------- SF.Net email is sponsored by: The Future of Linux Business White Paper from Novell. From the desktop to the data center, Linux is going mainstream. Let it simplify your IT future. http://altfarm.mediaplex.com/ad/ck/8857-50307-18918-4 _______________________________________________ kvm-devel mailing list kvm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/kvm-devel