]> git.baikalelectronics.ru Git - kernel.git/commitdiff
KVM: x86: SVM: allow AVIC to co-exist with a nested guest running
authorMaxim Levitsky <mlevitsk@redhat.com>
Tue, 22 Mar 2022 17:40:50 +0000 (19:40 +0200)
committerPaolo Bonzini <pbonzini@redhat.com>
Sat, 2 Apr 2022 09:41:24 +0000 (05:41 -0400)
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: Maxim Levitsky <mlevitsk@redhat.com>
Message-Id: <20220322174050.241850-7-mlevitsk@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/svm/avic.c
arch/x86/kvm/svm/nested.c
arch/x86/kvm/svm/svm.c
arch/x86/kvm/svm/svm.h

index b7dabc01d6449bd49a71164902ad8bddcc744251..6c81abe3153bf06241c7da0cec9f3a8511f46a4d 100644 (file)
@@ -356,6 +356,13 @@ int avic_incomplete_ipi_interception(struct kvm_vcpu *vcpu)
        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)
 {
        struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm);
index a99a34bf0305fc423a0b0c6cefc7ba6111b49792..73b545278f5ff50bc38fc6f5b356f17b3da1522a 100644 (file)
@@ -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.
         */
 
-       /*
-        * 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))
                int_ctl_vmcb12_bits |= (V_GIF_MASK | V_GIF_ENABLE_MASK);
        else
@@ -765,6 +759,9 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa,
 
        svm_set_gif(svm, true);
 
+       if (kvm_vcpu_apicv_active(vcpu))
+               kvm_make_request(KVM_REQ_APICV_UPDATE, vcpu);
+
        return 0;
 }
 
@@ -1042,6 +1039,13 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
        if (unlikely(vmcb01->save.rflags & X86_EFLAGS_TF))
                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;
 }
 
index 772efdff46b38819cde015a1b445d4a599323d98..95b26dbfd561d5f1e205a2d3200781914f3d7bc5 100644 (file)
@@ -1466,7 +1466,7 @@ static void svm_set_vintr(struct vcpu_svm *svm)
        /*
         * 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);
 
@@ -2975,9 +2975,16 @@ static int interrupt_window_interception(struct kvm_vcpu *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
         * 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);
 
@@ -3572,10 +3579,16 @@ static void svm_enable_irq_window(struct kvm_vcpu *vcpu)
                /*
                 * IRQ window is not needed when AVIC is enabled,
                 * 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.
+                *
+                * 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);
        }
 }
@@ -4046,13 +4059,6 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu)
                 */
                if (guest_cpuid_has(vcpu, X86_FEATURE_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);
 }
@@ -4715,6 +4721,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = {
        .complete_emulated_msr = svm_complete_emulated_msr,
 
        .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)
        } else {
                svm_x86_ops.vcpu_blocking = NULL;
                svm_x86_ops.vcpu_unblocking = NULL;
+               svm_x86_ops.vcpu_get_apicv_inhibit_reasons = NULL;
        }
 
        if (vls) {
index 6b822f79010631d08557d07e7c43b757f2690544..e246793cbeaebe2f147f43056f4c802477cb4a3e 100644 (file)
@@ -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_unblocking(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 */