Re: [PATCH 2/4] KVM: VMX: prepare sync_pir_to_irr for running with APICv disabled

From: Sean Christopherson
Date: Mon Nov 29 2021 - 17:38:47 EST


On Mon, Nov 22, 2021, Paolo Bonzini wrote:
> 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.

Overzealous newlines.

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@xxxxxxxxxxxxxxx
> Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx>
> ---
> 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;

Heh, maybe s/max_irr_updated/new_pir_found or so? This is a bit weird:

1. Update max_irr
2. 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

I'd strongly prefer to phrase this as a command, e.g. "process the interrupt via
KVM_REQ_EVENT". "will be processed" makes it sound like some other flow is
handling the event, which confused me.

> + * in two cases:
> + *
> + * 1) If we are running L2 and L1 has a new pending interrupt

Hmm, calling it L1's interrupt isn't technically wrong, but it's a bit confusing
because it's handled in L2. Maybe just say "vCPU"?

> + * which can be injected, this may cause a vmexit or it may

Overzealous newlines again.

> + * be injected into L2. We do not use virtual interrupt
> + * delivery to inject L1 interrupts into L2.

I found the part of "may cause a vmexit or may be injected into L2" hard to
follow, I think because this doesn't explicit state that the KVM_REQ_EVENT is
needed to synthesize a VM-Exit.

How about this for the comment?

/*
* If virtual interrupt delivery is not in use, process the interrupt
* via KVM_REQ_EVENT, not RVI. This is necessary in two cases:
*
* 1) If L2 is running and the vCPU has a new pending interrupt. If L1
* wants to exit on interrupts, KVM_REQ_EVENT is needed to synthesize a
* VM-Exit to L1. If L1 doesn't want to exit, the interrupt is injected
* into L2, but KVM doesn't use virtual interrupt delivery to inject
* interrupts into L2, and so KVM_REQ_EVENT is again needed.
*
* 2) If APICv is disabled for this vCPU, assigned devices may still
* attempt to post interrupts. The posted interrupt vector will cause
* a VM-Exit and the subsequent entry will call sync_pir_to_irr.
*/

> + *
> + * 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)

If the second check uses kvm_vcpu_apicv_active(), this will avoid a silent conflict
when kvm/master and kvm/queue are merged.

Nits aside, the logic is good:

Reviewed-by: Sean Christopherson <seanjc@xxxxxxxxxx>

> + vmx_set_rvi(max_irr);
> + else if (max_irr_updated)
> + kvm_make_request(KVM_REQ_EVENT, vcpu);
> +
> return max_irr;
> }
>
> --
> 2.27.0
>
>