R&D/Hypervisor

[KVM] Interrupt

sunshout 2014. 6. 25. 16:36

KVM Interrupt related structures




KVM Interrupt Injection to Guest





IOAPIC based interrupt delivery



MSI interrupt delivery


kvm_irq_delivery_to_apic(kvm, NULL, &irq, NULL);

- pass no kvm_lapic *src

- pass no dest_map

format : int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,

        struct kvm_lapic_irq *irq, unsigned long *dest_map)


Jul  7 23:10:39 gaus02-m kernel: [49646.052828] # SON MSI kvm_set_msi_inatomic

Jul  7 23:10:39 gaus02-m kernel: [49646.052833] ########################

Jul  7 23:10:39 gaus02-m kernel: [49646.052835] dest_id  : 0

Jul  7 23:10:39 gaus02-m kernel: [49646.052837] vector   : 65

Jul  7 23:10:39 gaus02-m kernel: [49646.052838] dest_mode: 0

Jul  7 23:10:39 gaus02-m kernel: [49646.052840] trig_mode: 0

Jul  7 23:10:39 gaus02-m kernel: [49646.052841] del_mode : 0


Interrupt Command Register (x86)



Vector [7:0] : The vector identifying the interrupt being sent.

Delivery Mode [10:8] : Specifies how the APICs listed in the destination filed should act upon reception of the interrupt

- 000 (Fixed) : Delivery the interrupt to all processors listed in the destination field according to the information provided in the ICR.


- 001 (Lowest Priority) : Samed as fixed mode, except that the interrupt is deliveryed to the processor executing at the lowest priority among the set of processors listed in the destination


- 010 (SMI) : Only the edge trigger mode is allowed. The vector field must be programmed to 00B


- 011 (Reserved) :


- 100 (NMI) : Delivery the interrupt as an NMI interrupt to all processors listed in the destination field. The vector information is ignored. NMI is treated as an edge triggered interrupt even if programmed otherwise


- 101 (INIT) : Delivers the interrupt as an INIT signal to all processors listed in the destination field. As a result, all addressed APICs will assume their INIT state. As in the case of NMI, the vector information is ignored, and INIT is treated as an edge triggered interrupt even if programmed otherwise


- 101 (INIT Level De-assert)


- 110 (Start-Up) : Sends a special message between processors in a multiple-processor system.


Destination

- destination shorthand field 가 "dest field"일 때만 적용됨

- destination field 가 "physical" 일 때 bit[56-59] = APIC ID

- destination field 가 "logical" 일 때 8bit destination field 는 DFR, LDR 에 의존함  


@ arch/x86/kvm/lapic.c


/*

 * Add a pending IRQ into lapic.

 * Return 1 if successfully added and 0 if discarded.

 */

static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,

                 int vector, int level, int trig_mode,

                 unsigned long *dest_map)

