All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jinmeng Zhou" <jinmengzhou22@gmail.com>
Cc: kvm@vger.kernel.org
Subject: Re: [PATCH v2 2/2] KVM: x86: Wake blocked vCPUs before offlining a CPU
Date: Tue, 20 Oct 2026 10:08:32 +0000	[thread overview]
Message-ID: <sashiko-outbox-178795@kernel.org> (raw)
In-Reply-To: <20261020085401.80296-3-jinmengzhou22@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] KVM: VMX: out-of-bounds per-CPU access in vmx_vcpu_pi_load() on TDX CPU offline
- [High] KVM: x86: pi_cpu_offline() runs before CPUHP_AP_SCHED_WAIT_EMPTY drains tasks
--

commit e59f5fb4482718a249da5dc7f86ef0922b551270
Author: Jinmeng Zhou <zhoujinmeng@bytedance.com>

KVM: x86: Wake blocked vCPUs before offlining a CPU

This commit adds an optional x86 vendor cpu_offline() callback invoked during
CPUHP_AP_X86_KVM_ONLINE teardown and implements it for VMX to wake blocked
vCPUs on the outgoing CPU's posted-interrupt wakeup list so their
posted-interrupt destination state is migrated to an active CPU.

> diff --git a/arch/x86/kvm/vmx/posted_intr.c b/arch/x86/kvm/vmx/posted_intr.c
> index 4a6d9a17da238..1905472b336d3 100644
> --- a/arch/x86/kvm/vmx/posted_intr.c
> +++ b/arch/x86/kvm/vmx/posted_intr.c
> @@ -266,6 +266,24 @@ void pi_wakeup_handler(void)
>  	raw_spin_unlock(spinlock);
>  }
>  
> +void pi_cpu_offline(unsigned int cpu)
> +{
> +	struct list_head *wakeup_list = &per_cpu(wakeup_vcpus_on_cpu, cpu);
> +	raw_spinlock_t *spinlock = &per_cpu(wakeup_vcpus_on_cpu_lock, cpu);
> +	struct vcpu_vt *vt;
> +	unsigned long flags;
> +
> +	/*
> +	 * CPUHP_AP_ACTIVE has already deactivated the CPU.  Wake blocked vCPUs
> +	 * so that they are scheduled on another CPU and rebuild their posted
> +	 * interrupt wakeup state in vmx_vcpu_pi_load().
> +	 */
> +	raw_spin_lock_irqsave(spinlock, flags);
> +	list_for_each_entry(vt, wakeup_list, pi_wakeup_list)
> +		__kvm_vcpu_wake_up(vt_to_vcpu(vt));
> +	raw_spin_unlock_irqrestore(spinlock, flags);
> +}

[Severity: Critical]
Can this trigger an out-of-bounds per-CPU access in vmx_vcpu_pi_load() when
offlining a CPU with a blocked TDX vCPU that uses VT-d posted interrupts?

When a TDX vCPU blocks on CPU C with posted interrupts enabled,
tdx_vcpu_put() -> vmx_vcpu_pi_put() -> pi_enable_wakeup_handler() adds
vt->pi_wakeup_list to per_cpu(wakeup_vcpus_on_cpu, C) and sets pi_desc->nv to
POSTED_INTR_WAKEUP_VECTOR while the vCPU is on per_cpu(associated_tdvcpus, C)
with vcpu->cpu == C.

During CPU hot-unplug of CPU C, pi_cpu_offline(C) calls __kvm_vcpu_wake_up(),
which asynchronously wakes the vCPU task on another active CPU D without
removing vt->pi_wakeup_list or waiting for the vCPU task to schedule in on
CPU D.

As CPU C continues hotplug teardown to CPUHP_AP_KVM_ONLINE (since
CPUHP_AP_SCHED_WAIT_EMPTY only waits for CPU C's own runqueue to drain),
kvm_offline_cpu() -> kvm_disable_virtualization_cpu() ->
vt_disable_virtualization_cpu() -> tdx_disable_virtualization_cpu() ->
tdx_flush_vp() -> tdx_disassociate_vp() runs on CPU C and resets vcpu->cpu to
-1:

arch/x86/kvm/vmx/tdx.c:tdx_disassociate_vp() {
	lockdep_assert_irqs_disabled();

	list_del(&to_tdx(vcpu)->cpu_list);
	...
	smp_wmb();

	vcpu->cpu = -1;
}

When the woken TDX vCPU task subsequently schedules in on CPU D,
kvm_sched_in() -> kvm_arch_vcpu_load() -> vt_vcpu_load() -> tdx_vcpu_load()
calls vmx_vcpu_pi_load(vcpu, D) before vcpu->cpu is updated at the end of
kvm_arch_vcpu_load():

arch/x86/kvm/vmx/posted_intr.c:vmx_vcpu_pi_load() {
	...
	if (pi_desc->nv == POSTED_INTR_WAKEUP_VECTOR) {
		raw_spinlock_t *spinlock = &per_cpu(wakeup_vcpus_on_cpu_lock, vcpu->cpu);

		raw_spin_lock(spinlock);
		spin_acquire(&spinlock->dep_map, PI_LOCK_SCHED_OUT, 0, _RET_IP_);
		list_del(&vt->pi_wakeup_list);
		spin_release(&spinlock->dep_map, _RET_IP_);
		raw_spin_unlock(spinlock);
	}
}

With vcpu->cpu == -1, doesn't &per_cpu(wakeup_vcpus_on_cpu_lock, vcpu->cpu)
index __per_cpu_offset[-1] out of bounds, pass an invalid spinlock pointer to
raw_spin_lock(), and delete vt->pi_wakeup_list without holding CPU C's
wakeup_vcpus_on_cpu_lock?

> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index a08ae25f373fb..38bffe6ec9b4b 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -6823,8 +6823,9 @@ static int kvm_x86_cpu_online(unsigned int cpu)
>  static int kvm_x86_cpu_offline(unsigned int cpu)
>  {
>  	if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC))
> -		return kvmclock_cpu_down_prep(cpu);
> +		kvmclock_cpu_down_prep(cpu);
>  
> +	kvm_x86_call(cpu_offline)(cpu);
>  	return 0;
>  }

