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 v3 13/15] KVM: VMX: Explicitly remove dying vCPUs from the PI wakeup list
Date: Tue, 27 Oct 2026 15:12:06 -0700 [thread overview]
Message-ID: <20261027221209.1560865-14-seanjc@google.com> (raw)
In-Reply-To: <20261027221209.1560865-1-seanjc@google.com>
Explicitly remove dying vCPUs that were blocking when the VM was released
from the Posted Interrupts wakeup list instead of very, very subtly relying
on the vcpu_load() in nested_vmx_free_vcpu() to take the vCPU off said list.
In addition to helping avoid future regressions, e.g. if an enterprising
developer were to optimize nested_vmx_free_vcpu() by bailing early if
"nested" is completely disabled, this will allow dropping the load+put pair
from nested_vmx_free_vcpu().
Provide a dedicated API to remove a vCPU from the wakeup list, i.e. don't
_completely_ abuse vt_vcpu_pi_load(), but do reuse the core logic for
updating the PID control word. Using a CMPXCHG8B _should_ be unnecessary
as IRQ routing has already been torn down, i.e. no IRTEs should be pointing
at updating the PID, and there are obviously no other references to the VM
or vCPU, and so concurrently modifications of the PID should be impossible.
For the same reasons, updating the destination should also be unnecessary,
as nothing should ever send an IRQ. However, relying on those assumptions
begs for WARNs to ensure they hold, along with fallback code to avoid the
worst case scenario, at which point trying to take advantage of the fact
that the PID is unreachable would be *more* complex than what is now
__vt_vcpu_pi_load(), not to mention KVM would have two paths to maintain.
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
arch/x86/kvm/vmx/posted_intr.c | 68 +++++++++++++++++++++-------------
arch/x86/kvm/vmx/posted_intr.h | 1 +
arch/x86/kvm/vmx/vmx.c | 2 +
3 files changed, 45 insertions(+), 26 deletions(-)
diff --git a/arch/x86/kvm/vmx/posted_intr.c b/arch/x86/kvm/vmx/posted_intr.c
index 2644add54b76..7414fb551d2e 100644
--- a/arch/x86/kvm/vmx/posted_intr.c
+++ b/arch/x86/kvm/vmx/posted_intr.c
@@ -56,35 +56,13 @@ static int pi_try_set_control(struct pi_desc *pi_desc, u64 *pold, u64 new)
return 0;
}
-void vt_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
+static void __vt_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
{
struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
- struct vcpu_vt *vt = to_vt(vcpu);
struct pi_desc old, new;
- unsigned long flags;
unsigned int dest;
- /*
- * To simplify hot-plug and dynamic toggling of APICv, keep PI.NDST and
- * PI.SN up-to-date even if there is no assigned device or if APICv is
- * deactivated due to a dynamic inhibit bit, e.g. for Hyper-V's SyncIC.
- */
- if (!enable_apicv || !lapic_in_kernel(vcpu))
- return;
-
- /*
- * If the vCPU wasn't on the wakeup list and wasn't migrated, then the
- * full update can be skipped as neither the vector nor the destination
- * needs to be changed. Clear SN even if there is no assigned device,
- * again for simplicity.
- */
- if (pi_desc->nv != POSTED_INTR_WAKEUP_VECTOR && vcpu->cpu == cpu) {
- if (pi_test_and_clear_sn(pi_desc))
- goto after_clear_sn;
- return;
- }
-
- local_irq_save(flags);
+ guard(irqsave)();
/*
* If the vCPU was waiting for wakeup, remove the vCPU from the wakeup
@@ -103,7 +81,7 @@ void vt_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
*/
raw_spin_lock(spinlock);
spin_acquire(&spinlock->dep_map, PI_LOCK_SCHED_OUT, 0, _RET_IP_);
- list_del(&vt->pi_wakeup_list);
+ list_del(&to_vt(vcpu)->pi_wakeup_list);
spin_release(&spinlock->dep_map, _RET_IP_);
raw_spin_unlock(spinlock);
}
@@ -129,8 +107,33 @@ void vt_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
*/
new.nv = POSTED_INTR_VECTOR;
} while (pi_try_set_control(pi_desc, &old.control, new.control));
+}
- local_irq_restore(flags);
+void vt_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
+{
+ struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
+
+ /*
+ * To simplify hot-plug and dynamic toggling of APICv, keep PI.NDST and
+ * PI.SN up-to-date even if there is no assigned device or if APICv is
+ * deactivated due to a dynamic inhibit bit, e.g. for Hyper-V's SyncIC.
+ */
+ if (!enable_apicv || !lapic_in_kernel(vcpu))
+ return;
+
+ /*
+ * If the vCPU wasn't on the wakeup list and wasn't migrated, then the
+ * full update can be skipped as neither the vector nor the destination
+ * needs to be changed. Clear SN even if there is no assigned device,
+ * again for simplicity.
+ */
+ if (pi_desc->nv != POSTED_INTR_WAKEUP_VECTOR && vcpu->cpu == cpu) {
+ if (pi_test_and_clear_sn(pi_desc))
+ goto after_clear_sn;
+ return;
+ }
+
+ __vt_vcpu_pi_load(vcpu, cpu);
after_clear_sn:
@@ -146,6 +149,19 @@ void vt_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
pi_set_on(pi_desc);
}
+void vt_vcpu_pi_remove_from_wakeup_list(struct kvm_vcpu *vcpu)
+{
+ WARN_ON_ONCE(refcount_read(&vcpu->kvm->users_count));
+
+ if (!enable_apicv || !lapic_in_kernel(vcpu) ||
+ vcpu_to_pi_desc(vcpu)->nv != POSTED_INTR_WAKEUP_VECTOR)
+ return;
+
+ __vt_vcpu_pi_load(vcpu, get_cpu());
+
+ put_cpu();
+}
+
static bool vt_can_use_vtd_pi(struct kvm *kvm)
{
/*
diff --git a/arch/x86/kvm/vmx/posted_intr.h b/arch/x86/kvm/vmx/posted_intr.h
index c5860ea828ae..1d9ccbc2bb6f 100644
--- a/arch/x86/kvm/vmx/posted_intr.h
+++ b/arch/x86/kvm/vmx/posted_intr.h
@@ -10,6 +10,7 @@
void vt_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu);
void vt_vcpu_pi_put(struct kvm_vcpu *vcpu);
+void vt_vcpu_pi_remove_from_wakeup_list(struct kvm_vcpu *vcpu);
void pi_wakeup_handler(void);
void __init pi_init_cpu(int cpu);
void pi_apicv_pre_state_restore(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 292001bbcbff..1f9e43c5b4c4 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -7514,6 +7514,8 @@ void vmx_vcpu_free(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ vt_vcpu_pi_remove_from_wakeup_list(vcpu);
+
if (enable_pml)
vmx_destroy_pml_buffer(vcpu);
free_vpid(vmx->vpid);
--
2.56.0.307.g5fe7bd680c-goog
next prev parent reply other threads:[~2026-10-27 22:12 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 22:11 [PATCH v3 00/15] KVM: Fix+harden against bad uaccess using dying VM Sean Christopherson
2026-10-27 22:11 ` [PATCH v3 01/15] KVM: Reject user accesses to guest memory if current->mm != kvm->mm Sean Christopherson
2026-10-27 22:11 ` [PATCH v3 02/15] KVM: nVMX: Don't force a full nested VM-Exit when destroying a vCPU Sean Christopherson
2026-10-27 22:54 ` Sean Christopherson
2026-10-27 22:11 ` [PATCH v3 03/15] KVM: PPC: Flush/zap all memslots on kvm_arch_flush_shadow_all() Sean Christopherson
2026-10-27 22:11 ` [PATCH v3 04/15] KVM: x86: Unmap VMAs for KVM-internal memslots when the memslot is freed Sean Christopherson
2026-10-27 22:11 ` [PATCH v3 05/15] KVM: Disallow setting memslots when the VM is being destroyed Sean Christopherson
2026-10-27 22:11 ` [PATCH v3 06/15] KVM: Destroy memslots immediately after mmu_notifiers are unregistered Sean Christopherson
2026-10-27 22:12 ` [PATCH v3 07/15] KVM: WARN if KVM attempts to do guest-related uaccess with "wrong" process Sean Christopherson
2026-10-27 22:12 ` [PATCH v3 08/15] KVM: WARN and reject guest-based uaccess if VM is dying Sean Christopherson
2026-10-28 0:26 ` James Houghton
2026-10-27 22:12 ` [PATCH v3 09/15] KVM: Pre-check uaccesses in KVM's APIs to read/write guest memory Sean Christopherson
2026-10-28 0:22 ` James Houghton
2026-10-27 22:12 ` [PATCH v3 10/15] KVM: VMX: Explicility free vmcs01, not the current VMCS, in vCPU free paths Sean Christopherson
2026-10-28 0:33 ` James Houghton
2026-10-27 22:12 ` [PATCH v3 11/15] KVM: nVMX: Don't explicitly VMCLEAR+free() vmcs01's shadow VMCS when freeing vCPU Sean Christopherson
2026-10-27 22:12 ` [PATCH v3 12/15] KVM: nVMX: Cancel the nested VMX Preemption Timer's hrtimer in free_nested() Sean Christopherson
2026-10-27 22:12 ` Sean Christopherson [this message]
2026-10-27 22:12 ` [PATCH v3 14/15] KVM: nVMX: Don't switch to vmcs01 when freeing a vCPU Sean Christopherson
2026-10-27 22:12 ` [PATCH v3 15/15] KVM: nVMX: Don't load+put the vCPU when freeing nested state for a dying vCPU Sean Christopherson
2026-10-28 9:18 ` [syzbot ci] Re: KVM: Fix+harden against bad uaccess using dying VM syzbot ci
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=20261027221209.1560865-14-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®