aboutsummaryrefslogtreecommitdiff
path: root/kernel/timer.c
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2015-03-16 15:54:29 -0400
committerAnders Roxell <anders.roxell@linaro.org>2015-06-01 12:03:39 +0200
commitea60c8a4d0c5f0ad17fcf77032c8e86c18c41a2c (patch)
tree634f5e1fc3a8b4c6371d060ad0afb78bebcf4490 /kernel/timer.c
parent53be47e5c1fa3b4edce74a21771c6008163e91ab (diff)
Revert "timers: do not raise softirq unconditionally"
This reverts commit 891f510568343d93c5aa2f477b6bebe009b48f05. An issue arisen that if a rt_mutex (spin_lock converted to a mutex in PREEMPT_RT) is taken in hard interrupt context, it could cause a false deadlock detection and trigger a BUG_ON() from the return value of task_blocks_on_rt_mutex() in rt_spin_lock_slowlock(). The problem is this: CPU0 CPU1 ---- ---- spin_lock(A) spin_lock(A) [ blocks, but spins as owner on CPU 0 is running ] <interrupt> spin_trylock(B) [ succeeds ] spin_lock(B) <blocks> Now the deadlock detection triggers and follows the locking: Task X (on CPU0) blocked on spinlock B owned by task Y on CPU1 (via the interrupt taking it with a try lock) The owner of B (Y) is blocked on spin_lock A (still spinning) A is owned by task X (self). DEADLOCK detected! BUG_ON triggered. This was caused by the code to try to not raise softirq unconditionally to allow NO_HZ_FULL to work. Unfortunately, reverting that patch causes NO_HZ_FULL to break again, but that's still better than triggering a BUG_ON(). Signed-off-by: Steven Rostedt <rostedt@goodmis.org> Conflicts: kernel/timer.c
Diffstat (limited to 'kernel/timer.c')
-rw-r--r--kernel/timer.c46
1 files changed, 2 insertions, 44 deletions
diff --git a/kernel/timer.c b/kernel/timer.c
index 2059f6b27595..34fd2dbba3e3 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1464,6 +1464,8 @@ static void run_timer_softirq(struct softirq_action *h)
{
struct tvec_base *base = __this_cpu_read(tvec_bases);
+ hrtimer_run_pending();
+
#if defined(CONFIG_IRQ_WORK) && defined(CONFIG_PREEMPT_RT_FULL)
irq_work_run();
#endif
@@ -1477,52 +1479,8 @@ static void run_timer_softirq(struct softirq_action *h)
*/
void run_local_timers(void)
{
- struct tvec_base *base = __this_cpu_read(tvec_bases);
-
hrtimer_run_queues();
- /*
- * We can access this lockless as we are in the timer
- * interrupt. If there are no timers queued, nothing to do in
- * the timer softirq.
- */
-#ifdef CONFIG_PREEMPT_RT_FULL
-
-#ifndef CONFIG_SMP
- /*
- * The spin_do_trylock() later may fail as the lock may be hold before
- * the interrupt arrived. The spin-lock debugging code will raise a
- * warning if the try_lock fails on UP. Since this is only an
- * optimization for the FULL_NO_HZ case (not to run the timer softirq on
- * an nohz_full CPU) we don't really care and shedule the softirq.
- */
raise_softirq(TIMER_SOFTIRQ);
- return;
-#endif
-
- /* On RT, irq work runs from softirq */
- if (irq_work_needs_cpu()) {
- raise_softirq(TIMER_SOFTIRQ);
- return;
- }
-
- if (!spin_do_trylock(&base->lock)) {
- raise_softirq(TIMER_SOFTIRQ);
- return;
- }
-#endif
-
- if (!base->active_timers)
- goto out;
-
- /* Check whether the next pending timer has expired */
- if (time_before_eq(base->next_timer, jiffies))
- raise_softirq(TIMER_SOFTIRQ);
-out:
-#ifdef CONFIG_PREEMPT_RT_FULL
- rt_spin_unlock_after_trylock_in_irq(&base->lock);
-#endif
- /* The ; ensures that gcc won't complain in the !RT case */
- ;
}
#ifdef __ARCH_WANT_SYS_ALARM