kvm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Paolo Bonzini <pbonzini@redhat.com>
Cc: Oliver Upton <oupton@google.com>,
	idan.brown@oracle.com, Jim Mattson <jmattson@google.com>,
	kvm list <kvm@vger.kernel.org>,
	liam.merwick@oracle.com, wanpeng.li@hotmail.com
Subject: Re: [PATCH v3 11/11] KVM: nVMX: Wake L2 from HLT when nested posted-interrupt pending
Date: Tue, 24 Nov 2020 21:22:15 +0000	[thread overview]
Message-ID: <20201124212215.GA246319@google.com> (raw)
In-Reply-To: <e140ed23-df91-5da2-965a-e92b4a54e54e@redhat.com>

On Tue, Nov 24, 2020, Paolo Bonzini wrote:
> On 24/11/20 02:55, Sean Christopherson wrote:
> > > > I believe there is a 1-to-many relationship here, which is why I said
> > > > each CPU would need to maintain a linked list of possible vCPUs to
> > > > iterate and find the intended recipient.
> > 
> > Ya, the concern is that it's theoretically possible for the PINV to arrive in L0
> > after a different vCPU has been loaded (or even multiple different vCPUs).
> > E.g. if the sending pCPU is hit with an NMI after checking vcpu->mode, and the
> > NMI runs for some absurd amount of time.  If that happens, the PINV handler
> > won't know which vCPU(s) should get an IRQ injected into L1 without additional
> > tracking.  KVM would need to set something like nested.pi_pending before doing
> > kvm_vcpu_trigger_posted_interrupt(), i.e. nothing really changes, it just gets
> > more complex.
> 
> Ah, gotcha.  What if IN_GUEST_MODE/OUTSIDE_GUEST_MODE was replaced by a
> generation count?  Then you reread vcpu->mode after sending the IPI, and
> retry if it does not match.
> 
> To guarantee atomicity, the OUTSIDE_GUEST_MODE IN_GUEST_MODE
> EXITING_GUEST_MODE READING_SHADOW_PAGE_TABLES values would remain in the
> bottom 2 bits.  That is, the vcpu->mode accesses like
> 
> 	vcpu->mode = IN_GUEST_MODE;
> 
> 	vcpu->mode = OUTSIDE_GUEST_MODE;
> 
> 	smp_store_mb(vcpu->mode, READING_SHADOW_PAGE_TABLES);
> 
> 	smp_store_release(&vcpu->mode, OUTSIDE_GUEST_MODE);
> 
> 	return cmpxchg(&vcpu->mode, IN_GUEST_MODE, EXITING_GUEST_MODE);
> 
> becoming
> 
> 	enum {
> 		OUTSIDE_GUEST_MODE,
> 		IN_GUEST_MODE,
> 		EXITING_GUEST_MODE,
> 		READING_SHADOW_PAGE_TABLES,
> 		GUEST_MODE_MASK = 3,
> 	};
> 
> 	vcpu->mode = (vcpu->mode | GUEST_MODE_MASK) + 1 + IN_GUEST_MODE;
> 
> 	vcpu->mode &= ~GUEST_MODE_MASK;
> 
> 	smp_store_mb(vcpu->mode, vcpu->mode|READING_SHADOW_PAGE_TABLES);
> 
> 	smp_store_release(&vcpu->mode, vcpu->mode & ~GUEST_MODE_MASK);
> 
> 	int x = READ_ONCE(vcpu->mode);
> 	do {
> 		if ((x & GUEST_MODE_MASK) != IN_GUEST_MODE)
> 			return x & GUEST_MODE_MASK;
> 	} while (!try_cmpxchg(&vcpu->mode, &x,
> 			      x ^ IN_GUEST_MODE ^ EXITING_GUEST_MODE))
> 	return IN_GUEST_MODE;
> 
> You could still get spurious posted interrupt IPIs, but the IPI handler need
> not do anything at all and that is much better.

