mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Sean Christopherson <seanjc@google.com>
To: Madhavan Srinivasan <maddy@linux.ibm.com>,
	Sean Christopherson <seanjc@google.com>,
	 Paolo Bonzini <pbonzini@redhat.com>
Cc: Nicholas Piggin <npiggin@gmail.com>,
	linuxppc-dev@lists.ozlabs.org, kvm@vger.kernel.org,
	 linux-kernel@vger.kernel.org, Jim Mattson <jmattson@google.com>,
	 James Houghton <jthoughton@google.com>,
	Chris Mason <mason@kernel.org>
Subject: [PATCH v4 17/23] KVM: nVMX: Don't explicitly VMCLEAR+free() vmcs01's shadow VMCS when freeing vCPU
Date: Fri, 30 Oct 2026 14:21:05 -0700	[thread overview]
Message-ID: <20261030212112.2598640-18-seanjc@google.com> (raw)
In-Reply-To: <20261030212112.2598640-1-seanjc@google.com>

Explicitly VMCLEAR and free() vmcs01's shadow VMCS only when doing VMXOFF
or forcing the vCPU out of L2, not when freeing the vCPU.  Instead, free
the shadow VMCS as part of free_loaded_vmcs(), i.e. when freeing vmcs01 via
vmx_vcpu_free().  This will allow leaving vmcs02 as the loaded/current VMCS
when freeing a vCPU, i.e. will allow dropping the vmx_switch_vmcs() call as
well as the vcpu_{load,put}() calls, all of which do far too much work when
freeing a vCPU.  E.g. "just" switching to vmcs01 means potentially doing an
extra VCLEAR shootdown, potentially multiple VMPTRLDs (to first load vmcs02
and then vmcs01) and VMWRITEs, and so on and so forth.

Note, shadow VMCSes are rather odd.  They're only ever used when nested
virtualization is live, but a shadow VMCS is provided to L1, not L2, and
more importantly is made "active" (in VMX parlance) when vmcs01 is run (the
shadow VMCS is referenced by a pointer in vmcs01).  Because KVM dynamically
allocates and frees nested state, shadow VMCSes are loosely coupled to the
VMXON/VMXOFF lifecycle, but in practice are more concretely tied to vmcs01,
e.g. a shadow VMCS needs to be VMCLEAR'd if and only if its associated
vmcs01 was launched.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 arch/x86/kvm/vmx/nested.c | 54 ++++++++++++++++++++++++---------------
 arch/x86/kvm/vmx/vmx.c    |  5 +++-
 2 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 49c4529901c5..1b30ad7731ee 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -330,17 +330,9 @@ static void nested_put_vmcs12_pages(struct kvm_vcpu *vcpu)
 	vmx->nested.pi_desc = NULL;
 }
 
