diff options
author | Peter Zijlstra <a.p.zijlstra@chello.nl> | 2011-08-12 17:39:54 +0200 |
---|---|---|
committer | Clark Williams <williams@redhat.com> | 2012-04-13 11:01:32 -0500 |
commit | 44c24a8833c71bb627cbff691199787d1b520fa8 (patch) | |
tree | 534cf8088ad236f18f673d0500a9393f36ff41e8 | |
parent | 6a5fc8d53d37b53035d610336b1aa24b5c97b5f8 (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.c | 48 |
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); |