Re: [PATCH] KVM: nVMX: Only update last_vpid on a successful nested VM-Enter

From: Sean Christopherson

Date: Thu Jul 23 2026 - 13:58:25 EST


On Wed, Jul 22, 2026, Yosry Ahmed wrote:
> On Wed, Jul 22, 2026 at 2:41 PM Sean Christopherson <seanjc@xxxxxxxxxx> wrote:
> >
> > On Fri, Jul 17, 2026, Yosry Ahmed wrote:
> > > ---
> > > arch/x86/kvm/vmx/nested.c | 4 ++--
> > > 1 file changed, 2 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
> > > index b5460de4b1a72..0a4ea410b0483 100644
> > > --- a/arch/x86/kvm/vmx/nested.c
> > > +++ b/arch/x86/kvm/vmx/nested.c
> > > @@ -2818,8 +2818,6 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
> > > if (kvm_caps.has_tsc_control)
> > > vmcs_write64(TSC_MULTIPLIER, vcpu->arch.tsc_scaling_ratio);
> > >
> > > - nested_vmx_transition_tlb_flush(vcpu, vmcs12, true);
> > > -
> > > if (nested_cpu_has_ept(vmcs12))
> > > nested_ept_init_mmu_context(vcpu);
> > >
> > > @@ -3739,6 +3737,8 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
> > > vmx_start_preemption_timer(vcpu, timer_value);
> > > }
> > >
> > > + nested_vmx_transition_tlb_flush(vcpu, vmcs12, true);
> >
> > Hmm, so the SDM doesn't explicitly say _when_ TLB flushes happen, but I suspect
> > this doesn't match how hardware behaves. My guess is that any TLB flushes happen
> > once VM-Enter has gotten past the VM-Fail consistency checks, i.e. once a VM-Exit
> > is guaranteed.
> >
> > The SDM doesn't actually say anything about VM-Enter, so I don't think KVM *must*
> > implement *that* specific behavior, but I do think we should leave the call to
> > nested_vmx_transition_tlb_flush() where it's at, and instead service the local
> > pending flushes in the pseudo-VM-Exit path.
>
> Well, technically, changing the VPID is not a TLB flush. KVM has to
> flush the TLB because under the hood it's using the same VPID, but
> from L1's perspective, it's using a new VPID so any TLB entries
> associated with the old VPID should not be used (for that VM entry). I
> guess you're referring to other architectural flushes, like a VM entry
> with VPID disabled.

Oh, right, I overlooked that this specifically affects the vpid12 tracking.

> The reason why I moved the call to nested_vmx_transition_tlb_flush()
> is that it only makes sense (semantically) to update last_vpid when we
> will actually use the VPID. Otherwise, if a VM entry fails, the CPU
> couldn't have cached any translations associated with the new VPID,
> and a flush is not needed if the VPID is changed again.
>
> IOW, the choice was purely based on semantics and code readability.
>
> > Because the other way the TLB flushes
> > can be queued during VM-Enter is via the MSR load lists:
> >
> > If any MSR is being loaded in such a way that would architecturally require
> > a TLB flush, the TLBs are updated so that, after VM entry, the logical
> > processor will not use any translations that were cached before the transition.
> >
> > E.g. if L1 successfully loads one or more MTRRs on VM-Enter to L2[*], then fails
> > on a subsequent MSR, architecturally I believe L2 TLB entries are guaranteed to
> > be flushed.
>
> Hmm that is an interesting case. I guess the right thing to do here
> depends on hardware, but yeah I think it makes sense in this case to
> service local flushes in the failure path. It still annoys me that we
> would update last_vpid even on failed nested VM entries, so part of me
> still wants to move the call to nested_vmx_transition_tlb_flush() just
> for that, but that may not make sense for the VPID disabled case.

Yeah, but I'm not convinced that KVM is actually diverging from hardware. At
some point during VM-Enter, hardware needs to "officially" switch to VMX Non-Root
and start tagging TLB entries with the new VPID. I can see that being at the
bitter end, when success is confirmed, but I could also see it happening before
ucode starts loading guest state into hardware.

Heh, I wonder if we could abuse the MSR load list to deduce when hardware switches
its TLB tagging to VMX Non-Root and starts using the new VPID. E.g. maybe put
DS_AREA and PEBS_ENABLE in the load list, followed by a ton of MSRs to chew up
CPU cycles, and then reverse engineering how translations to service PEBS buffer
writes are resolved :-)