hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8OSNK CVE: NA
--------------------------------
Commit 4cf0b18c7f5f("hrtimers: Push pending hrtimers away from outgoing CPU earlier") introduce the following issue: - CPU1 goes offline and all hrtimers are migrated to other CPUs - CPU1 continues the offline process and migrates all tasks. In some case, hrtimer_start will be called to restart cfs_period_timer - CPU1 shuts down and does not handle timers, task throttled by CFS bandwidth never get scheduled.
So revert it before we provide a better solution.
This reverts commit 4cf0b18c7f5ff90fe395f38030f8df8fbedc6033.
Signed-off-by: Yu Liao liaoyu15@huawei.com --- include/linux/cpuhotplug.h | 1 - include/linux/hrtimer.h | 4 ++-- kernel/cpu.c | 8 +------- kernel/time/hrtimer.c | 33 +++++++++++++++++++++------------ 4 files changed, 24 insertions(+), 22 deletions(-)
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index a17bb2c393d4..d67c0035165c 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -137,7 +137,6 @@ enum cpuhp_state { CPUHP_AP_ARM_CORESIGHT_STARTING, CPUHP_AP_ARM64_ISNDEP_STARTING, CPUHP_AP_SMPCFD_DYING, - CPUHP_AP_HRTIMERS_DYING, CPUHP_AP_X86_TBOOT_DYING, CPUHP_AP_ARM_CACHE_B15_RAC_DYING, CPUHP_AP_ONLINE, diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 3bdaa92a2cab..542b4fa2cda9 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -508,9 +508,9 @@ extern void sysrq_timer_list_show(void);
int hrtimers_prepare_cpu(unsigned int cpu); #ifdef CONFIG_HOTPLUG_CPU -int hrtimers_cpu_dying(unsigned int cpu); +int hrtimers_dead_cpu(unsigned int cpu); #else -#define hrtimers_cpu_dying NULL +#define hrtimers_dead_cpu NULL #endif
#endif diff --git a/kernel/cpu.c b/kernel/cpu.c index 83f6cc6f6c61..c943454b748e 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1390,7 +1390,7 @@ static struct cpuhp_step cpuhp_hp_states[] = { [CPUHP_HRTIMERS_PREPARE] = { .name = "hrtimers:prepare", .startup.single = hrtimers_prepare_cpu, - .teardown.single = NULL, + .teardown.single = hrtimers_dead_cpu, }, [CPUHP_SMPCFD_PREPARE] = { .name = "smpcfd:prepare", @@ -1457,12 +1457,6 @@ static struct cpuhp_step cpuhp_hp_states[] = { .startup.single = NULL, .teardown.single = smpcfd_dying_cpu, }, - [CPUHP_AP_HRTIMERS_DYING] = { - .name = "hrtimers:dying", - .startup.single = NULL, - .teardown.single = hrtimers_cpu_dying, - }, - /* Entry state on starting. Interrupts enabled from here on. Transient * state for synchronsization */ [CPUHP_AP_ONLINE] = { diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index bf74f43e42af..8512f06f0ebe 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1922,22 +1922,29 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, } }
-int hrtimers_cpu_dying(unsigned int dying_cpu) +int hrtimers_dead_cpu(unsigned int scpu) { struct hrtimer_cpu_base *old_base, *new_base; - int i, ncpu = cpumask_first(cpu_active_mask); - - tick_cancel_sched_timer(dying_cpu); + int i;
- old_base = this_cpu_ptr(&hrtimer_bases); - new_base = &per_cpu(hrtimer_bases, ncpu); + BUG_ON(cpu_online(scpu)); + tick_cancel_sched_timer(scpu);
+ /* + * this BH disable ensures that raise_softirq_irqoff() does + * not wakeup ksoftirqd (and acquire the pi-lock) while + * holding the cpu_base lock + */ + local_bh_disable(); + local_irq_disable(); + old_base = &per_cpu(hrtimer_bases, scpu); + new_base = this_cpu_ptr(&hrtimer_bases); /* * The caller is globally serialized and nobody else * takes two locks at once, deadlock is not possible. */ - raw_spin_lock(&old_base->lock); - raw_spin_lock_nested(&new_base->lock, SINGLE_DEPTH_NESTING); + raw_spin_lock(&new_base->lock); + raw_spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { migrate_hrtimer_list(&old_base->clock_base[i], @@ -1948,13 +1955,15 @@ int hrtimers_cpu_dying(unsigned int dying_cpu) * The migration might have changed the first expiring softirq * timer on this CPU. Update it. */ - __hrtimer_get_next_event(new_base, HRTIMER_ACTIVE_SOFT); - /* Tell the other CPU to retrigger the next event */ - smp_call_function_single(ncpu, retrigger_next_event, NULL, 0); + hrtimer_update_softirq_timer(new_base, false);
- raw_spin_unlock(&new_base->lock); raw_spin_unlock(&old_base->lock); + raw_spin_unlock(&new_base->lock);
+ /* Check, if we got expired work to do */ + __hrtimer_peek_ahead_timers(); + local_irq_enable(); + local_bh_enable(); return 0; }