blob: ca198578c16f988ffc3f0e982cebd6407d1463d9 [file] [log] [blame]
From 62d3301d1f68a084ff40f564f34ae7411214b352 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>
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.1.1