summaryrefslogtreecommitdiff
path: root/patches/tick-Fix-timer-storm-since-introduction-of-timersd.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/tick-Fix-timer-storm-since-introduction-of-timersd.patch')
-rw-r--r--patches/tick-Fix-timer-storm-since-introduction-of-timersd.patch105
1 files changed, 105 insertions, 0 deletions
diff --git a/patches/tick-Fix-timer-storm-since-introduction-of-timersd.patch b/patches/tick-Fix-timer-storm-since-introduction-of-timersd.patch
new file mode 100644
index 000000000000..3febe509a913
--- /dev/null
+++ b/patches/tick-Fix-timer-storm-since-introduction-of-timersd.patch
@@ -0,0 +1,105 @@
+From: Frederic Weisbecker <frederic@kernel.org>
+Date: Tue, 5 Apr 2022 03:07:52 +0200
+Subject: [PATCH] tick: Fix timer storm since introduction of timersd
+
+If timers are pending while the tick is reprogrammed on nohz_mode, the
+next expiry is not armed to fire now, it is delayed one jiffy forward
+instead so as not to raise an inextinguishable timer storm with such
+scenario:
+
+1) IRQ triggers and queue a timer
+2) ksoftirqd() is woken up
+3) IRQ tail: timer is reprogrammed to fire now
+4) IRQ exit
+5) TIMER interrupt
+6) goto 3)
+
+...all that until we finally reach ksoftirqd.
+
+Unfortunately we are checking the wrong softirq vector bitmask since
+timersd kthread has split from ksoftirqd. Timers now have their own
+vector state field that must be checked separately. As a result, the
+old timer storm is back. This shows up early on boot with extremely long
+initcalls:
+
+ [ 333.004807] initcall dquot_init+0x0/0x111 returned 0 after 323822879 usecs
+
+and the cause is uncovered with the right trace events showing just
+10 microseconds between ticks (~100 000 Hz):
+
+|swapper/-1 1dn.h111 60818582us : hrtimer_expire_entry: hrtimer=00000000e0ef0f6b function=tick_sched_timer now=60415486608
+|swapper/-1 1dn.h111 60818592us : hrtimer_expire_entry: hrtimer=00000000e0ef0f6b function=tick_sched_timer now=60415496082
+|swapper/-1 1dn.h111 60818601us : hrtimer_expire_entry: hrtimer=00000000e0ef0f6b function=tick_sched_timer now=60415505550
+
+Fix this by checking the right timer vector state from the nohz code.
+
+Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Cc: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Link: https://lkml.kernel.org/r/20220405010752.1347437-2-frederic@kernel.org
+---
+ include/linux/interrupt.h | 12 ++++++++++++
+ kernel/softirq.c | 7 +------
+ kernel/time/tick-sched.c | 2 +-
+ 3 files changed, 14 insertions(+), 7 deletions(-)
+
+--- a/include/linux/interrupt.h
++++ b/include/linux/interrupt.h
+@@ -625,9 +625,16 @@ extern void raise_softirq(unsigned int n
+
+ #ifdef CONFIG_PREEMPT_RT
+ DECLARE_PER_CPU(struct task_struct *, timersd);
++DECLARE_PER_CPU(unsigned long, pending_timer_softirq);
++
+ extern void raise_timer_softirq(void);
+ extern void raise_hrtimer_softirq(void);
+
++static inline unsigned int local_pending_timers(void)
++{
++ return __this_cpu_read(pending_timer_softirq);
++}
++
+ #else
+ static inline void raise_timer_softirq(void)
+ {
+@@ -638,6 +645,11 @@ static inline void raise_hrtimer_softirq
+ {
+ raise_softirq_irqoff(HRTIMER_SOFTIRQ);
+ }
++
++static inline unsigned int local_pending_timers(void)
++{
++ return local_softirq_pending();
++}
+ #endif
+
+ DECLARE_PER_CPU(struct task_struct *, ksoftirqd);
+--- a/kernel/softirq.c
++++ b/kernel/softirq.c
+@@ -638,12 +638,7 @@ static inline void tick_irq_exit(void)
+ }
+
+ DEFINE_PER_CPU(struct task_struct *, timersd);
+-static DEFINE_PER_CPU(unsigned long, pending_timer_softirq);
+-
+-static unsigned int local_pending_timers(void)
+-{
+- return __this_cpu_read(pending_timer_softirq);
+-}
++DEFINE_PER_CPU(unsigned long, pending_timer_softirq);
+
+ static void wake_timersd(void)
+ {
+--- a/kernel/time/tick-sched.c
++++ b/kernel/time/tick-sched.c
+@@ -780,7 +780,7 @@ static void tick_nohz_restart(struct tic
+
+ static inline bool local_timer_softirq_pending(void)
+ {
+- return local_softirq_pending() & BIT(TIMER_SOFTIRQ);
++ return local_pending_timers() & BIT(TIMER_SOFTIRQ);
+ }
+
+ static ktime_t tick_nohz_next_event(struct tick_sched *ts, int cpu)