summaryrefslogtreecommitdiffstats
path: root/softirq-Sanitize-softirq-pending-for-NOHZ-RT.patch
blob: 3c333c5dfb889e13418ec56b8aada93d3c653f3f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
From 0016728c7248109eabf7e73f03e20b12ca332c40 Mon Sep 17 00:00:00 2001
From: Thomas Gleixner <tglx@linutronix.de>
Date: Fri, 3 Jul 2009 13:16:38 -0500
Subject: [PATCH] softirq: Sanitize softirq pending for NOHZ/RT

commit 1b5c1881af922ba4a3c793ec9afb240885293370 in tip.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
---
 include/linux/interrupt.h |    1 +
 kernel/softirq.c          |   68 +++++++++++++++++++++++++++++++++++++++++++++
 kernel/time/tick-sched.c  |    8 +----
 3 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 0fd5b27..9f6580a 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -390,6 +390,7 @@ extern void open_softirq(int nr, void (*action)(struct softirq_action *));
 extern void softirq_init(void);
 extern void raise_softirq_irqoff(unsigned int nr);
 extern void raise_softirq(unsigned int nr);
+extern void softirq_check_pending_idle(void);
 
 /* This is the worklist that queues up per-cpu softirq work.
  *
diff --git a/kernel/softirq.c b/kernel/softirq.c
index b4b1819..f0b863e 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -76,6 +76,74 @@ char *softirq_to_name[NR_SOFTIRQS] = {
 	"TASKLET", "SCHED", "HRTIMER",	"RCU"
 };
 
+#ifdef CONFIG_PREEMPT_RT
+/*
+ * On preempt-rt a softirq might be blocked on a lock. There might be
+ * no other runnable task on this CPU because the lock owner runs on
+ * some other CPU. So we have to go into idle with the pending bit
+ * set. Therefor we need to check this otherwise we warn about false
+ * positives which confuses users and defeats the whole purpose of
+ * this test.
+ *
+ * This code is called with interrupts disabled.
+ */
+void softirq_check_pending_idle(void)
+{
+	static int rate_limit;
+	u32 warnpending = 0, pending = local_softirq_pending();
+	int curr = 0;
+
+	if (rate_limit >= 10)
+		return;
+
+	while (pending) {
+		if (pending & 1) {
+			struct task_struct *tsk;
+
+			tsk = __get_cpu_var(ksoftirqd)[curr].tsk;
+			/*
+			 * The wakeup code in rtmutex.c wakes up the
+			 * task _before_ it sets pi_blocked_on to NULL
+			 * under tsk->pi_lock. So we need to check for
+			 * both: state and pi_blocked_on.
+			 */
+			raw_spin_lock(&tsk->pi_lock);
+
+			if (!tsk->pi_blocked_on &&
+			    !(tsk->state == TASK_RUNNING) &&
+			    !(tsk->state & TASK_RUNNING_MUTEX))
+				warnpending |= 1 << curr;
+
+			raw_spin_unlock(&tsk->pi_lock);
+		}
+		pending >>= 1;
+		curr++;
+	}
+
+	if (warnpending) {
+		printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n",
+		       warnpending);
+		rate_limit++;
+	}
+}
+
+#else
+/*
+ * On !PREEMPT_RT we just printk rate limited:
+ */
+void softirq_check_pending_idle(void)
+{
+	static int rate_limit;
+
+	if (rate_limit < 10) {
+		printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n",
+		       local_softirq_pending());
+		rate_limit++;
+	}
+}
+
+#endif
+
 /*
  * we cannot loop indefinitely here to avoid userspace starvation,
  * but we also don't want to introduce a worst case 1/HZ latency
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index a85776e..a521150 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -252,13 +252,7 @@ void tick_nohz_stop_sched_tick(int inidle)
 		goto end;
 
 	if (unlikely(local_softirq_pending() && cpu_online(cpu))) {
-		static int ratelimit;
-
-		if (ratelimit < 10) {
-			printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n",
-			       (unsigned int) local_softirq_pending());
-			ratelimit++;
-		}
+		softirq_check_pending_idle();
 		goto end;
 	}
 
-- 
1.7.0.4