• Valentin Schneider's avatar
    sched/core: Initialize the idle task with preemption disabled · f1a0a376
    Valentin Schneider authored
    As pointed out by commit
    
      de9b8f5d ("sched: Fix crash trying to dequeue/enqueue the idle thread")
    
    init_idle() can and will be invoked more than once on the same idle
    task. At boot time, it is invoked for the boot CPU thread by
    sched_init(). Then smp_init() creates the threads for all the secondary
    CPUs and invokes init_idle() on them.
    
    As the hotplug machinery brings the secondaries to life, it will issue
    calls to idle_thread_get(), which itself invokes init_idle() yet again.
    In this case it's invoked twice more per secondary: at _cpu_up(), and at
    bringup_cpu().
    
    Given smp_init() already initializes the idle tasks for all *possible*
    CPUs, no further initialization should be required. Now, removing
    init_idle() from idle_thread_get() exposes some interesting expectations
    with regards to the idle task's preempt_count: the secondary startup always
    issues a preempt_disable(), requiring some reset of the preempt count to 0
    between hot-unplug and hotplug, which is currently served by
    idle_thread_get() -> idle_init().
    
    Given the idle task is supposed to have preemption disabled once and never
    see it re-enabled, it seems that what we actually want is to initialize its
    preempt_count to PREEMPT_DISABLED and leave it there. Do that, and remove
    init_idle() from idle_thread_get().
    
    Secondary startups were patched via coccinelle:
    
      @begone@
      @@
    
      -preempt_disable();
      ...
      cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
    Signed-off-by: default avatarValentin Schneider <valentin.schneider@arm.com>
    Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
    Acked-by: default avatarPeter Zijlstra <peterz@infradead.org>
    Link: https://lore.kernel.org/r/20210512094636.2958515-1-valentin.schneider@arm.com
    f1a0a376
smp.c 16 KB