-/*
- * Free whatever needs to be freed from vmx->nested when L1 goes down, or
- * just stops using VMX.
- */
 static void free_nested(struct kvm_vcpu *vcpu)
 {
 	struct vcpu_vmx *vmx = to_vmx(vcpu);
-	struct vmcs *shadow_vmcs;
-
-	if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
-		vmx_switch_vmcs(vcpu, &vmx->vmcs01);
 
 	if (!vmx->nested.vmxon && !vmx->nested.smm.vmxon)
 		return;
@@ -353,6 +345,36 @@ static void free_nested(struct kvm_vcpu *vcpu)
 	free_vpid(vmx->nested.vpid02);
 	vmx->nested.posted_intr_nv = -1;
 	vmx->nested.current_vmptr = INVALID_GPA;
+
+	kfree(vmx->nested.cached_vmcs12);
+	vmx->nested.cached_vmcs12 = NULL;
+	kfree(vmx->nested.cached_shadow_vmcs12);
+	vmx->nested.cached_shadow_vmcs12 = NULL;
+
+	nested_put_vmcs12_pages(vcpu);
+
+	kvm_mmu_free_roots(vcpu->kvm, &vcpu->arch.guest_mmu, KVM_MMU_ROOTS_ALL);
+
+	nested_release_evmcs(vcpu);
+
+	free_loaded_vmcs(&vmx->nested.vmcs02);
+}
+
+/*
+ * Free whatever needs to be freed from vmx->nested when L1 goes down, or
+ * just stops using VMX.
+ */
+static void nested_vmx_do_vmxoff(struct kvm_vcpu *vcpu)
+{
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
+	struct vmcs *shadow_vmcs;
+
+	if (WARN_ON_ONCE(vmx->loaded_vmcs != &vmx->vmcs01))
+		vmx_switch_vmcs(vcpu, &vmx->vmcs01);
+
+	if (!vmx->nested.vmxon && !vmx->nested.smm.vmxon)
+		return;
+
 	if (enable_shadow_vmcs) {
 		vmx_disable_shadow_vmcs(vmx);
 
@@ -365,18 +387,8 @@ static void free_nested(struct kvm_vcpu *vcpu)
 		vmx->vmcs01.shadow_vmcs = NULL;
 		free_vmcs(shadow_vmcs);
 	}
-	kfree(vmx->nested.cached_vmcs12);
-	vmx->nested.cached_vmcs12 = NULL;
-	kfree(vmx->nested.cached_shadow_vmcs12);
-	vmx->nested.cached_shadow_vmcs12 = NULL;
 
-	nested_put_vmcs12_pages(vcpu);
-
-	kvm_mmu_free_roots(vcpu->kvm, &vcpu->arch.guest_mmu, KVM_MMU_ROOTS_ALL);
-
-	nested_release_evmcs(vcpu);
-
-	free_loaded_vmcs(&vmx->nested.vmcs02);
+	free_nested(vcpu);
 }
 
 /*
@@ -5606,7 +5618,7 @@ static int handle_vmxoff(struct kvm_vcpu *vcpu)
 	if (!nested_vmx_check_permission(vcpu))
 		return 1;
 
-	free_nested(vcpu);
+	nested_vmx_do_vmxoff(vcpu);
 
 	if (kvm_apic_has_pending_init_or_sipi(vcpu))
 		kvm_make_request(KVM_REQ_EVENT, vcpu);
@@ -6898,7 +6910,7 @@ void vmx_leave_nested(struct kvm_vcpu *vcpu)
 		vcpu->arch.nested_run_pending = 0;
 		nested_vmx_vmexit(vcpu, -1, 0, 0);
 	}
-	free_nested(vcpu);
+	nested_vmx_do_vmxoff(vcpu);
 }
 
 int nested_vmx_check_restored_vmcs12(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 2ef1f634e7ec..292001bbcbff 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -3089,7 +3089,10 @@ void free_loaded_vmcs(struct loaded_vmcs *loaded_vmcs)
 	loaded_vmcs->vmcs = NULL;
 	if (loaded_vmcs->msr_bitmap)
 		free_page((unsigned long)loaded_vmcs->msr_bitmap);
-	WARN_ON(loaded_vmcs->shadow_vmcs != NULL);
+	if (loaded_vmcs->shadow_vmcs) {
+		free_vmcs(loaded_vmcs->shadow_vmcs);
+		loaded_vmcs->shadow_vmcs = NULL;
+	}
 }
 
 int alloc_loaded_vmcs(struct loaded_vmcs *loaded_vmcs)
-- 
2.56.0.385.gac671c819a-goog


  parent reply	other threads:[~2026-10-30 21:21 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 21:20 [PATCH v4 00/23] KVM: Fix+harden against bad uaccess using dying VM Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 01/23] KVM: Reject user accesses to guest memory if current->mm != kvm->mm Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 02/23] KVM: nVMX: Don't force a full nested VM-Exit when destroying a vCPU Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 03/23] KVM: PPC: Flush/zap all memslots on kvm_arch_flush_shadow_all() Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 04/23] KVM: x86: Drop dead check on an existing memslot not having any pages Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 05/23] KVM: WARN and error out if memslot update encounters existing slot with no pages Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 06/23] KVM: x86: Unmap VMAs for KVM-internal memslots when the memslot is freed Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 07/23] KVM: x86: Delete non-SMM internal memslot if creating SMM memslot fails Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 08/23] KVM: x86: Process all address spaces when deleting internal memslot Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 09/23] KVM: x86: Re-queue APICv update request if inhibiting APIC access page fails Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 10/23] KVM: x86: Map a separate VMA for each address space for internal memslots Sean Christopherson
2026-10-30 21:20 ` [PATCH v4 11/23] KVM: Disallow setting memslots when the VM is being destroyed Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 12/23] KVM: Destroy memslots immediately after mmu_notifiers are unregistered Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 13/23] KVM: WARN if KVM attempts to do guest-related uaccess with "wrong" process Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 14/23] KVM: WARN and reject guest-based uaccess if VM is dying Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 15/23] KVM: Pre-check uaccesses in KVM's APIs to read/write guest memory Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 16/23] KVM: VMX: Explicility free vmcs01, not the current VMCS, in vCPU free paths Sean Christopherson
2026-10-30 21:21 ` Sean Christopherson [this message]
2026-10-30 21:21 ` [PATCH v4 18/23] KVM: nVMX: Cancel the nested VMX Preemption Timer's hrtimer in free_nested() Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 19/23] KVM: Add lockdep-only APIs to query and assert that a vCPU is unreachable Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 20/23] KVM: VMX: Explicitly remove dying vCPUs from the PI wakeup list Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 21/23] KVM: nVMX: Don't switch to vmcs01 when freeing a vCPU Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 22/23] KVM: nVMX: Don't load+put the vCPU when freeing nested state for a dying vCPU Sean Christopherson
2026-10-30 21:21 ` [PATCH v4 23/23] KVM: x86: Rename __x86_set_memory_region() => x86_set_internal_memslot() Sean Christopherson

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=20261030212112.2598640-18-seanjc@google.com \
    --to=seanjc@google.com \
    --cc=jmattson@google.com \
    --cc=jthoughton@google.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linuxppc-dev@lists.ozlabs.org \
    --cc=maddy@linux.ibm.com \
    --cc=mason@kernel.org \
    --cc=npiggin@gmail.com \
    --cc=pbonzini@redhat.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

all inboxes | Powered by JetHome®