This doesn't handle the case where the PINV arrives in L0 after VM-Exit but
before the vCPU clears IN_GUEST_MODE.  The sender will have seen IN_GUEST_MODE
and so won't retry the IPI, but hardware didn't process the PINV as a
posted-interrupt.  I.e. the L0 PINV handler still needs an indicator a la
nested.pi_pending to know that it should stuff an IRQ into L1's vIRR.

> > if we're ok with KVM
> > processing virtual interrupts that technically shouldn't happen, yet.  E.g. if
> > the L0 PINV handler consumes vIRR bits that were set after the last PI from L1.
> 
> I actually find it curious that the spec promises posted interrupt
> processing to be triggered only by the arrival of the posted interrupt IPI.
> Why couldn't the processor in principle snoop for the address of the ON bit
> instead, similar to an MWAIT?

It would lead to false positives and missed IRQs.  PI processing would fire on
writing the PI _cache line_, not just on writes to PI.ON.  I suspect MONITOR is
also triggered on request-for-EXLUSIVE and not just writes, i.e. on speculative
behavior, but I forget if that's actually the case.

Regardless, a write to any part of the PI would trip the monitoring, and then
all subsequent writes would be missed, e.g. other package writes PI.IRR then
PI.ON, CPU PI processing triggers on the PI.IRR write but not PI.ON write.  The
target CPU (the running vCPU) would have to constantly rearm the monitor, but
even then there would always be a window where a write would get missed.

> But even without that, I don't think the spec promises that kind of strict
> ordering with respect to what goes on in the source.  Even though posted
> interrupt processing is atomic with the acknowledgement of the posted
> interrupt IPI, the spec only promises that the PINV triggers an _eventual_
> scan of PID.PIR when the interrupt controller delivers an unmasked external
> interrupt to the destination CPU.  You can still have something like
> 
> 	set PID.PIR[100]
> 	set PID.ON
> 					processor starts executing a
> 					 very slow instruction...
> 	send PINV
> 	set PID.PIR[200]
> 					acknowledge PINV
> 
> and then vector 200 would be delivered before vector 100.  Of course with
> nested PI the effect would be amplified, but it's possible even on bare
> metal.

Jim was concerned that L1 could poll the PID to determine whether or not
PID.PIR[200] should be seen in L2.  The whole PIR is copied to the vIRR after
PID.ON is cleared the auto-EOI is done, and the read->clear is atomic.  So the
above sequence where PINV is acknowledge after PID.PIR[200] is legal, but
processing PIR bits that are set after the PIR is observed to be cleared would
be illegal.  E.g. if L1 did this

	set PID.PIR[100]
	set PID.ON
	send PINV
	while (PID.PIR)
	set PID.PIR[200]
	set PID.ON

then L2 should never observe vector 200.  KVM violates this because
nested.pi_pending is left set even if PINV is handled as a posted interrupt, and
KVM's processing of nested.pi_pending will see the second PID.ON and incorrectly
do PI processing in software.  This is the part that is likely impossible to
solve without shadowing the PID (which, for the record, I have zero desire to do).

