Re: [PATCH v2 10/29] KVM: SVM: Use a static ASID per vCPU
From: Jim Mattson
Date: Wed Oct 14 2026 - 19:54:20 EST
On Wed, Oct 7, 2026 at 5:14 PM Yosry Ahmed <yosry@xxxxxxxxxx> wrote:
>
> Switch from dynamic ASID allocation to a static per-vCPU ASID. The
> dynamic ASID allocation logic is now only effectively used when running
> out of ASIDs on a CPU (uncommon on modern hardware), or switching CPUs.
>
> The per-VMCB ASID generation is initialized to 0, which never matches the
> per-CPU ASID generation (bumped every time a CPU is enabled for
> virtualization). This leads to a TLB flush of the ASID on the first VMRUN,
> allocating a new ASID, and bumping the per-VMCB generation to match the
> per-CPU generation. The ASID remains static until one of the following:
>
> - The vCPU runs on a new CPU, in which case KVM resets the generation of
> both VMCBs to allocate a new ASID on the new CPU.
>
> - KVM hits the maximum ASID on a CPU and increments the per-CPU ASID
> generation, at which point all vCPUs on this CPU will allocate a new
> ASID.
>
> - The CPU is re-enabled for virtualization (e.g. after CPU hotplug), at
> which point the per-CPU generation is bumped and all vCPUs that
> previously ran on it will allocate a new ASID.
>
> Drop the complexity and make the ASID static for each vCPU. This makes
> SVM's handling of ASIDs closer to VMX's handling of VPIDs, and
> simplifies the code. It also completely avoids full TLB flushes (i.e.
> TLB_CONTROL_FLUSH_ALL_ASID) on systems with FlushByAsid. Full flushes
> previously happened when updating the generation, so not so common, but
> should generally be avoided as they cause a VMRUN to invalidate the TLB
> entries for other VMs as well as the host.
>
> When a vCPU is migrated to a new physical CPU, flush the (now static)
> ASID instead of allocating a new one. This might cause extra TLB flushes
> when switching CPUs (compared to just using a new ASID), but odds are
> that the TLB is cold on the new CPU anyway.
>
> Static ASIDs also make the following fixes to the dynamic ASID
> allocation scheme unnecessary, so drop them:
>
> - Bumping the per-CPU generation when enabling virtualization on a CPU,
> added by commit 25f744ffa0c8 ("KVM: SVM: Bump asid_generation on CPU
> online to avoid ASID collision after hotplug"). A vCPU can no longer
> run with an ASID that is (re)assigned to another vCPU.
>
> - Resetting the generation of both VMCBs on a CPU switch, added by
> commit bf260dc9e7b8 ("KVM: SVM: Trigger new ASID allocation in both
> VMCBs on pCPU switch"). An ASID is now owned by the same vCPU on all
> CPUs, so a VMCB that misses a CPU switch can no longer end up using an
> ASID owned by another vCPU on that CPU.
>
> - Tracking a pending full flush per-CPU (i.e. flush_all_asids), added by
> commit 60d93c27859f ("KVM: SVM: Preserve TLB control (i.e. pending TLB
> flush) on failed VMRUN"). The full flush on generation wraparound was
> needed on behalf of all vCPUs on the CPU, as their ASIDs were about to
> be recycled, so it had to be tracked per-CPU instead of in the VMCB of
> the vCPU that happened to trigger the wraparound. With static ASIDs,
> TLB flushes are only needed for the vCPU's own ASID, so tracking them in
> the VMCB is sufficient, and a pending flush in the VMCB is still
> preserved on a failed VMRUN.
>
> As using ASIDs cannot be disabled like VPIDs, allocate a fallback ASID
> to be shared by all vCPUs after running out of ASIDs (and flushed on
> every vCPU run, for now). If a fallback ASID cannot be allocated, fail
> vCPU creation (for non-SEV guests), but do not fail SVM initialization,
> to allow for allocating all ASIDs for SEV (if at all possible).
>
> For SEV, the same ASID continues to be used by all vCPUs in a VM. Move
> svm->asid initialization from SEV-specific VMCB initialization to vCPU
> creation, such that svm->asid is always initialized on vCPU creation for
> both SEV and non-SEV vCPUs. This makes the logic simpler to follow, and
> makes it easier to hide it in allocate_asid() and free_asid() helpers.
> Decide whether to free an ASID based on the ASID range rather than the
> VM type, as an SEV VM's vCPUs may stop being SEV vCPUs (or vice versa)
> as a result of SEV VM migration.
>
> Keep the ASID initialization in sev_init_vmcb(), even though it's
> already done in init_vmcb(), to handle SEV VM migration. svm->asid is
> already reinitialized on destination SEV VMs during migration before
> VMCB initialization, so the ASID ends up correctly in the VMCB.
> Additionally, free any existing non-SEV ASID on the destination vCPU
> before overwriting it, to avoid leaking ASIDs when creating vCPUs then
> migrating an SEV VM onto them.
>
> Note: A nice side-effect is reading the min/max ASIDs once during
> initialization, instead of once per-CPU.
>
> Signed-off-by: Yosry Ahmed <yosry@xxxxxxxxxx>
> ---
> arch/x86/kvm/svm/nested.c | 4 +-
> arch/x86/kvm/svm/sev.c | 7 +++-
> arch/x86/kvm/svm/svm.c | 79 +++++++++++++++------------------------
> arch/x86/kvm/svm/svm.h | 25 +++++++++----
> 4 files changed, 55 insertions(+), 60 deletions(-)
>
[ ... ]
> @@ -1375,6 +1370,12 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu)
> goto error_free_avic;
> }
>
> + svm->asid = allocate_asid(vcpu);
> + if (!svm->asid) {
> + err = -EINVAL;
It seems like -ENOSPC is more appropriate, and would make triaging
this failure easier.
> + goto error_free_msrpm;
> + }