{

    int result = 0;

    struct kvm_vcpu *vcpu = apic->vcpu;


    switch (delivery_mode) {

    case APIC_DM_LOWEST:

        vcpu->arch.apic_arb_prio++;

    case APIC_DM_FIXED:

        /* FIXME add logic for vcpu on reset */

        if (unlikely(!apic_enabled(apic)))

            break;


        result = 1;


        if (dest_map)

            __set_bit(vcpu->vcpu_id, dest_map);


        if (kvm_x86_ops->deliver_posted_interrupt)

            kvm_x86_ops->deliver_posted_interrupt(vcpu, vector);

        else {

            apic_set_irr(vector, apic);


            kvm_make_request(KVM_REQ_EVENT, vcpu);

            kvm_vcpu_kick(vcpu);

        }

        trace_kvm_apic_accept_irq(vcpu->vcpu_id, delivery_mode,

                      trig_mode, vector, false);

        break;


    case APIC_DM_REMRD:

        result = 1;

        vcpu->arch.pv.pv_unhalted = 1;

        kvm_make_request(KVM_REQ_EVENT, vcpu);

        kvm_vcpu_kick(vcpu);

        break;


    case APIC_DM_SMI:

        apic_debug("Ignoring guest SMI\n");

        break;


    case APIC_DM_NMI:

        result = 1;

        kvm_inject_nmi(vcpu);

        kvm_vcpu_kick(vcpu);

        break;


    case APIC_DM_INIT:

        if (!trig_mode || level) {

            result = 1;

            /* assumes that there are only KVM_APIC_INIT/SIPI */

            apic->pending_events = (1UL << KVM_APIC_INIT);

            /* make sure pending_events is visible before sending

             * the request */

            smp_wmb();

            kvm_make_request(KVM_REQ_EVENT, vcpu);

            kvm_vcpu_kick(vcpu);

        } else {

            apic_debug("Ignoring de-assert INIT to vcpu %d\n",

                   vcpu->vcpu_id);

        }

        break;


    case APIC_DM_STARTUP:

        apic_debug("SIPI to vcpu %d vector 0x%02x\n",

               vcpu->vcpu_id, vector);

        result = 1;

        apic->sipi_vector = vector;

        /* make sure sipi_vector is visible for the receiver */

        smp_wmb();

        set_bit(KVM_APIC_SIPI, &apic->pending_events);

        kvm_make_request(KVM_REQ_EVENT, vcpu);

        kvm_vcpu_kick(vcpu);

        break;


    case APIC_DM_EXTINT:

        /*

         * Should only be called by kvm_apic_local_deliver() with LVT0,

         * before NMI watchdog was enabled. Already handled by

         * kvm_apic_accept_pic_intr().

         */

        break;


    default:

        printk(KERN_ERR "TODO: unsupported delivery mode %x\n",

               delivery_mode);

        break;

    }

    return result;

}



Posted Interrupt

Posted Interrupt allows APIC interrupts to inject into guest directly without any vmexit.

- When delivering a interrupt to guest, if target vcpu is running, update Posted-interrupt request bitmap and send a notification event to the vcpu. Then the vcpu will handle this interrupt automatically, without any software involvement.

- If target vcpu is not running or there already a notification event pending in the vcpu, do nothing. The interrupt will be handled by next vm entry.


 @arch/x86/kvm/vmx.c

/*

 * Send interrupt to vcpu via posted interrupt way.

 * 1. If target vcpu is running(non-root mode), send posted interrupt

 * notification to vcpu and hardware will sync PIR to vIRR atomically.

 * 2. If target vcpu isn't running(root mode), kick it to pick up the

 * interrupt from PIR in next vmentry.

 */

static void vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu, int vector)

{

    struct vcpu_vmx *vmx = to_vmx(vcpu);

    int r;

    /* posted interrupt discriptor 가 존재하지 않으면 리턴 */

    if (pi_test_and_set_pir(vector, &vmx->pi_desc))

        return;

   

    r = pi_test_and_set_on(&vmx->pi_desc);

    kvm_make_request(KVM_REQ_EVENT, vcpu);

#ifdef CONFIG_SMP

    /* interrupt를 전달하고자 하는 vCPU가 Guest Mode로 동작하고 있으면 IPI를 통해서 전달함 */

    if (!r && (vcpu->mode == IN_GUEST_MODE))

        apic->send_IPI_mask(get_cpu_mask(vcpu->cpu),

                POSTED_INTR_VECTOR);

    else

#endif

        kvm_vcpu_kick(vcpu);

}



RVI: Requesting Virtual Interrupt

SVI: Serving Virtual Interrupt

VISR: Virtual Interrupt-Service Register

VPPR: Virtual Processor-Priority Register

VIRR: Virtual Interrupt-Request Register



References:

[x86] interrupt : http://sunshout.tistory.com/1613

http://blog.csdn.net/yearn520/article/details/6663532

http://www.spinics.net/lists/kvm/msg88742.html