diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index b1f2e4692e66fb83ee08189d77d86487abf40b32..cfc62d447fa0623fd48e55b7c425d74cacd6a419 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -543,9 +543,9 @@ extern void sysrq_timer_list_show(void); int hrtimers_prepare_cpu(unsigned int cpu); #ifdef CONFIG_HOTPLUG_CPU -int hrtimers_dead_cpu(unsigned int cpu); +int hrtimers_cpu_dying(unsigned int cpu); #else -#define hrtimers_dead_cpu NULL +static inline int hrtimers_cpu_dying(unsigned int cpu) { return 0; } #endif #endif diff --git a/include/linux/smp.h b/include/linux/smp.h index 84a0b4828f6618910a05a80f4b3ada218a68878a..812c26f61300cf102d72e838f553ffda46137e0f 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h @@ -278,5 +278,6 @@ int smp_call_on_cpu(unsigned int cpu, int (*func)(void *), void *par, int smpcfd_prepare_cpu(unsigned int cpu); int smpcfd_dead_cpu(unsigned int cpu); int smpcfd_dying_cpu(unsigned int cpu); +int smpcfd_and_hrtimer_dying_cpu(unsigned int cpu); #endif /* __LINUX_SMP_H */ diff --git a/kernel/cpu.c b/kernel/cpu.c index c06ced18f78ad37c7c93263de1ee729204c65606..b7b2560d060bdd290ddb3e654434c5e9ed4fa8b6 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -1589,7 +1589,7 @@ static struct cpuhp_step cpuhp_hp_states[] = { [CPUHP_HRTIMERS_PREPARE] = { .name = "hrtimers:prepare", .startup.single = hrtimers_prepare_cpu, - .teardown.single = hrtimers_dead_cpu, + .teardown.single = NULL, }, [CPUHP_SMPCFD_PREPARE] = { .name = "smpcfd:prepare", @@ -1651,11 +1651,24 @@ static struct cpuhp_step cpuhp_hp_states[] = { .startup.single = NULL, .teardown.single = rcutree_dying_cpu, }, + /* + * In order to fix the kabi breakage, we had to move the hrtimers:dying + * step into smpcfd:dying and create a new function smpcfd_and_hrtimer_dying_cpu(). + * Please ensure that there are no other steps with teardown handler + * between smpcfd:dying and cpu:teardown. + */ [CPUHP_AP_SMPCFD_DYING] = { .name = "smpcfd:dying", .startup.single = NULL, - .teardown.single = smpcfd_dying_cpu, + .teardown.single = smpcfd_and_hrtimer_dying_cpu, }, + + /* + * Attention: Please do not add steps between smpcfd:dying + * and ap:online. Please refer to the above for specific + * reasons. + */ + /* Entry state on starting. Interrupts enabled from here on. Transient * state for synchronsization */ [CPUHP_AP_ONLINE] = { diff --git a/kernel/smp.c b/kernel/smp.c index 114776d0d11eca6901a088966d582f6865927c86..863cf7e2dbdc7ee33da3485001adba054c2e749c 100644 --- a/kernel/smp.c +++ b/kernel/smp.c @@ -75,6 +75,14 @@ int smpcfd_dead_cpu(unsigned int cpu) return 0; } +int smpcfd_and_hrtimer_dying_cpu(unsigned int cpu) +{ + hrtimers_cpu_dying(cpu); + smpcfd_dying_cpu(cpu); + + return 0; +} + int smpcfd_dying_cpu(unsigned int cpu) { /* diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 544ce87ba38a70676e067fb10eb603a4f33cc342..0c44e81c4901b8e4cbc1301fe4ef1edb285ef8ce 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -2112,29 +2112,22 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base, } } -int hrtimers_dead_cpu(unsigned int scpu) +int hrtimers_cpu_dying(unsigned int dying_cpu) { struct hrtimer_cpu_base *old_base, *new_base; - int i; + int i, ncpu = cpumask_first(cpu_active_mask); - BUG_ON(cpu_online(scpu)); - tick_cancel_sched_timer(scpu); + tick_cancel_sched_timer(dying_cpu); + + old_base = this_cpu_ptr(&hrtimer_bases); + new_base = &per_cpu(hrtimer_bases, ncpu); - /* - * 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(&new_base->lock); - raw_spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING); + raw_spin_lock(&old_base->lock); + raw_spin_lock_nested(&new_base->lock, SINGLE_DEPTH_NESTING); for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { migrate_hrtimer_list(&old_base->clock_base[i], @@ -2145,15 +2138,13 @@ int hrtimers_dead_cpu(unsigned int scpu) * The migration might have changed the first expiring softirq * timer on this CPU. Update it. */ - hrtimer_update_softirq_timer(new_base, false); + __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); - raw_spin_unlock(&old_base->lock); raw_spin_unlock(&new_base->lock); + raw_spin_unlock(&old_base->lock); - /* Check, if we got expired work to do */ - __hrtimer_peek_ahead_timers(); - local_irq_enable(); - local_bh_enable(); return 0; }