Skip to content

Commit f55acb1

Browse files
Frederic WeisbeckerKAGA-KOKO
Frederic Weisbecker
authored andcommitted
timers/migration: Fix endless timer requeue after idle interrupts
When a CPU is an idle migrator, but another CPU wakes up before it, becomes an active migrator and handles the queue, the initial idle migrator may end up endlessly reprogramming its clockevent, chasing ghost timers forever such as in the following scenario: [GRP0:0] migrator = 0 active = 0 nextevt = T1 / \ 0 1 active idle (T1) 0) CPU 1 is idle and has a timer queued (T1), CPU 0 is active and is the active migrator. [GRP0:0] migrator = NONE active = NONE nextevt = T1 / \ 0 1 idle idle (T1) wakeup = T1 1) CPU 0 is now idle and is therefore the idle migrator. It has programmed its next timer interrupt to handle T1. [GRP0:0] migrator = 1 active = 1 nextevt = KTIME_MAX / \ 0 1 idle active wakeup = T1 2) CPU 1 has woken up, it is now active and it has just handled its own timer T1. 3) CPU 0 gets a timer interrupt to handle T1 but tmigr_handle_remote() realize it is not the migrator anymore. So it early returns without observing that T1 has been expired already and therefore without updating its ->wakeup value. 4) CPU 0 goes into tmigr_cpu_new_timer() which also early returns because it doesn't queue a timer of its own. So ->wakeup is left unchanged and the next timer is programmed to fire now. 5) goto 3) forever This results in timer interrupt storms in idle and also in nohz_full (as observed in rcutorture's TREE07 scenario). Fix this with forcing a re-evaluation of tmc->wakeup while trying remote timer handling when the CPU isn't the migrator anymmore. The check is inherently racy but in the worst case the CPU just races setting the KTIME_MAX value that a remote expiry also tries to set. Fixes: 7ee9887 ("timers: Implement the hierarchical pull model") Reported-by: Paul E. McKenney <[email protected]> Signed-off-by: Frederic Weisbecker <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 4b6f4c5 commit f55acb1

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

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)