Re: [PATCH] sched/core: Don't pin the idle task in migrate_disable_switch()

Peter Zijlstra <[email protected]>
Newsgroups org.kernel.vger.stable,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
On Thu, Aug 06, 2026 at 09:17:40AM +0200, Krystian Slowik wrote:
> Since commit 650952d3fb38 ("sched: Make __do_set_cpus_allowed() use the
> sched_change pattern"), do_set_cpus_allowed() dequeues and re-enqueues
> the target task through the sched_change guard whenever it is queued.
> 
> The idle task counts as queued: init_idle() sets
> idle->on_rq = TASK_ON_RQ_QUEUED. But the idle sched class implements no
> real dequeue_task() (only the "bad: scheduling from the idle thread!"
> debug stub, which even drops and re-takes the rq lock in the middle of
> the guarded section) and no enqueue_task() at all, so running the guard
> on the idle task jumps through a NULL pointer in sched_change_end():
> 
>   bad: scheduling from the idle thread!
>   CPU: 3 UID: 0 PID: 0 Comm: swapper/3 Kdump: loaded Not tainted 7.0.0-28-generic #28-Ubuntu PREEMPT(lazy)
>   Call Trace:
>    dequeue_task_idle+0x29/0x50
>    dequeue_task+0xfb/0x300
>    sched_change_begin+0x1ff/0x240
>    migrate_disable_switch.isra.0+0xf8/0x190
>    __schedule+0xdd/0x650
>    schedule_idle+0x22/0x40
>   BUG: kernel NULL pointer dereference, address: 0000000000000000
>   #PF: supervisor instruction fetch in kernel mode
>   RIP: 0010:0x0
>   Call Trace:
>    enqueue_task+0x89/0x1d0
>    sched_change_end+0x18e/0x1d0
>    migrate_disable_switch.isra.0+0x11e/0x190
>    __schedule+0xdd/0x650
>    schedule_idle+0x22/0x40
>    do_idle+0xb6/0xf0
>    cpu_startup_entry+0x29/0x30
>    start_secondary+0x125/0x180
> 
> The path is reachable since commit 942b8db96500 ("sched: Fix
> migrate_disable_switch() locking") moved migrate_disable_switch() to
> the top of __schedule(), where it runs on every schedule out of the
> idle loop rather than only on an actual context switch: any
> migrate_disable() taken in the idle loop (e.g. from a tracing or BPF
> callback) that is still held when the idle task schedules triggers the
> pinning path.
> 
> Pinning the idle task is meaningless to begin with: it is a per-CPU
> task that can never migrate. Skip it. This also keeps
> ___migrate_enable() unreachable for the idle task, since its cpus_ptr
> is never repointed.
> 
> The check uses p == rq->idle rather than is_idle_task(), because the
> latter also matches idle-injection threads (PF_IDLE), which are
> ordinary queueable tasks.
> 
> Observed in production on two separate x86-64 machines running the
> Ubuntu 7.0.0-28 kernel, both panicking from the idle loop with the
> oops above.

What is actually doing migrate_disable() here? Why would the idle thread
ever hit this...
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.