Skip to content

Commit 2b8272f

Browse files
committed
cpu/hotplug: Prevent self deadlock on CPU hot-unplug
Xiongfeng reported and debugged a self deadlock of the task which initiates and controls a CPU hot-unplug operation vs. the CFS bandwidth timer. CPU1 CPU2 T1 sets cfs_quota starts hrtimer cfs_bandwidth 'period_timer' T1 is migrated to CPU2 T1 initiates offlining of CPU1 Hotplug operation starts ... 'period_timer' expires and is re-enqueued on CPU1 ... take_cpu_down() CPU1 shuts down and does not handle timers anymore. They have to be migrated in the post dead hotplug steps by the control task. T1 runs the post dead offline operation T1 is scheduled out T1 waits for 'period_timer' to expire T1 waits there forever if it is scheduled out before it can execute the hrtimer offline callback hrtimers_dead_cpu(). Cure this by delegating the hotplug control operation to a worker thread on an online CPU. This takes the initiating user space task, which might be affected by the bandwidth timer, completely out of the picture. Reported-by: Xiongfeng Wang <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Tested-by: Yu Liao <[email protected]> Acked-by: Vincent Guittot <[email protected]> Cc: [email protected] Link: https://lore.kernel.org/lkml/[email protected] Link: https://lore.kernel.org/r/87h6oqdq0i.ffs@tglx
1 parent e0a99a8 commit 2b8272f

File tree

1 file changed

+23
-1
lines changed

1 file changed

+23
-1
lines changed

kernel/cpu.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1487,8 +1487,22 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen,
14871487
return ret;
14881488
}
14891489

1490+
struct cpu_down_work {
1491+
unsigned int cpu;
1492+
enum cpuhp_state target;
1493+
};
1494+
1495+
static long __cpu_down_maps_locked(void *arg)
1496+
{
1497+
struct cpu_down_work *work = arg;
1498+
1499+
return _cpu_down(work->cpu, 0, work->target);
1500+
}
1501+
14901502
static int cpu_down_maps_locked(unsigned int cpu, enum cpuhp_state target)
14911503
{
1504+
struct cpu_down_work work = { .cpu = cpu, .target = target, };
1505+
14921506
/*
14931507
* If the platform does not support hotplug, report it explicitly to
14941508
* differentiate it from a transient offlining failure.
@@ -1497,7 +1511,15 @@ static int cpu_down_maps_locked(unsigned int cpu, enum cpuhp_state target)
14971511
return -EOPNOTSUPP;
14981512
if (cpu_hotplug_disabled)
14991513
return -EBUSY;
1500-
return _cpu_down(cpu, 0, target);
1514+
1515+
/*
1516+
* Ensure that the control task does not run on the to be offlined
1517+
* CPU to prevent a deadlock against cfs_b->period_timer.
1518+
*/
1519+
cpu = cpumask_any_but(cpu_online_mask, cpu);
1520+
if (cpu >= nr_cpu_ids)
1521+
return -EBUSY;
1522+
return work_on_cpu(cpu, __cpu_down_maps_locked, &work);
15011523
}
15021524

15031525
static int cpu_down(unsigned int cpu, enum cpuhp_state target)

0 commit comments

Comments
 (0)