If APICv is disabled for this vCPU, assigned devices may still attempt to post interrupts. In that case, we need to cancel the vmentry and deliver the interrupt with KVM_REQ_EVENT. Extend the existing code that handles injection of L1 interrupts into L2 to cover this case as well.
vmx_hwapic_irr_update is only called when APICv is active so it would be confusing to add a check for vcpu->arch.apicv_active in there. Instead, just use vmx_set_rvi directly in vmx_sync_pir_to_irr.
Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini pbonzini@redhat.com --- arch/x86/kvm/vmx/vmx.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index ba66c171d951..cccf1eab58ac 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -6264,7 +6264,7 @@ static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) int max_irr; bool max_irr_updated;
- if (KVM_BUG_ON(!vcpu->arch.apicv_active, vcpu->kvm)) + if (KVM_BUG_ON(!enable_apicv, vcpu->kvm)) return -EIO;
if (pi_test_on(&vmx->pi_desc)) { @@ -6276,20 +6276,31 @@ static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu) smp_mb__after_atomic(); max_irr_updated = kvm_apic_update_irr(vcpu, vmx->pi_desc.pir, &max_irr); - - /* - * If we are running L2 and L1 has a new pending interrupt - * which can be injected, this may cause a vmexit or it may - * be injected into L2. Either way, this interrupt will be - * processed via KVM_REQ_EVENT, not RVI, because we do not use - * virtual interrupt delivery to inject L1 interrupts into L2. - */ - if (is_guest_mode(vcpu) && max_irr_updated) - kvm_make_request(KVM_REQ_EVENT, vcpu); } else { max_irr = kvm_lapic_find_highest_irr(vcpu); + max_irr_updated = false; } - vmx_hwapic_irr_update(vcpu, max_irr); + + /* + * If virtual interrupt delivery is not in use, the interrupt + * will be processed via KVM_REQ_EVENT, not RVI. This can happen + * in two cases: + * + * 1) If we are running L2 and L1 has a new pending interrupt + * which can be injected, this may cause a vmexit or it may + * be injected into L2. We do not use virtual interrupt + * delivery to inject L1 interrupts into L2. + * + * 2) If APICv is disabled for this vCPU, assigned devices may + * still attempt to post interrupts. The posted interrupt + * vector will cause a vmexit and the subsequent entry will + * call sync_pir_to_irr. + */ + if (!is_guest_mode(vcpu) && vcpu->arch.apicv_active) + vmx_set_rvi(max_irr); + else if (max_irr_updated) + kvm_make_request(KVM_REQ_EVENT, vcpu); + return max_irr; }