Skip to content

Commit 5d62c18

Browse files
committed
nohz: Prevent a timer interrupt storm in tick_nohz_stop_sched_tick()
The conditions in irq_exit() to invoke tick_nohz_irq_exit() which subsequently invokes tick_nohz_stop_sched_tick() are: if ((idle_cpu(cpu) && !need_resched()) || tick_nohz_full_cpu(cpu)) If need_resched() is not set, but a timer softirq is pending then this is an indication that the softirq code punted and delegated the execution to softirqd. need_resched() is not true because the current interrupted task takes precedence over softirqd. Invoking tick_nohz_irq_exit() in this case can cause an endless loop of timer interrupts because the timer wheel contains an expired timer, but softirqs are not yet executed. So it returns an immediate expiry request, which causes the timer to fire immediately again. Lather, rinse and repeat.... Prevent that by adding a check for a pending timer soft interrupt to the conditions in tick_nohz_stop_sched_tick() which avoid calling get_next_timer_interrupt(). That keeps the tick sched timer on the tick and prevents a repetitive programming of an already expired timer. Reported-by: Sebastian Siewior <bigeasy@linutronix.d> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Paul McKenney <paulmck@linux.vnet.ibm.com> Cc: Anna-Maria Gleixner <anna-maria@linutronix.de> Cc: Sebastian Siewior <bigeasy@linutronix.de> Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/alpine.DEB.2.20.1712272156050.2431@nanos
1 parent 26456f8 commit 5d62c18

File tree

1 file changed

+17
-2
lines changed

1 file changed

+17
-2
lines changed

kernel/time/tick-sched.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,11 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
650650
ts->next_tick = 0;
651651
}
652652

653+
static inline bool local_timer_softirq_pending(void)
654+
{
655+
return local_softirq_pending() & TIMER_SOFTIRQ;
656+
}
657+
653658
static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
654659
ktime_t now, int cpu)
655660
{
@@ -666,8 +671,18 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
666671
} while (read_seqretry(&jiffies_lock, seq));
667672
ts->last_jiffies = basejiff;
668673

669-
if (rcu_needs_cpu(basemono, &next_rcu) ||
670-
arch_needs_cpu() || irq_work_needs_cpu()) {
674+
/*
675+
* Keep the periodic tick, when RCU, architecture or irq_work
676+
* requests it.
677+
* Aside of that check whether the local timer softirq is
678+
* pending. If so its a bad idea to call get_next_timer_interrupt()
679+
* because there is an already expired timer, so it will request
680+
* immeditate expiry, which rearms the hardware timer with a
681+
* minimal delta which brings us back to this place
682+
* immediately. Lather, rinse and repeat...
683+
*/
684+
if (rcu_needs_cpu(basemono, &next_rcu) || arch_needs_cpu() ||
685+
irq_work_needs_cpu() || local_timer_softirq_pending()) {
671686
next_tick = basemono + TICK_NSEC;
672687
} else {
673688
/*

0 commit comments

Comments
 (0)