Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

clockevents: Shutdown and unregister current clockevents at CPUHP_AP_TICK_DYING

The way the clockevent devices are finally stopped while a CPU is
offlining is currently chaotic. The layout being by order:

1) tick_sched_timer_dying() stops the tick and the underlying clockevent
but only for oneshot case. The periodic tick and its related
clockevent still runs.

2) tick_broadcast_offline() detaches and stops the per-cpu oneshot
broadcast and append it to the released list.

3) Some individual clockevent drivers stop the clockevents (a second time if
the tick is oneshot)

4) Once the CPU is dead, a control CPU remotely detaches and stops
(a 3rd time if oneshot mode) the CPU clockevent and adds it to the
released list.

5) The released list containing the broadcast device released on step 2)
and the remotely detached clockevent from step 4) are unregistered.

These random events can be factorized if the current clockevent is
detached and stopped by the dying CPU at the generic layer, that is
from the dying CPU:

a) Stop the tick
b) Stop/detach the underlying per-cpu oneshot broadcast clockevent
c) Stop/detach the underlying clockevent
d) Release / unregister the clockevents from b) and c)
e) Release / unregister the remaining clockevents from the dying CPU.
This part could be performed by the dying CPU

This way the drivers and the tick layer don't need to care about
clockevent operations during cpuhotplug down. This also unifies the tick
behaviour on offline CPUs between oneshot and periodic modes, avoiding
offline ticks altogether for sanity.

Adopt the simplification.

[ tglx: Remove the WARN_ON() in clockevents_register_device() as that
is called from an upcoming CPU before the CPU is marked online ]

Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20241029125451.54574-3-frederic@kernel.org

authored by

Frederic Weisbecker and committed by
Thomas Gleixner
3b1596a2 17a8945f

+12 -25
-2
include/linux/tick.h
··· 20 20 extern void tick_suspend_local(void); 21 21 /* Should be core only, but XEN resume magic and ARM BL switcher require it */ 22 22 extern void tick_resume_local(void); 23 - extern void tick_cleanup_dead_cpu(int cpu); 24 23 #else /* CONFIG_GENERIC_CLOCKEVENTS */ 25 24 static inline void tick_init(void) { } 26 25 static inline void tick_suspend_local(void) { } 27 26 static inline void tick_resume_local(void) { } 28 - static inline void tick_cleanup_dead_cpu(int cpu) { } 29 27 #endif /* !CONFIG_GENERIC_CLOCKEVENTS */ 30 28 31 29 #if defined(CONFIG_GENERIC_CLOCKEVENTS) && defined(CONFIG_HOTPLUG_CPU)
-2
kernel/cpu.c
··· 1338 1338 1339 1339 cpuhp_bp_sync_dead(cpu); 1340 1340 1341 - tick_cleanup_dead_cpu(cpu); 1342 - 1343 1341 /* 1344 1342 * Callbacks must be re-integrated right away to the RCU state machine. 1345 1343 * Otherwise an RCU callback could block a further teardown function
+11 -19
kernel/time/clockevents.c
··· 618 618 619 619 #ifdef CONFIG_HOTPLUG_CPU 620 620 621 - # ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST 622 621 /** 623 - * tick_offline_cpu - Take CPU out of the broadcast mechanism 622 + * tick_offline_cpu - Shutdown all clock events related 623 + * to this CPU and take it out of the 624 + * broadcast mechanism. 624 625 * @cpu: The outgoing CPU 625 626 * 626 - * Called on the outgoing CPU after it took itself offline. 627 + * Called by the dying CPU during teardown. 627 628 */ 628 629 void tick_offline_cpu(unsigned int cpu) 629 630 { 630 - raw_spin_lock(&clockevents_lock); 631 - tick_broadcast_offline(cpu); 632 - raw_spin_unlock(&clockevents_lock); 633 - } 634 - # endif 635 - 636 - /** 637 - * tick_cleanup_dead_cpu - Cleanup the tick and clockevents of a dead cpu 638 - * @cpu: The dead CPU 639 - */ 640 - void tick_cleanup_dead_cpu(int cpu) 641 - { 642 631 struct clock_event_device *dev, *tmp; 643 - unsigned long flags; 644 632 645 - raw_spin_lock_irqsave(&clockevents_lock, flags); 633 + raw_spin_lock(&clockevents_lock); 646 634 635 + tick_broadcast_offline(cpu); 647 636 tick_shutdown(cpu); 637 + 648 638 /* 649 639 * Unregister the clock event devices which were 650 - * released from the users in the notify chain. 640 + * released above. 651 641 */ 652 642 list_for_each_entry_safe(dev, tmp, &clockevents_released, list) 653 643 list_del(&dev->list); 644 + 654 645 /* 655 646 * Now check whether the CPU has left unused per cpu devices 656 647 */ ··· 653 662 list_del(&dev->list); 654 663 } 655 664 } 656 - raw_spin_unlock_irqrestore(&clockevents_lock, flags); 665 + 666 + raw_spin_unlock(&clockevents_lock); 657 667 } 658 668 #endif 659 669
+1 -2
kernel/time/tick-internal.h
··· 25 25 extern void tick_setup_periodic(struct clock_event_device *dev, int broadcast); 26 26 extern void tick_handle_periodic(struct clock_event_device *dev); 27 27 extern void tick_check_new_device(struct clock_event_device *dev); 28 + extern void tick_offline_cpu(unsigned int cpu); 28 29 extern void tick_shutdown(unsigned int cpu); 29 30 extern void tick_suspend(void); 30 31 extern void tick_resume(void); ··· 143 142 #endif /* !(BROADCAST && ONESHOT) */ 144 143 145 144 #if defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) && defined(CONFIG_HOTPLUG_CPU) 146 - extern void tick_offline_cpu(unsigned int cpu); 147 145 extern void tick_broadcast_offline(unsigned int cpu); 148 146 #else 149 - static inline void tick_offline_cpu(unsigned int cpu) { } 150 147 static inline void tick_broadcast_offline(unsigned int cpu) { } 151 148 #endif 152 149