Skip to content

Commit 3ad6eb0

Browse files
Frederic WeisbeckerKAGA-KOKO
authored andcommitted
tick: Start centralizing tick related CPU hotplug operations
During the CPU offlining process, the various timer tick features are shut down from scattered places, sometimes from teardown callbacks on stop machine, sometimes through explicit calls, sometimes from the control CPU after the CPU died. The reason why these shutdown operations are spread around is not always clear and it makes the tick lifecycle hard to follow. The tick should be shut down in order from highest to lowest level: On stop machine from the dying CPU (high-level): 1) Hand-over the timekeeping duty (tick_handover_do_timer()) 2) Cancel the tick implementation called by the clockevent callback (tick_cancel_sched_timer()) 3) Shutdown broadcasting (tick_offline_cpu() / tick_broadcast_offline()) On stop machine from the dying CPU (low-level): 4) Shutdown clockevents drivers (CPUHP_AP_*_TIMER_STARTING states) From the control CPU after the CPU died (low-level): 5) Shutdown/unregister/cleanup clockevents for the dead CPU (tick_cleanup_dead_cpu()) Instead the current order is 2, 4 (both from CPU hotplug states), then 1 and 3 through direct calls. This layout and order don't make much sense. The operations 1, 2, 3 should be gathered together and in order. Sort this situation with creating a new TICK shut-down CPU hotplug state and start with introducing the timekeeping duty hand-over there. The state must precede hrtimers migration because the tick hrtimer will be stopped from it in a further patch. Signed-off-by: Frederic Weisbecker <frederic@kernel.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Link: https://lore.kernel.org/r/20240225225508.11587-8-frederic@kernel.org
1 parent 60313c2 commit 3ad6eb0

File tree

4 files changed

+23
-11
lines changed

4 files changed

+23
-11
lines changed

include/linux/cpuhotplug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ enum cpuhp_state {
184184
CPUHP_AP_ARM64_ISNDEP_STARTING,
185185
CPUHP_AP_SMPCFD_DYING,
186186
CPUHP_AP_HRTIMERS_DYING,
187+
CPUHP_AP_TICK_DYING,
187188
CPUHP_AP_X86_TBOOT_DYING,
188189
CPUHP_AP_ARM_CACHE_B15_RAC_DYING,
189190
CPUHP_AP_ONLINE,

include/linux/tick.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,20 @@ extern void __init tick_init(void);
1919
extern void tick_suspend_local(void);
2020
/* Should be core only, but XEN resume magic and ARM BL switcher require it */
2121
extern void tick_resume_local(void);
22-
extern void tick_handover_do_timer(void);
2322
extern void tick_cleanup_dead_cpu(int cpu);
2423
#else /* CONFIG_GENERIC_CLOCKEVENTS */
2524
static inline void tick_init(void) { }
2625
static inline void tick_suspend_local(void) { }
2726
static inline void tick_resume_local(void) { }
28-
static inline void tick_handover_do_timer(void) { }
2927
static inline void tick_cleanup_dead_cpu(int cpu) { }
3028
#endif /* !CONFIG_GENERIC_CLOCKEVENTS */
3129

30+
#if defined(CONFIG_GENERIC_CLOCKEVENTS) && defined(CONFIG_HOTPLUG_CPU)
31+
extern int tick_cpu_dying(unsigned int cpu);
32+
#else
33+
#define tick_cpu_dying NULL
34+
#endif
35+
3236
#if defined(CONFIG_GENERIC_CLOCKEVENTS) && defined(CONFIG_SUSPEND)
3337
extern void tick_freeze(void);
3438
extern void tick_unfreeze(void);

kernel/cpu.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,8 +1324,6 @@ static int take_cpu_down(void *_param)
13241324
*/
13251325
cpuhp_invoke_callback_range_nofail(false, cpu, st, target);
13261326

1327-
/* Give up timekeeping duties */
1328-
tick_handover_do_timer();
13291327
/* Remove CPU from timer broadcasting */
13301328
tick_offline_cpu(cpu);
13311329
/* Park the stopper thread */
@@ -2205,7 +2203,11 @@ static struct cpuhp_step cpuhp_hp_states[] = {
22052203
.startup.single = NULL,
22062204
.teardown.single = hrtimers_cpu_dying,
22072205
},
2208-
2206+
[CPUHP_AP_TICK_DYING] = {
2207+
.name = "tick:dying",
2208+
.startup.single = NULL,
2209+
.teardown.single = tick_cpu_dying,
2210+
},
22092211
/* Entry state on starting. Interrupts enabled from here on. Transient
22102212
* state for synchronsization */
22112213
[CPUHP_AP_ONLINE] = {

kernel/time/tick-common.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -397,15 +397,20 @@ EXPORT_SYMBOL_GPL(tick_broadcast_oneshot_control);
397397

398398
#ifdef CONFIG_HOTPLUG_CPU
399399
/*
400-
* Transfer the do_timer job away from a dying cpu.
401-
*
402-
* Called with interrupts disabled. No locking required. If
403-
* tick_do_timer_cpu is owned by this cpu, nothing can change it.
400+
* Stop the tick and transfer the timekeeping job away from a dying cpu.
404401
*/
405-
void tick_handover_do_timer(void)
402+
int tick_cpu_dying(unsigned int dying_cpu)
406403
{
407-
if (tick_do_timer_cpu == smp_processor_id())
404+
/*
405+
* If the current CPU is the timekeeper, it's the only one that
406+
* can safely hand over its duty. Also all online CPUs are in
407+
* stop machine, guaranteed not to be idle, therefore it's safe
408+
* to pick any online successor.
409+
*/
410+
if (tick_do_timer_cpu == dying_cpu)
408411
tick_do_timer_cpu = cpumask_first(cpu_online_mask);
412+
413+
return 0;
409414
}
410415

411416
/*

0 commit comments

Comments
 (0)