KVM: x86: fix apic_accept_events vs check_nested_events
authorPaolo Bonzini <pbonzini@redhat.com>
Thu, 5 Nov 2020 16:20:49 +0000 (11:20 -0500)
committerPaolo Bonzini <pbonzini@redhat.com>
Sun, 15 Nov 2020 14:49:08 +0000 (09:49 -0500)
vmx_apic_init_signal_blocked is buggy in that it returns true
even in VMX non-root mode.  In non-root mode, however, INITs
are not latched, they just cause a vmexit.  Previously,
KVM was waiting for them to be processed when kvm_apic_accept_events
and in the meanwhile it ate the SIPIs that the processor received.

However, in order to implement the wait-for-SIPI activity state,
KVM will have to process KVM_APIC_SIPI in vmx_check_nested_events,
and it will not be possible anymore to disregard SIPIs in non-root
mode as the code is currently doing.

By calling kvm_x86_ops.nested_ops->check_events, we can force a vmexit
(with the side-effect of latching INITs) before incorrectly injecting
an INIT or SIPI in a guest, and therefore vmx_apic_init_signal_blocked
can do the right thing.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/lapic.c
arch/x86/kvm/vmx/vmx.c

index 105e785..e3ee597 100644 (file)
@@ -2843,14 +2843,35 @@ void kvm_apic_accept_events(struct kvm_vcpu *vcpu)
 {
        struct kvm_lapic *apic = vcpu->arch.apic;
        u8 sipi_vector;
+       int r;
        unsigned long pe;
 
-       if (!lapic_in_kernel(vcpu) || !apic->pending_events)
+       if (!lapic_in_kernel(vcpu))
+               return;
+
+       /*
+        * Read pending events before calling the check_events
+        * callback.
+        */
+       pe = smp_load_acquire(&apic->pending_events);
+       if (!pe)
                return;
 
+       if (is_guest_mode(vcpu)) {
+               r = kvm_x86_ops.nested_ops->check_events(vcpu);
+               if (r < 0)
+                       return;
+               /*
+                * If an event has happened and caused a vmexit,
+                * we know INITs are latched and therefore
+                * we will not incorrectly deliver an APIC
+                * event instead of a vmexit.
+                */
+       }
+
        /*
         * INITs are latched while CPU is in specific states
-        * (SMM, VMX non-root mode, SVM with GIF=0).
+        * (SMM, VMX root mode, SVM with GIF=0).
         * Because a CPU cannot be in these states immediately
         * after it has processed an INIT signal (and thus in
         * KVM_MP_STATE_INIT_RECEIVED state), just eat SIPIs
@@ -2858,13 +2879,13 @@ void kvm_apic_accept_events(struct kvm_vcpu *vcpu)
         */
        if (kvm_vcpu_latch_init(vcpu)) {
                WARN_ON_ONCE(vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED);
-               if (test_bit(KVM_APIC_SIPI, &apic->pending_events))
+               if (test_bit(KVM_APIC_SIPI, &pe))
                        clear_bit(KVM_APIC_SIPI, &apic->pending_events);
                return;
        }
 
-       pe = xchg(&apic->pending_events, 0);
        if (test_bit(KVM_APIC_INIT, &pe)) {
+               clear_bit(KVM_APIC_INIT, &apic->pending_events);
                kvm_vcpu_reset(vcpu, true);
                if (kvm_vcpu_is_bsp(apic->vcpu))
                        vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
@@ -2873,6 +2894,7 @@ void kvm_apic_accept_events(struct kvm_vcpu *vcpu)
        }
        if (test_bit(KVM_APIC_SIPI, &pe) &&
            vcpu->arch.mp_state == KVM_MP_STATE_INIT_RECEIVED) {
+               clear_bit(KVM_APIC_SIPI, &apic->pending_events);
                /* evaluate pending_events before reading the vector */
                smp_rmb();
                sipi_vector = apic->sipi_vector;
index 39ff760..fd70bdf 100644 (file)
@@ -7558,7 +7558,7 @@ static void enable_smi_window(struct kvm_vcpu *vcpu)
 
 static bool vmx_apic_init_signal_blocked(struct kvm_vcpu *vcpu)
 {
-       return to_vmx(vcpu)->nested.vmxon;
+       return to_vmx(vcpu)->nested.vmxon && !is_guest_mode(vcpu);
 }
 
 static void vmx_migrate_timers(struct kvm_vcpu *vcpu)