linux/kernel/sched
Peter Zijlstra 20195f87db sched: Fix stop_one_cpu_nowait() vs hotplug
[ Upstream commit f0498d2a54 ]

Kuyo reported sporadic failures on a sched_setaffinity() vs CPU
hotplug stress-test -- notably affine_move_task() remains stuck in
wait_for_completion(), leading to a hung-task detector warning.

Specifically, it was reported that stop_one_cpu_nowait(.fn =
migration_cpu_stop) returns false -- this stopper is responsible for
the matching complete().

The race scenario is:

	CPU0					CPU1

					// doing _cpu_down()

  __set_cpus_allowed_ptr()
    task_rq_lock();
					takedown_cpu()
					  stop_machine_cpuslocked(take_cpu_down..)

					<PREEMPT: cpu_stopper_thread()
					  MULTI_STOP_PREPARE
					  ...
    __set_cpus_allowed_ptr_locked()
      affine_move_task()
        task_rq_unlock();

  <PREEMPT: cpu_stopper_thread()\>
    ack_state()
					  MULTI_STOP_RUN
					    take_cpu_down()
					      __cpu_disable();
					      stop_machine_park();
						stopper->enabled = false;
					 />
   />
	stop_one_cpu_nowait(.fn = migration_cpu_stop);
          if (stopper->enabled) // false!!!

That is, by doing stop_one_cpu_nowait() after dropping rq-lock, the
stopper thread gets a chance to preempt and allows the cpu-down for
the target CPU to complete.

OTOH, since stop_one_cpu_nowait() / cpu_stop_queue_work() needs to
issue a wakeup, it must not be ran under the scheduler locks.

Solve this apparent contradiction by keeping preemption disabled over
the unlock + queue_stopper combination:

	preempt_disable();
	task_rq_unlock(...);
	if (!stop_pending)
	  stop_one_cpu_nowait(...)
	preempt_enable();

This respects the lock ordering contraints while still avoiding the
above race. That is, if we find the CPU is online under rq-lock, the
targeted stop_one_cpu_nowait() must succeed.

Apply this pattern to all similar stop_one_cpu_nowait() invocations.

Fixes: 6d337eab04 ("sched: Fix migrate_disable() vs set_cpus_allowed_ptr()")
Reported-by: "Kuyo Chang (張建文)" <Kuyo.Chang@mediatek.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: "Kuyo Chang (張建文)" <Kuyo.Chang@mediatek.com>
Link: https://lkml.kernel.org/r/20231010200442.GA16515@noisy.programming.kicks-ass.net
Signed-off-by: Sasha Levin <sashal@kernel.org>
2023-11-20 11:08:13 +01:00
..
autogroup.c sched/fair: Prevent dead task groups from regaining cfs_rq's 2021-11-25 09:48:32 +01:00
autogroup.h
clock.c sched: Fix various typos 2021-03-22 00:11:52 +01:00
completion.c
core_sched.c sched: prctl() core-scheduling interface 2021-05-12 11:43:31 +02:00
core.c sched: Fix stop_one_cpu_nowait() vs hotplug 2023-11-20 11:08:13 +01:00
cpuacct.c sched/cpuacct: Optimize away RCU read lock 2023-10-06 13:18:19 +02:00
cpudeadline.c sched/core: Introduce sched_asym_cpucap_active() 2022-12-31 13:14:01 +01:00
cpudeadline.h
cpufreq_schedutil.c sched/uclamp: Fix iowait boost escaping uclamp restriction 2022-04-08 14:23:10 +02:00
cpufreq.c
cpupri.c sched/rt: Fix live lock between select_fallback_rq() and RT push 2023-10-06 13:18:22 +02:00
cpupri.h sched/cpupri: Add CPUPRI_HIGHER 2020-10-29 11:00:30 +01:00
cputime.c cputime, cpuacct: Include guest time in user time in cpuacct.stat 2022-01-27 11:05:09 +01:00
deadline.c sched: Fix stop_one_cpu_nowait() vs hotplug 2023-11-20 11:08:13 +01:00
debug.c sched: Fix DEBUG && !SCHEDSTATS warn 2023-05-11 23:00:40 +09:00
fair.c sched: Fix stop_one_cpu_nowait() vs hotplug 2023-11-20 11:08:13 +01:00
features.h sched/fair: Introduce SIS_UTIL to search idle CPU based on sum of util_avg 2022-08-17 14:23:00 +02:00
idle.c Revert "kernel/sched: Modify initial boot task idle setup" 2023-10-19 23:05:38 +02:00
isolation.c sched/isolation: Reconcile rcu_nocbs= and nohz_full= 2021-05-13 14:12:47 +02:00
loadavg.c sched: Make multiple runqueue task counters 32-bit 2021-05-12 21:34:17 +02:00
Makefile sched: Trivial core scheduling cookie management 2021-05-12 11:43:31 +02:00
membarrier.c sched/membarrier: Fix membarrier-rseq fence command missing from query bitmask 2022-02-01 17:27:05 +01:00
pelt.c sched: Fix various typos 2021-03-22 00:11:52 +01:00
pelt.h sched/fair: Fix cfs_rq_clock_pelt() for throttled cfs_rq 2022-06-09 10:22:48 +02:00
psi.c sched/psi: Fix use-after-free in ep_remove_wait_queue() 2023-02-22 12:57:06 +01:00
rt.c sched: Fix stop_one_cpu_nowait() vs hotplug 2023-11-20 11:08:13 +01:00
sched-pelt.h
sched.h sched/deadline: Create DL BW alloc, free & check overflow interface 2023-08-30 16:18:20 +02:00
smp.h
stats.c sched: Fix various typos 2021-03-22 00:11:52 +01:00
stats.h sched: Make struct sched_statistics independent of fair sched class 2023-05-11 23:00:34 +09:00
stop_task.c sched: Make struct sched_statistics independent of fair sched class 2023-05-11 23:00:34 +09:00
swait.c
topology.c sched/topology: Skip updating masks for non-online nodes 2021-08-20 12:32:57 +02:00
wait_bit.c
wait.c wait: add wake_up_pollfree() 2021-12-14 10:57:15 +01:00