Commit 851c1a18 authored by Liran Alon's avatar Liran Alon Committed by Radim Krčmář

KVM: nVMX: Fix injection to L2 when L1 don't intercept external-interrupts

Before each vmentry to guest, vcpu_enter_guest() calls sync_pir_to_irr()
which calls vmx_hwapic_irr_update() to update RVI.
Currently, vmx_hwapic_irr_update() contains a tweak in case it is called
when CPU is running L2 and L1 don't intercept external-interrupts.
In that case, code injects interrupt directly into L2 instead of
updating RVI.

Besides being hacky (wouldn't expect function updating RVI to also
inject interrupt), it also doesn't handle this case correctly.
The code contains several issues:
1. When code calls kvm_queue_interrupt() it just passes it max_irr which
represents the highest IRR currently pending in L1 LAPIC.
This is problematic as interrupt was injected to guest but it's bit is
still set in LAPIC IRR instead of being cleared from IRR and set in ISR.
2. Code doesn't check if LAPIC PPR is set to accept an interrupt of
max_irr priority. It just checks if interrupts are enabled in guest with
vmx_interrupt_allowed().

To fix the above issues:
1. Simplify vmx_hwapic_irr_update() to just update RVI.
Note that this shouldn't happen when CPU is running L2
(See comment in code).
2. Since now vmx_hwapic_irr_update() only does logic for L1
virtual-interrupt-delivery, inject_pending_event() should be the
one responsible for injecting the interrupt directly into L2.
Therefore, change kvm_cpu_has_injectable_intr() to check L1
LAPIC when CPU is running L2.
3. Change vmx_sync_pir_to_irr() to set KVM_REQ_EVENT when L1
has a pending injectable interrupt.

Fixes: 963fee16 ("KVM: nVMX: Fix virtual interrupt delivery
injection")
Signed-off-by: default avatarLiran Alon <liran.alon@oracle.com>
Reviewed-by: default avatarNikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: default avatarKrish Sadhukhan <krish.sadhukhan@oracle.com>
Reviewed-by: default avatarLiam Merwick <liam.merwick@oracle.com>
Signed-off-by: default avatarLiam Merwick <liam.merwick@oracle.com>
Signed-off-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Signed-off-by: default avatarRadim Krčmář <rkrcmar@redhat.com>
parent f27a85c4
...@@ -79,7 +79,7 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v) ...@@ -79,7 +79,7 @@ int kvm_cpu_has_injectable_intr(struct kvm_vcpu *v)
if (kvm_cpu_has_extint(v)) if (kvm_cpu_has_extint(v))
return 1; return 1;
if (kvm_vcpu_apicv_active(v)) if (!is_guest_mode(v) && kvm_vcpu_apicv_active(v))
return 0; return 0;
return kvm_apic_has_interrupt(v) != -1; /* LAPIC */ return kvm_apic_has_interrupt(v) != -1; /* LAPIC */
......
...@@ -8948,30 +8948,16 @@ static void vmx_set_rvi(int vector) ...@@ -8948,30 +8948,16 @@ static void vmx_set_rvi(int vector)
static void vmx_hwapic_irr_update(struct kvm_vcpu *vcpu, int max_irr) static void vmx_hwapic_irr_update(struct kvm_vcpu *vcpu, int max_irr)
{ {
if (!is_guest_mode(vcpu)) {
vmx_set_rvi(max_irr);
return;
}
if (max_irr == -1)
return;
/*
* In guest mode. If a vmexit is needed, vmx_check_nested_events
* handles it.
*/
if (nested_exit_on_intr(vcpu))
return;
/* /*
* Else, fall back to pre-APICv interrupt injection since L2 * When running L2, updating RVI is only relevant when
* is run without virtual interrupt delivery. * vmcs12 virtual-interrupt-delivery enabled.
* However, it can be enabled only when L1 also
* intercepts external-interrupts and in that case
* we should not update vmcs02 RVI but instead intercept
* interrupt. Therefore, do nothing when running L2.
*/ */
if (!kvm_event_needs_reinjection(vcpu) && if (!is_guest_mode(vcpu))
vmx_interrupt_allowed(vcpu)) { vmx_set_rvi(max_irr);
kvm_queue_interrupt(vcpu, max_irr, false);
vmx_inject_irq(vcpu);
}
} }
static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
...@@ -8995,9 +8981,16 @@ static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) ...@@ -8995,9 +8981,16 @@ static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)
* If we are running L2 and L1 has a new pending interrupt * If we are running L2 and L1 has a new pending interrupt
* which can be injected, we should re-evaluate * which can be injected, we should re-evaluate
* what should be done with this new L1 interrupt. * what should be done with this new L1 interrupt.
* If L1 intercepts external-interrupts, we should
* exit from L2 to L1. Otherwise, interrupt should be
* delivered directly to L2.
*/ */
if (is_guest_mode(vcpu) && max_irr_updated) if (is_guest_mode(vcpu) && max_irr_updated) {
if (nested_exit_on_intr(vcpu))
kvm_vcpu_exiting_guest_mode(vcpu); kvm_vcpu_exiting_guest_mode(vcpu);
else
kvm_make_request(KVM_REQ_EVENT, vcpu);
}
} else { } else {
max_irr = kvm_lapic_find_highest_irr(vcpu); max_irr = kvm_lapic_find_highest_irr(vcpu);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment