Re: [RFC PATCH v2] sched: irq: cpu-hotplug race vs set_cpus_allowed_ptr()

From: Sebastian Andrzej Siewior

Date: Tue Oct 13 2026 - 10:20:38 EST


On 2026-10-13 08:46:19 [+0200], To Peter Zijlstra wrote:
> > I am struggling to follow along. The actual fail scenario is a
> > combination of hotplug *and* migrate_disable()? Can we get a picture?
>
> let me grab the reproducer and a few tracepoints to make an accurate
> drawing.

CPU1 is actually doing nothing. And migrate_disable() is involved way
later:

CPU0
_cpu_down(1)
set_cpu_dying(1)
/* CPU1 is now present in ONLINE and DYING mask */

<idle or whatever>
try_to_wake_up(p)
-> select_fallback_rq() -> falls back to CPU0 because CPU1 is not available

<irq-thread>
irq_thread_check_affinity()
set_cpus_allowed_ptr(, 1)
/* cpu_valid_mask = cpu_online_mask */
-> affine_move_task(dest_cpu = 1);
-> stop_one_cpu_nowait()

/* switch to migration_cpu_stop-0 */
migration_cpu_stop()
-> __migrate_task(arg->dest_cpu = 1) /* returns rq of 0 */

< back to irq-thread >
set_cpus_allowed_ptr(, 1) return with cpus_ptr set to CPU1 while running
on CPU0.

A schedule out (and sleep) at this point is fine, try_to_wake_up() will
again go to select_fallback_rq() and schedule the task on 0 with proper
::cpus_ptr.

If instead the task does
migrate_disable()
schedule() /* remain running on RQ */
-> migrate_disable_switch()
-> set_cpus_allowed_common()
-> p->cpus_ptr = ctx->new_mask; = CPU0

Once back on CPU0, migrate_enable() does:
___migrate_enable()
-> __set_cpus_allowed_ptr(SCA_MIGRATE_ENABLE | &p->cpus_mask)
/* ::cpus_mask has still the 1 set. There was no wakeup, task
remained on RQ so the mask is not fixed */

Now affine_move_task() will expect task_struct::migration_pending to be
set but it was never set. The task would have remained on the current
CPU (0) if the previous set_cpus_allowed_ptr() would not have set an
invalid CPU mask.

Sebastian