It seems extremely unlikely any guest will puke on the above, I can't imagine
there's for setting a PID.PIR + PID.ON without triggering PINV, but it's
technically bad behavior in KVM.

  reply	other threads:[~2020-11-24 21:22 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-24 16:12 [PATCH v3 00/11] KVM: nVMX: Fix multiple issues in nested posted-interrupts Liran Alon
2017-12-24 16:12 ` [PATCH v3 01/11] KVM: x86: Optimization: Create SVM stubs for sync_pir_to_irr() Liran Alon
2017-12-27  9:56   ` Paolo Bonzini
2017-12-27 10:01     ` Liran Alon
2017-12-24 16:12 ` [PATCH v3 02/11] KVM: x86: Change __kvm_apic_update_irr() to also return if max IRR updated Liran Alon
2018-01-02  1:51   ` Quan Xu
2017-12-24 16:12 ` [PATCH v3 03/11] KVM: nVMX: Re-evaluate L1 pending events when running L2 and L1 got posted-interrupt Liran Alon
2018-01-02  2:45   ` Quan Xu
2018-01-02  9:57     ` Liran Alon
2018-01-02 11:21       ` Quan Xu
2018-01-02 11:52         ` Quan Xu
2018-01-02 12:20           ` Liran Alon
2018-01-03  5:32             ` Quan Xu
2018-01-03  5:35   ` Quan Xu
2017-12-24 16:12 ` [PATCH v3 04/11] KVM: nVMX: Fix injection to L2 when L1 don't intercept external-interrupts Liran Alon
2017-12-24 16:12 ` [PATCH v3 05/11] KVM: x86: Rename functions which saves vCPU in per-cpu var Liran Alon
2017-12-24 16:12 ` [PATCH v3 06/11] KVM: x86: Set current_vcpu per-cpu var before enabling interrupts at host Liran Alon
2017-12-27 10:06   ` Paolo Bonzini
2017-12-27 10:44     ` Liran Alon
2017-12-24 16:12 ` [PATCH v3 07/11] KVM: x86: Add util for getting current vCPU running on CPU Liran Alon
2017-12-24 16:13 ` [PATCH v3 08/11] KVM: x86: Register empty handler for POSTED_INTR_NESTED_VECTOR IPI Liran Alon
2017-12-24 16:13 ` [PATCH v3 09/11] KVM: nVMX: Deliver missed nested-PI notification-vector via self-IPI while interrupts disabled Liran Alon
2017-12-24 16:13 ` [PATCH v3 10/11] KVM: nVMX: Wake halted L2 on nested posted-interrupt Liran Alon
2017-12-27 11:31   ` Paolo Bonzini
2017-12-27 12:01     ` Liran Alon
2017-12-27 12:27       ` Paolo Bonzini
2017-12-27 12:52         ` Liran Alon
2017-12-27 13:05           ` Paolo Bonzini
2017-12-27 15:33             ` Liran Alon
2017-12-27 15:54               ` Paolo Bonzini
2018-01-01 21:32                 ` Paolo Bonzini
2018-01-01 22:37                   ` Liran Alon
2018-01-02  7:25                     ` Paolo Bonzini
2017-12-24 16:13 ` [PATCH v3 11/11] KVM: nVMX: Wake L2 from HLT when nested posted-interrupt pending Liran Alon
2017-12-27 10:15   ` Paolo Bonzini
2017-12-27 10:51     ` Liran Alon
2017-12-27 12:55       ` Paolo Bonzini
2017-12-27 15:15         ` Liran Alon
2017-12-27 15:55           ` Paolo Bonzini
2020-11-23 19:22             ` Oliver Upton
2020-11-23 22:42               ` Paolo Bonzini
2020-11-24  0:10                 ` Oliver Upton
2020-11-24  0:13                   ` Oliver Upton
2020-11-24  1:55                     ` Sean Christopherson
2020-11-24  3:19                       ` Sean Christopherson
2020-11-24 11:39                       ` Paolo Bonzini
2020-11-24 21:22                         ` Sean Christopherson [this message]
2020-11-25  0:10                           ` Paolo Bonzini
2020-11-25  1:14                             ` Sean Christopherson
2020-11-25 17:00                               ` Paolo Bonzini
2020-11-25 18:32                                 ` Sean Christopherson
2020-11-26 13:13                                   ` Paolo Bonzini
2020-11-30 19:14                                     ` Sean Christopherson
2020-11-30 19:36                                       ` Paolo Bonzini
2020-12-03 22:07                         ` Jim Mattson
2020-11-24 11:09                   ` Paolo Bonzini
2020-12-03 21:45                     ` Jim Mattson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201124212215.GA246319@google.com \
    --to=seanjc@google.com \
    --cc=idan.brown@oracle.com \
    --cc=jmattson@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=liam.merwick@oracle.com \
    --cc=oupton@google.com \
    --cc=pbonzini@redhat.com \
    --cc=wanpeng.li@hotmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).