Commit f44509f8 authored by Maxim Levitsky's avatar Maxim Levitsky Committed by Paolo Bonzini

KVM: x86: SVM: allow AVIC to co-exist with a nested guest running

Inhibit the AVIC of the vCPU that is running nested for the duration of the
nested run, so that all interrupts arriving from both its vCPU siblings
and from KVM are delivered using normal IPIs and cause that vCPU to vmexit.

Note that unlike normal AVIC inhibition, there is no need to
update the AVIC mmio memslot, because the nested guest uses its
own set of paging tables.
That also means that AVIC doesn't need to be inhibited VM wide.
Signed-off-by: default avatarMaxim Levitsky <mlevitsk@redhat.com>
Message-Id: <20220322174050.241850-7-mlevitsk@redhat.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent d5fa597e
...@@ -356,6 +356,13 @@ int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu) ...@@ -356,6 +356,13 @@ int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu)
return 1; return 1;
} }
unsigned long avic_vcpu_get_apicv_inhibit_reasons(struct kvm_vcpu *vcpu)
{
if (is_guest_mode(vcpu))
return APICV_INHIBIT_REASON_NESTED;
return 0;
}
static u32 *avic_get_logical_id_entry(struct kvm_vcpu *vcpu, u32 ldr, bool flat) static u32 *avic_get_logical_id_entry(struct kvm_vcpu *vcpu, u32 ldr, bool flat)
{ {
struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm);
......
...@@ -620,12 +620,6 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) ...@@ -620,12 +620,6 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
* exit_int_info, exit_int_info_err, next_rip, insn_len, insn_bytes. * exit_int_info, exit_int_info_err, next_rip, insn_len, insn_bytes.
*/ */
/*
* Also covers avic_vapic_bar, avic_backing_page, avic_logical_id,
* avic_physical_id.
*/
WARN_ON(kvm_apicv_activated(svm->vcpu.kvm));
if (svm->vgif_enabled && (svm->nested.ctl.int_ctl & V_GIF_ENABLE_MASK)) if (svm->vgif_enabled && (svm->nested.ctl.int_ctl & V_GIF_ENABLE_MASK))
int_ctl_vmcb12_bits |= (V_GIF_MASK | V_GIF_ENABLE_MASK); int_ctl_vmcb12_bits |= (V_GIF_MASK | V_GIF_ENABLE_MASK);
else else
...@@ -765,6 +759,9 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, ...@@ -765,6 +759,9 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa,
svm_set_gif(svm, true); svm_set_gif(svm, true);
if (kvm_vcpu_apicv_active(vcpu))
kvm_make_request(KVM_REQ_APICV_UPDATE, vcpu);
return 0; return 0;
} }
...@@ -1042,6 +1039,13 @@ int nested_svm_vmexit(struct vcpu_svm *svm) ...@@ -1042,6 +1039,13 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
if (unlikely(vmcb01->save.rflags & X86_EFLAGS_TF)) if (unlikely(vmcb01->save.rflags & X86_EFLAGS_TF))
kvm_queue_exception(&(svm->vcpu), DB_VECTOR); kvm_queue_exception(&(svm->vcpu), DB_VECTOR);
/*
* Un-inhibit the AVIC right away, so that other vCPUs can start
* to benefit from it right away.
*/
if (kvm_apicv_activated(vcpu->kvm))
kvm_vcpu_update_apicv(vcpu);
return 0; return 0;
} }
......
...@@ -1466,7 +1466,7 @@ static void svm_set_vintr(struct vcpu_svm *svm) ...@@ -1466,7 +1466,7 @@ static void svm_set_vintr(struct vcpu_svm *svm)
/* /*
* The following fields are ignored when AVIC is enabled * The following fields are ignored when AVIC is enabled
*/ */
WARN_ON(kvm_apicv_activated(svm->vcpu.kvm)); WARN_ON(kvm_vcpu_apicv_activated(&svm->vcpu));
svm_set_intercept(svm, INTERCEPT_VINTR); svm_set_intercept(svm, INTERCEPT_VINTR);
...@@ -2975,9 +2975,16 @@ static int interrupt_window_interception(struct kvm_vcpu *vcpu) ...@@ -2975,9 +2975,16 @@ static int interrupt_window_interception(struct kvm_vcpu *vcpu)
svm_clear_vintr(to_svm(vcpu)); svm_clear_vintr(to_svm(vcpu));
/* /*
* For AVIC, the only reason to end up here is ExtINTs. * If not running nested, for AVIC, the only reason to end up here is ExtINTs.
* In this case AVIC was temporarily disabled for * In this case AVIC was temporarily disabled for
* requesting the IRQ window and we have to re-enable it. * requesting the IRQ window and we have to re-enable it.
*
* If running nested, still remove the VM wide AVIC inhibit to
* support case in which the interrupt window was requested when the
* vCPU was not running nested.
* All vCPUs which run still run nested, will remain to have their
* AVIC still inhibited due to per-cpu AVIC inhibition.
*/ */
kvm_clear_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_IRQWIN); kvm_clear_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_IRQWIN);
...@@ -3572,10 +3579,16 @@ static void svm_enable_irq_window(struct kvm_vcpu *vcpu) ...@@ -3572,10 +3579,16 @@ static void svm_enable_irq_window(struct kvm_vcpu *vcpu)
/* /*
* IRQ window is not needed when AVIC is enabled, * IRQ window is not needed when AVIC is enabled,
* unless we have pending ExtINT since it cannot be injected * unless we have pending ExtINT since it cannot be injected
* via AVIC. In such case, we need to temporarily disable AVIC, * via AVIC. In such case, KVM needs to temporarily disable AVIC,
* and fallback to injecting IRQ via V_IRQ. * and fallback to injecting IRQ via V_IRQ.
*
* If running nested, AVIC is already locally inhibited
* on this vCPU, therefore there is no need to request
* the VM wide AVIC inhibition.
*/ */
kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_IRQWIN); if (!is_guest_mode(vcpu))
kvm_set_apicv_inhibit(vcpu->kvm, APICV_INHIBIT_REASON_IRQWIN);
svm_set_vintr(svm); svm_set_vintr(svm);
} }
} }
...@@ -4046,13 +4059,6 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) ...@@ -4046,13 +4059,6 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
*/ */
if (guest_cpuid_has(vcpu, X86_FEATURE_X2APIC)) if (guest_cpuid_has(vcpu, X86_FEATURE_X2APIC))
kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_X2APIC); kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_X2APIC);
/*
* Currently, AVIC does not work with nested virtualization.
* So, we disable AVIC when cpuid for SVM is set in the L1 guest.
*/
if (nested && guest_cpuid_has(vcpu, X86_FEATURE_SVM))
kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_NESTED);
} }
init_vmcb_after_set_cpuid(vcpu); init_vmcb_after_set_cpuid(vcpu);
} }
...@@ -4715,6 +4721,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { ...@@ -4715,6 +4721,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
.complete_emulated_msr = svm_complete_emulated_msr, .complete_emulated_msr = svm_complete_emulated_msr,
.vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector, .vcpu_deliver_sipi_vector = svm_vcpu_deliver_sipi_vector,
.vcpu_get_apicv_inhibit_reasons = avic_vcpu_get_apicv_inhibit_reasons,
}; };
/* /*
...@@ -4916,6 +4923,7 @@ static __init int svm_hardware_setup(void) ...@@ -4916,6 +4923,7 @@ static __init int svm_hardware_setup(void)
} else { } else {
svm_x86_ops.vcpu_blocking = NULL; svm_x86_ops.vcpu_blocking = NULL;
svm_x86_ops.vcpu_unblocking = NULL; svm_x86_ops.vcpu_unblocking = NULL;
svm_x86_ops.vcpu_get_apicv_inhibit_reasons = NULL;
} }
if (vls) { if (vls) {
......
...@@ -624,6 +624,7 @@ int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq, ...@@ -624,6 +624,7 @@ int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq,
void avic_vcpu_blocking(struct kvm_vcpu *vcpu); void avic_vcpu_blocking(struct kvm_vcpu *vcpu);
void avic_vcpu_unblocking(struct kvm_vcpu *vcpu); void avic_vcpu_unblocking(struct kvm_vcpu *vcpu);
void avic_ring_doorbell(struct kvm_vcpu *vcpu); void avic_ring_doorbell(struct kvm_vcpu *vcpu);
unsigned long avic_vcpu_get_apicv_inhibit_reasons(struct kvm_vcpu *vcpu);
/* sev.c */ /* sev.c */
......
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