[RFC PATCH v2 17/25] KVM: nSVM: Service local TLB flushes before nested transitions

From: Yosry Ahmed

Date: Mon Jun 15 2026 - 20:47:34 EST


KVM does not track TLB flush requests for L1 vs. L2. Hence, service
local flush that target the current context before switching to a new
one. Since TLB flushes are performed through the VMCB's TLB_CONTROL
field, service the flushes before every VMCB switch.

Note that nested_svm_{entry/exit}_tlb_flush() must be called after
kvm_service_local_tlb_flush_requests(), otherwise the TLB flushes will
be immediately serviced in the "old" VMCB rather than the new one.

This is conceptually similar to how nVMX calls
kvm_service_local_tlb_flush_requests() with a few differences:

1. VMX performs TLB flushes through INVVPID or INVEPT. The VPID is
determined based on guest mode, and the EPT pointer is determined
based on the active MMU. Hence, local TLB flushes are serviced before
switching guest mode and switching the MMU. On the other hand, SVM
performs TLB flushes by updating the VMCB, hence local TLB flushes
are serviced before switching the VMCB.

2. VMX has a single code path for entering guest mode (i.e.
nested_vmx_enter_non_root_mode()) and a single code path for exiting
guest mode (i.e. nested_vmx_vmexit()). Other code paths like
vmx_set_nested_state() and vmx_leave_nested() call into these
functions. On the other hand, SVM open codes the switches in several
places, so call kvm_service_local_tlb_flush_requests() from
svm_switch_svm() to more-or-less guarantee it is not missed.

Signed-off-by: Yosry Ahmed <yosry@xxxxxxxxxx>
---
arch/x86/kvm/svm/nested.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index 234724d8b4c54..7b19191e0e43f 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -717,9 +717,16 @@ static void svm_switch_vmcb(struct vcpu_svm *svm, struct kvm_vmcb_info *target_v
{
struct kvm_vcpu *vcpu = &svm->vcpu;

+ /*
+ * TLB flushes are applied to the VMCB, so apply any pending TLB flushes
+ * on the current VMCB before switching to a new one..
+ */
+ kvm_service_local_tlb_flush_requests(vcpu);
+
svm->current_vmcb = target_vmcb;
svm->vmcb = target_vmcb->ptr;

+ /* .. then request TLB flushes needed for the new VMCB */
if (target_vmcb == &svm->nested.vmcb02)
nested_svm_entry_tlb_flush(vcpu);
else
--
2.54.0.1136.gdb2ca164c4-goog