KVM: x86: Tweak the code and comment related to handling concurrent NMIs
authorSean Christopherson <seanjc@google.com>
Mon, 27 Feb 2023 08:40:11 +0000 (14:10 +0530)
committerSean Christopherson <seanjc@google.com>
Wed, 22 Mar 2023 19:34:33 +0000 (12:34 -0700)
Tweak the code and comment that deals with concurrent NMIs to explicitly
call out that x86 allows exactly one pending NMI, but that KVM needs to
temporarily allow two pending NMIs in order to workaround the fact that
the target vCPU cannot immediately recognize an incoming NMI, unlike bare
metal.

No functional change intended.

Signed-off-by: Santosh Shukla <Santosh.Shukla@amd.com>
Link: https://lore.kernel.org/r/20230227084016.3368-7-santosh.shukla@amd.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/x86.c

index 3c995483b40523f2d83406569f5f26a28f331ff8..e317559df489a88a4e929fa2128b36730b29b2e4 100644 (file)
@@ -10138,15 +10138,22 @@ out:
 
 static void process_nmi(struct kvm_vcpu *vcpu)
 {
-       unsigned limit = 2;
+       unsigned int limit;
 
        /*
-        * x86 is limited to one NMI running, and one NMI pending after it.
-        * If an NMI is already in progress, limit further NMIs to just one.
-        * Otherwise, allow two (and we'll inject the first one immediately).
+        * x86 is limited to one NMI pending, but because KVM can't react to
+        * incoming NMIs as quickly as bare metal, e.g. if the vCPU is
+        * scheduled out, KVM needs to play nice with two queued NMIs showing
+        * up at the same time.  To handle this scenario, allow two NMIs to be
+        * (temporarily) pending so long as NMIs are not blocked and KVM is not
+        * waiting for a previous NMI injection to complete (which effectively
+        * blocks NMIs).  KVM will immediately inject one of the two NMIs, and
+        * will request an NMI window to handle the second NMI.
         */
        if (static_call(kvm_x86_get_nmi_mask)(vcpu) || vcpu->arch.nmi_injected)
                limit = 1;
+       else
+               limit = 2;
 
        vcpu->arch.nmi_pending += atomic_xchg(&vcpu->arch.nmi_queued, 0);
        vcpu->arch.nmi_pending = min(vcpu->arch.nmi_pending, limit);