Skip to content

Commit 7029324

Browse files
committed
Merge tag 'timers-urgent-2024-03-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer fixes from Thomas Gleixner: "Two regression fixes for the timer and timer migration code: - Prevent endless timer requeuing which is caused by two CPUs racing out of idle. This happens when the last CPU goes idle and therefore has to ensure to expire the pending global timers and some other CPU come out of idle at the same time and the other CPU wins the race and expires the global queue. This causes the last CPU to chase ghost timers forever and reprogramming it's clockevent device endlessly. Cure this by re-evaluating the wakeup time unconditionally. - The split into local (pinned) and global timers in the timer wheel caused a regression for NOHZ full as it broke the idle tracking of global timers. On NOHZ full this prevents an self IPI being sent which in turn causes the timer to be not programmed and not being expired on time. Restore the idle tracking for the global timer base so that the self IPI condition for NOHZ full is working correctly again" * tag 'timers-urgent-2024-03-23' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: timers: Fix removed self-IPI on global timer's enqueue in nohz_full timers/migration: Fix endless timer requeue after idle interrupts
2 parents 00164f4 + 0387703 commit 7029324

File tree

2 files changed

+20
-3
lines changed

2 files changed

+20
-3
lines changed

kernel/time/timer.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,8 @@ trigger_dyntick_cpu(struct timer_base *base, struct timer_list *timer)
642642
* the base lock:
643643
*/
644644
if (base->is_idle) {
645-
WARN_ON_ONCE(!(timer->flags & TIMER_PINNED));
645+
WARN_ON_ONCE(!(timer->flags & TIMER_PINNED ||
646+
tick_nohz_full_cpu(base->cpu)));
646647
wake_up_nohz_cpu(base->cpu);
647648
}
648649
}
@@ -2292,6 +2293,13 @@ static inline u64 __get_next_timer_interrupt(unsigned long basej, u64 basem,
22922293
*/
22932294
if (!base_local->is_idle && time_after(nextevt, basej + 1)) {
22942295
base_local->is_idle = true;
2296+
/*
2297+
* Global timers queued locally while running in a task
2298+
* in nohz_full mode need a self-IPI to kick reprogramming
2299+
* in IRQ tail.
2300+
*/
2301+
if (tick_nohz_full_cpu(base_local->cpu))
2302+
base_global->is_idle = true;
22952303
trace_timer_base_idle(true, base_local->cpu);
22962304
}
22972305
*idle = base_local->is_idle;
@@ -2364,6 +2372,8 @@ void timer_clear_idle(void)
23642372
* path. Required for BASE_LOCAL only.
23652373
*/
23662374
__this_cpu_write(timer_bases[BASE_LOCAL].is_idle, false);
2375+
if (tick_nohz_full_cpu(smp_processor_id()))
2376+
__this_cpu_write(timer_bases[BASE_GLOBAL].is_idle, false);
23672377
trace_timer_base_idle(false, smp_processor_id());
23682378

23692379
/* Activate without holding the timer_base->lock */

kernel/time/timer_migration.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,8 +1038,15 @@ void tmigr_handle_remote(void)
10381038
* in tmigr_handle_remote_up() anyway. Keep this check to speed up the
10391039
* return when nothing has to be done.
10401040
*/
1041-
if (!tmigr_check_migrator(tmc->tmgroup, tmc->childmask))
1042-
return;
1041+
if (!tmigr_check_migrator(tmc->tmgroup, tmc->childmask)) {
1042+
/*
1043+
* If this CPU was an idle migrator, make sure to clear its wakeup
1044+
* value so it won't chase timers that have already expired elsewhere.
1045+
* This avoids endless requeue from tmigr_new_timer().
1046+
*/
1047+
if (READ_ONCE(tmc->wakeup) == KTIME_MAX)
1048+
return;
1049+
}
10431050

10441051
data.now = get_jiffies_update(&data.basej);
10451052

0 commit comments

Comments
 (0)