summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Zijlstra <a.p.zijlstra@chello.nl>2011-08-12 17:39:54 +0200
committerClark Williams <williams@redhat.com>2012-04-13 11:01:32 -0500
commit44c24a8833c71bb627cbff691199787d1b520fa8 (patch)
tree534cf8088ad236f18f673d0500a9393f36ff41e8
parent6a5fc8d53d37b53035d610336b1aa24b5c97b5f8 (diff)
hrtimer: Don't call the timer handler from hrtimer_start
[<ffffffff812de4a9>] __delay+0xf/0x11 [<ffffffff812e36e9>] do_raw_spin_lock+0xd2/0x13c [<ffffffff815028ee>] _raw_spin_lock+0x60/0x73 rt_b->rt_runtime_lock [<ffffffff81068f68>] ? sched_rt_period_timer+0xad/0x281 [<ffffffff81068f68>] sched_rt_period_timer+0xad/0x281 [<ffffffff8109e5e1>] __run_hrtimer+0x1e4/0x347 [<ffffffff81068ebb>] ? enqueue_rt_entity+0x36/0x36 [<ffffffff8109f2b1>] __hrtimer_start_range_ns+0x2b5/0x40a base->cpu_base->lock (lock_hrtimer_base) [<ffffffff81068b6f>] __enqueue_rt_entity+0x26f/0x2aa rt_b->rt_runtime_lock (start_rt_bandwidth) [<ffffffff81068ead>] enqueue_rt_entity+0x28/0x36 [<ffffffff81069355>] enqueue_task_rt+0x3d/0xb0 [<ffffffff810679d6>] enqueue_task+0x5d/0x64 [<ffffffff810714fc>] task_setprio+0x210/0x29c rq->lock [<ffffffff810b56cb>] __rt_mutex_adjust_prio+0x25/0x2a p->pi_lock [<ffffffff810b5d2c>] task_blocks_on_rt_mutex+0x196/0x20f Instead make __hrtimer_start_range_ns() return -ETIME when the timer is in the past. Since body actually uses the hrtimer_start*() return value its pretty safe to wreck it. Also, it will only ever return -ETIME for timer->irqsafe || !wakeup timers. Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
-rw-r--r--kernel/hrtimer.c48
1 files changed, 23 insertions, 25 deletions
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index 1dd627ba799e..358442b0421e 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -646,37 +646,24 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer,
struct hrtimer_clock_base *base,
int wakeup)
{
-#ifdef CONFIG_PREEMPT_RT_BASE
-again:
if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) {
+ if (!wakeup)
+ return -ETIME;
+
+#ifdef CONFIG_PREEMPT_RT_BASE
/*
* Move softirq based timers away from the rbtree in
* case it expired already. Otherwise we would have a
* stale base->first entry until the softirq runs.
*/
- if (!hrtimer_rt_defer(timer)) {
- ktime_t now = ktime_get();
-
- __run_hrtimer(timer, &now);
- /*
- * __run_hrtimer might have requeued timer and
- * it could be base->first again.
- */
- if (&timer->node == base->active.next)
- goto again;
- return 1;
- }
-#else
- if (base->cpu_base->hres_active && hrtimer_reprogram(timer, base)) {
+ if (!hrtimer_rt_defer(timer))
+ return -ETIME;
#endif
- if (wakeup) {
- raw_spin_unlock(&base->cpu_base->lock);
- raise_softirq_irqoff(HRTIMER_SOFTIRQ);
- raw_spin_lock(&base->cpu_base->lock);
- } else
- __raise_softirq_irqoff(HRTIMER_SOFTIRQ);
+ raw_spin_unlock(&base->cpu_base->lock);
+ raise_softirq_irqoff(HRTIMER_SOFTIRQ);
+ raw_spin_lock(&base->cpu_base->lock);
- return 1;
+ return 0;
}
return 0;
@@ -1046,8 +1033,19 @@ int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
*
* XXX send_remote_softirq() ?
*/
- if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases))
- hrtimer_enqueue_reprogram(timer, new_base, wakeup);
+ if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)) {
+ ret = hrtimer_enqueue_reprogram(timer, new_base, wakeup);
+ if (ret) {
+ /*
+ * In case we failed to reprogram the timer (mostly
+ * because out current timer is already elapsed),
+ * remove it again and report a failure. This avoids
+ * stale base->first entries.
+ */
+ __remove_hrtimer(timer, new_base,
+ timer->state & HRTIMER_STATE_CALLBACK, 0);
+ }
+ }
unlock_hrtimer_base(timer, &flags);