107 lines
3.6 KiB
Diff
107 lines
3.6 KiB
Diff
|
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
|
||
|
Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/6.11/older/patches-6.11-rt7.tar.xz
|
||
|
|
||
|
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
|
||
|
@@ -618,9 +618,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)
|
||
|
{
|
||
|
@@ -631,6 +638,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
|
||
|
@@ -626,12 +626,7 @@ static inline void tick_irq_exit(void)
|
||
|
|
||
|
#ifdef CONFIG_PREEMPT_RT
|
||
|
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
|
||
|
@@ -859,7 +859,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);
|
||
|
}
|
||
|
|
||
|
/*
|