[Severity: High]
Could a vCPU task in a migrate_disable() region still block on the outgoing
CPU and add itself to wakeup_vcpus_on_cpu after kvm_x86_cpu_offline() has
already walked the list?

During CPU teardown, callbacks run in descending order of enum cpuhp_state:

include/linux/cpuhotplug.h:
	CPUHP_AP_KVM_ONLINE,
	CPUHP_AP_SCHED_WAIT_EMPTY,
	...
	CPUHP_AP_X86_KVM_ONLINE,
	CPUHP_AP_ACTIVE,

In CPUHP_AP_ACTIVE, sched_cpu_deactivate() clears cpu_active_mask and enables
balance_push_set(cpu, true), but draining tasks in migrate_disable() regions
on the outgoing CPU happens later at CPUHP_AP_SCHED_WAIT_EMPTY via
sched_cpu_wait_empty() -> balance_hotplug_wait().

If a vCPU task running vcpu_run() enters a migrate_disable() region (for
example on CONFIG_PREEMPT_RT where spinlock_t, rwlock_t such as kvm->mmu_lock,
and local_lock_t call migrate_disable(), or via bpf_prog_run_pin_on_cpu())
and gets preempted or blocks on an rtmutex, __schedule() calls
migrate_disable_switch(), setting p->cpus_ptr = cpumask_of(cpu) while
p->cpus_mask retains its normal affinity mask.

CPUHP_AP_X86_KVM_ONLINE then runs kvm_x86_cpu_offline() -> pi_cpu_offline(cpu)
while the vCPU is not in kvm_vcpu_block(), and the hotplug thread waits at
CPUHP_AP_SCHED_WAIT_EMPTY in balance_hotplug_wait() because
rq_has_pinned_tasks(rq) is true.

When the vCPU task switches back in on the dying CPU, finish_lock_switch() ->
__balance_callbacks() -> balance_push() returns early because migration is
disabled:

kernel/sched/core.c:balance_push() {
	...
	if (kthread_is_per_cpu(push_task) ||
	    is_migration_disabled(push_task)) {
		...
		return;
	}
}

When the vCPU task exits its migrate_disable() region, __migrate_enable() ->
___migrate_enable() -> __set_cpus_allowed_ptr() -> affine_move_task() checks
whether the current CPU is still in p->cpus_mask:

kernel/sched/core.c:affine_move_task() {
	...
	if (cpumask_test_cpu(task_cpu(p), &p->cpus_mask) ||
	    (task_current_donor(rq, p) && !task_current(rq, p))) {
		struct task_struct *push_task = NULL;

		if ((flags & SCA_MIGRATE_ENABLE) &&
		    (p->migration_flags & MDF_PUSH) && !rq->push_busy) {
			rq->push_busy = true;
			push_task = get_task_struct(p);
		}
		...
		return 0;
	}
}

Because offline CPUs are not cleared from p->cpus_mask for tasks in
top_cpuset, and MDF_PUSH was not set (MDF_PUSH is only set by RT/DL
push_cpu_stop()), affine_move_task() returns 0 without migrating the task or
setting TIF_NEED_RESCHED.

If the vCPU task then reaches kvm_vcpu_halt() -> kvm_vcpu_block() ->
schedule() before another preemption occurs, kvm_sched_out() ->
vmx_vcpu_pi_put() -> pi_enable_wakeup_handler() will add vt->pi_wakeup_list to
per_cpu(wakeup_vcpus_on_cpu, cpu) with pi_desc.ndst targeting the offline CPU
after pi_cpu_offline(cpu) has already finished.

Should pi_cpu_offline() instead be invoked after CPUHP_AP_SCHED_WAIT_EMPTY
(for instance from the CPUHP_AP_KVM_ONLINE stage, which was placed below
CPUHP_AP_SCHED_WAIT_EMPTY so balance_hotplug_wait() has already drained all
vCPU tasks from the outgoing CPU)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261020085401.80296-1-jinmengzhou22@gmail.com?part=2

      reply	other threads:[~2026-10-20 10:08 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-24  8:06 [PATCH] KVM: x86: Wake blocked vCPUs before offlining a CPU Jinmeng Zhou
2026-09-24 14:55 ` Sean Christopherson
2026-10-20  8:53 ` [PATCH v2 0/2] " Jinmeng Zhou
2026-10-20  8:54   ` [PATCH v2 1/2] KVM: x86: Make the kvmclock CPU hotplug state generic Jinmeng Zhou
2026-10-20  8:54   ` [PATCH v2 2/2] KVM: x86: Wake blocked vCPUs before offlining a CPU Jinmeng Zhou
2026-10-20 10:08     ` sashiko-bot [this message]

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=sashiko-outbox-178795@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=jinmengzhou22@gmail.com \
    --cc=kvm@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.