| From: Thomas Gleixner <tglx@linutronix.de> |
| Date: Fri, 3 Jul 2009 08:44:31 -0500 |
| Subject: hrtimer: fixup hrtimer callback changes for preempt-rt |
| |
| In preempt-rt we can not call the callbacks which take sleeping locks |
| from the timer interrupt context. |
| |
| Bring back the softirq split for now, until we fixed the signal |
| delivery problem for real. |
| |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Ingo Molnar <mingo@elte.hu> |
| |
| --- |
| include/linux/hrtimer.h | 3 |
| kernel/hrtimer.c | 220 ++++++++++++++++++++++++++++++++++++++++------- |
| kernel/sched/core.c | 1 |
| kernel/sched/rt.c | 1 |
| kernel/time/tick-sched.c | 1 |
| kernel/watchdog.c | 1 |
| 6 files changed, 198 insertions(+), 29 deletions(-) |
| |
| --- a/include/linux/hrtimer.h |
| +++ b/include/linux/hrtimer.h |
| @@ -111,6 +111,8 @@ struct hrtimer { |
| enum hrtimer_restart (*function)(struct hrtimer *); |
| struct hrtimer_clock_base *base; |
| unsigned long state; |
| + struct list_head cb_entry; |
| + int irqsafe; |
| #ifdef CONFIG_MISSED_TIMER_OFFSETS_HIST |
| ktime_t praecox; |
| #endif |
| @@ -150,6 +152,7 @@ struct hrtimer_clock_base { |
| int index; |
| clockid_t clockid; |
| struct timerqueue_head active; |
| + struct list_head expired; |
| ktime_t resolution; |
| ktime_t (*get_time)(void); |
| ktime_t softirq_time; |
| --- a/kernel/hrtimer.c |
| +++ b/kernel/hrtimer.c |
| @@ -590,8 +590,7 @@ static int hrtimer_reprogram(struct hrti |
| * When the callback is running, we do not reprogram the clock event |
| * device. The timer callback is either running on a different CPU or |
| * the callback is executed in the hrtimer_interrupt context. The |
| - * reprogramming is handled either by the softirq, which called the |
| - * callback or at the end of the hrtimer_interrupt. |
| + * reprogramming is handled at the end of the hrtimer_interrupt. |
| */ |
| if (hrtimer_callback_running(timer)) |
| return 0; |
| @@ -626,6 +625,9 @@ static int hrtimer_reprogram(struct hrti |
| return res; |
| } |
| |
| +static void __run_hrtimer(struct hrtimer *timer, ktime_t *now); |
| +static int hrtimer_rt_defer(struct hrtimer *timer); |
| + |
| /* |
| * Initialize the high resolution related parts of cpu_base |
| */ |
| @@ -642,9 +644,18 @@ static inline void hrtimer_init_hres(str |
| * and expiry check is done in the hrtimer_interrupt or in the softirq. |
| */ |
| static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
| - struct hrtimer_clock_base *base) |
| + struct hrtimer_clock_base *base, |
| + int wakeup) |
| { |
| - return base->cpu_base->hres_active && hrtimer_reprogram(timer, base); |
| + if (!(base->cpu_base->hres_active && hrtimer_reprogram(timer, base))) |
| + return 0; |
| + if (!wakeup) |
| + return -ETIME; |
| +#ifdef CONFIG_PREEMPT_RT_BASE |
| + if (!hrtimer_rt_defer(timer)) |
| + return -ETIME; |
| +#endif |
| + return 1; |
| } |
| |
| static inline ktime_t hrtimer_update_base(struct hrtimer_cpu_base *base) |
| @@ -725,12 +736,18 @@ static inline int hrtimer_switch_to_hres |
| static inline void |
| hrtimer_force_reprogram(struct hrtimer_cpu_base *base, int skip_equal) { } |
| static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, |
| - struct hrtimer_clock_base *base) |
| + struct hrtimer_clock_base *base, |
| + int wakeup) |
| { |
| return 0; |
| } |
| static inline void hrtimer_init_hres(struct hrtimer_cpu_base *base) { } |
| static inline void retrigger_next_event(void *arg) { } |
| +static inline int hrtimer_reprogram(struct hrtimer *timer, |
| + struct hrtimer_clock_base *base) |
| +{ |
| + return 0; |
| +} |
| |
| #endif /* CONFIG_HIGH_RES_TIMERS */ |
| |
| @@ -862,9 +879,9 @@ void hrtimer_wait_for_timer(const struct |
| { |
| struct hrtimer_clock_base *base = timer->base; |
| |
| - if (base && base->cpu_base && !hrtimer_hres_active(base->cpu_base)) |
| + if (base && base->cpu_base && !timer->irqsafe) |
| wait_event(base->cpu_base->wait, |
| - !(timer->state & HRTIMER_STATE_CALLBACK)); |
| + !(timer->state & HRTIMER_STATE_CALLBACK)); |
| } |
| |
| #else |
| @@ -914,6 +931,11 @@ static void __remove_hrtimer(struct hrti |
| if (!(timer->state & HRTIMER_STATE_ENQUEUED)) |
| goto out; |
| |
| + if (unlikely(!list_empty(&timer->cb_entry))) { |
| + list_del_init(&timer->cb_entry); |
| + goto out; |
| + } |
| + |
| next_timer = timerqueue_getnext(&base->active); |
| timerqueue_del(&base->active, &timer->node); |
| if (&timer->node == next_timer) { |
| @@ -1021,9 +1043,19 @@ int __hrtimer_start_range_ns(struct hrti |
| * |
| * XXX send_remote_softirq() ? |
| */ |
| - if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases) |
| - && hrtimer_enqueue_reprogram(timer, new_base)) { |
| - if (wakeup) { |
| + if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases)) { |
| + ret = hrtimer_enqueue_reprogram(timer, new_base, wakeup); |
| + if (ret < 0) { |
| + /* |
| + * 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. |
| + */ |
| + debug_deactivate(timer); |
| + __remove_hrtimer(timer, new_base, |
| + timer->state & HRTIMER_STATE_CALLBACK, 0); |
| + } else if (ret > 0) { |
| /* |
| * We need to drop cpu_base->lock to avoid a |
| * lock ordering issue vs. rq->lock. |
| @@ -1031,9 +1063,7 @@ int __hrtimer_start_range_ns(struct hrti |
| raw_spin_unlock(&new_base->cpu_base->lock); |
| raise_softirq_irqoff(HRTIMER_SOFTIRQ); |
| local_irq_restore(flags); |
| - return ret; |
| - } else { |
| - __raise_softirq_irqoff(HRTIMER_SOFTIRQ); |
| + return 0; |
| } |
| } |
| |
| @@ -1200,6 +1230,7 @@ static void __hrtimer_init(struct hrtime |
| |
| base = hrtimer_clockid_to_base(clock_id); |
| timer->base = &cpu_base->clock_base[base]; |
| + INIT_LIST_HEAD(&timer->cb_entry); |
| timerqueue_init(&timer->node); |
| |
| #ifdef CONFIG_TIMER_STATS |
| @@ -1283,10 +1314,128 @@ static void __run_hrtimer(struct hrtimer |
| timer->state &= ~HRTIMER_STATE_CALLBACK; |
| } |
| |
| -#ifdef CONFIG_HIGH_RES_TIMERS |
| - |
| static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer); |
| |
| +#ifdef CONFIG_PREEMPT_RT_BASE |
| +static void hrtimer_rt_reprogram(int restart, struct hrtimer *timer, |
| + struct hrtimer_clock_base *base) |
| +{ |
| + /* |
| + * Note, we clear the callback flag before we requeue the |
| + * timer otherwise we trigger the callback_running() check |
| + * in hrtimer_reprogram(). |
| + */ |
| + timer->state &= ~HRTIMER_STATE_CALLBACK; |
| + |
| + if (restart != HRTIMER_NORESTART) { |
| + BUG_ON(hrtimer_active(timer)); |
| + /* |
| + * Enqueue the timer, if it's the leftmost timer then |
| + * we need to reprogram it. |
| + */ |
| + if (!enqueue_hrtimer(timer, base)) |
| + return; |
| + |
| +#ifndef CONFIG_HIGH_RES_TIMERS |
| + } |
| +#else |
| + if (base->cpu_base->hres_active && |
| + hrtimer_reprogram(timer, base)) |
| + goto requeue; |
| + |
| + } else if (hrtimer_active(timer)) { |
| + /* |
| + * If the timer was rearmed on another CPU, reprogram |
| + * the event device. |
| + */ |
| + if (&timer->node == base->active.next && |
| + base->cpu_base->hres_active && |
| + hrtimer_reprogram(timer, base)) |
| + goto requeue; |
| + } |
| + return; |
| + |
| +requeue: |
| + /* |
| + * Timer is expired. Thus move it from tree to pending list |
| + * again. |
| + */ |
| + __remove_hrtimer(timer, base, timer->state, 0); |
| + list_add_tail(&timer->cb_entry, &base->expired); |
| +#endif |
| +} |
| + |
| +/* |
| + * The changes in mainline which removed the callback modes from |
| + * hrtimer are not yet working with -rt. The non wakeup_process() |
| + * based callbacks which involve sleeping locks need to be treated |
| + * seperately. |
| + */ |
| +static void hrtimer_rt_run_pending(void) |
| +{ |
| + enum hrtimer_restart (*fn)(struct hrtimer *); |
| + struct hrtimer_cpu_base *cpu_base; |
| + struct hrtimer_clock_base *base; |
| + struct hrtimer *timer; |
| + int index, restart; |
| + |
| + local_irq_disable(); |
| + cpu_base = &per_cpu(hrtimer_bases, smp_processor_id()); |
| + |
| + raw_spin_lock(&cpu_base->lock); |
| + |
| + for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) { |
| + base = &cpu_base->clock_base[index]; |
| + |
| + while (!list_empty(&base->expired)) { |
| + timer = list_first_entry(&base->expired, |
| + struct hrtimer, cb_entry); |
| + |
| + /* |
| + * Same as the above __run_hrtimer function |
| + * just we run with interrupts enabled. |
| + */ |
| + debug_hrtimer_deactivate(timer); |
| + __remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0); |
| + timer_stats_account_hrtimer(timer); |
| + fn = timer->function; |
| + |
| + raw_spin_unlock_irq(&cpu_base->lock); |
| + restart = fn(timer); |
| + raw_spin_lock_irq(&cpu_base->lock); |
| + |
| + hrtimer_rt_reprogram(restart, timer, base); |
| + } |
| + } |
| + |
| + raw_spin_unlock_irq(&cpu_base->lock); |
| + |
| + wake_up_timer_waiters(cpu_base); |
| +} |
| + |
| +static int hrtimer_rt_defer(struct hrtimer *timer) |
| +{ |
| + if (timer->irqsafe) |
| + return 0; |
| + |
| + __remove_hrtimer(timer, timer->base, timer->state, 0); |
| + list_add_tail(&timer->cb_entry, &timer->base->expired); |
| + return 1; |
| +} |
| + |
| +#else |
| + |
| +static inline void hrtimer_rt_run_pending(void) |
| +{ |
| + hrtimer_peek_ahead_timers(); |
| +} |
| + |
| +static inline int hrtimer_rt_defer(struct hrtimer *timer) { return 0; } |
| + |
| +#endif |
| + |
| +#ifdef CONFIG_HIGH_RES_TIMERS |
| + |
| /* |
| * High resolution timer interrupt |
| * Called with interrupts disabled |
| @@ -1295,7 +1444,7 @@ void hrtimer_interrupt(struct clock_even |
| { |
| struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); |
| ktime_t expires_next, now, entry_time, delta; |
| - int i, retries = 0; |
| + int i, retries = 0, raise = 0; |
| |
| BUG_ON(!cpu_base->hres_active); |
| cpu_base->nr_events++; |
| @@ -1362,7 +1511,10 @@ retry: |
| break; |
| } |
| |
| - __run_hrtimer(timer, &basenow); |
| + if (!hrtimer_rt_defer(timer)) |
| + __run_hrtimer(timer, &basenow); |
| + else |
| + raise = 1; |
| } |
| } |
| |
| @@ -1377,6 +1529,10 @@ retry: |
| if (expires_next.tv64 == KTIME_MAX || |
| !tick_program_event(expires_next, 0)) { |
| cpu_base->hang_detected = 0; |
| + |
| + if (raise) |
| + raise_softirq_irqoff(HRTIMER_SOFTIRQ); |
| + |
| return; |
| } |
| |
| @@ -1457,24 +1613,26 @@ void hrtimer_peek_ahead_timers(void) |
| local_irq_restore(flags); |
| } |
| |
| +#else /* CONFIG_HIGH_RES_TIMERS */ |
| + |
| +static inline void __hrtimer_peek_ahead_timers(void) { } |
| + |
| +#endif /* !CONFIG_HIGH_RES_TIMERS */ |
| + |
| static void run_hrtimer_softirq(struct softirq_action *h) |
| { |
| +#ifdef CONFIG_HIGH_RES_TIMERS |
| struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); |
| |
| if (cpu_base->clock_was_set) { |
| cpu_base->clock_was_set = 0; |
| clock_was_set(); |
| } |
| +#endif |
| |
| - hrtimer_peek_ahead_timers(); |
| + hrtimer_rt_run_pending(); |
| } |
| |
| -#else /* CONFIG_HIGH_RES_TIMERS */ |
| - |
| -static inline void __hrtimer_peek_ahead_timers(void) { } |
| - |
| -#endif /* !CONFIG_HIGH_RES_TIMERS */ |
| - |
| /* |
| * Called from timer softirq every jiffy, expire hrtimers: |
| * |
| @@ -1507,7 +1665,7 @@ void hrtimer_run_queues(void) |
| struct timerqueue_node *node; |
| struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); |
| struct hrtimer_clock_base *base; |
| - int index, gettime = 1; |
| + int index, gettime = 1, raise = 0; |
| |
| if (hrtimer_hres_active()) |
| return; |
| @@ -1532,12 +1690,16 @@ void hrtimer_run_queues(void) |
| hrtimer_get_expires_tv64(timer)) |
| break; |
| |
| - __run_hrtimer(timer, &base->softirq_time); |
| + if (!hrtimer_rt_defer(timer)) |
| + __run_hrtimer(timer, &base->softirq_time); |
| + else |
| + raise = 1; |
| } |
| raw_spin_unlock(&cpu_base->lock); |
| } |
| |
| - wake_up_timer_waiters(cpu_base); |
| + if (raise) |
| + raise_softirq_irqoff(HRTIMER_SOFTIRQ); |
| } |
| |
| /* |
| @@ -1559,6 +1721,7 @@ static enum hrtimer_restart hrtimer_wake |
| void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task) |
| { |
| sl->timer.function = hrtimer_wakeup; |
| + sl->timer.irqsafe = 1; |
| sl->task = task; |
| } |
| EXPORT_SYMBOL_GPL(hrtimer_init_sleeper); |
| @@ -1695,6 +1858,7 @@ static void __cpuinit init_hrtimers_cpu( |
| for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) { |
| cpu_base->clock_base[i].cpu_base = cpu_base; |
| timerqueue_init_head(&cpu_base->clock_base[i].active); |
| + INIT_LIST_HEAD(&cpu_base->clock_base[i].expired); |
| } |
| |
| hrtimer_init_hres(cpu_base); |
| @@ -1813,9 +1977,7 @@ void __init hrtimers_init(void) |
| hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE, |
| (void *)(long)smp_processor_id()); |
| register_cpu_notifier(&hrtimers_nb); |
| -#ifdef CONFIG_HIGH_RES_TIMERS |
| open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq); |
| -#endif |
| } |
| |
| /** |
| --- a/kernel/sched/core.c |
| +++ b/kernel/sched/core.c |
| @@ -489,6 +489,7 @@ static void init_rq_hrtick(struct rq *rq |
| |
| hrtimer_init(&rq->hrtick_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| rq->hrtick_timer.function = hrtick; |
| + rq->hrtick_timer.irqsafe = 1; |
| } |
| #else /* CONFIG_SCHED_HRTICK */ |
| static inline void hrtick_clear(struct rq *rq) |
| --- a/kernel/sched/rt.c |
| +++ b/kernel/sched/rt.c |
| @@ -41,6 +41,7 @@ void init_rt_bandwidth(struct rt_bandwid |
| |
| hrtimer_init(&rt_b->rt_period_timer, |
| CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| + rt_b->rt_period_timer.irqsafe = 1; |
| rt_b->rt_period_timer.function = sched_rt_period_timer; |
| } |
| |
| --- a/kernel/time/tick-sched.c |
| +++ b/kernel/time/tick-sched.c |
| @@ -867,6 +867,7 @@ void tick_setup_sched_timer(void) |
| * Emulate tick processing via per-CPU hrtimers: |
| */ |
| hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); |
| + ts->sched_timer.irqsafe = 1; |
| ts->sched_timer.function = tick_sched_timer; |
| |
| /* Get the next period (per cpu) */ |
| --- a/kernel/watchdog.c |
| +++ b/kernel/watchdog.c |
| @@ -358,6 +358,7 @@ static void watchdog_enable(unsigned int |
| /* kick off the timer for the hardlockup detector */ |
| hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
| hrtimer->function = watchdog_timer_fn; |
| + hrtimer->irqsafe = 1; |
| |
| if (!watchdog_enabled) { |
| kthread_park(current); |