| From b926c8ec02044923580578ea9a9e8801095b25de Mon Sep 17 00:00:00 2001 |
| From: Ingo Molnar <mingo@elte.hu> |
| Date: Fri, 3 Jul 2009 08:29:34 -0500 |
| Subject: [PATCH] hrtimers: prepare full preemption |
| |
| commit 20634762996dac9138fc968785ae0f41c896aad0 in tip. |
| |
| Make cancellation of a running callback in softirq context safe |
| against preemption. |
| |
| Signed-off-by: Ingo Molnar <mingo@elte.hu> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h |
| index 5d86fb2..67945c3 100644 |
| --- a/include/linux/hrtimer.h |
| +++ b/include/linux/hrtimer.h |
| @@ -180,6 +180,9 @@ struct hrtimer_cpu_base { |
| unsigned long nr_hangs; |
| ktime_t max_hang_time; |
| #endif |
| +#ifdef CONFIG_PREEMPT_SOFTIRQS |
| + wait_queue_head_t wait; |
| +#endif |
| }; |
| |
| static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) |
| @@ -367,6 +370,13 @@ static inline int hrtimer_restart(struct hrtimer *timer) |
| return hrtimer_start_expires(timer, HRTIMER_MODE_ABS); |
| } |
| |
| +/* Softirq preemption could deadlock timer removal */ |
| +#ifdef CONFIG_PREEMPT_SOFTIRQS |
| + extern void hrtimer_wait_for_timer(const struct hrtimer *timer); |
| +#else |
| +# define hrtimer_wait_for_timer(timer) do { cpu_relax(); } while (0) |
| +#endif |
| + |
| /* Query timers: */ |
| extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer); |
| extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp); |
| diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c |
| index 54cf84f..c586357 100644 |
| --- a/kernel/hrtimer.c |
| +++ b/kernel/hrtimer.c |
| @@ -889,6 +889,32 @@ static int enqueue_hrtimer(struct hrtimer *timer, |
| return leftmost; |
| } |
| |
| +#ifdef CONFIG_PREEMPT_SOFTIRQS |
| +# define wake_up_timer_waiters(b) wake_up(&(b)->wait) |
| + |
| +/** |
| + * hrtimer_wait_for_timer - Wait for a running timer |
| + * |
| + * @timer: timer to wait for |
| + * |
| + * The function waits in case the timers callback function is |
| + * currently executed on the waitqueue of the timer base. The |
| + * waitqueue is woken up after the timer callback function has |
| + * finished execution. |
| + */ |
| +void hrtimer_wait_for_timer(const struct hrtimer *timer) |
| +{ |
| + struct hrtimer_clock_base *base = timer->base; |
| + |
| + if (base && base->cpu_base) |
| + wait_event(base->cpu_base->wait, |
| + !(timer->state & HRTIMER_STATE_CALLBACK)); |
| +} |
| + |
| +#else |
| +# define wake_up_timer_waiters(b) do { } while (0) |
| +#endif |
| + |
| /* |
| * __remove_hrtimer - internal function to remove a timer |
| * |
| @@ -927,6 +953,8 @@ static void __remove_hrtimer(struct hrtimer *timer, |
| rb_erase(&timer->node, &base->active); |
| out: |
| timer->state = newstate; |
| + |
| + wake_up_timer_waiters(base->cpu_base); |
| } |
| |
| /* |
| @@ -1085,7 +1113,7 @@ int hrtimer_cancel(struct hrtimer *timer) |
| |
| if (ret >= 0) |
| return ret; |
| - cpu_relax(); |
| + hrtimer_wait_for_timer(timer); |
| } |
| } |
| EXPORT_SYMBOL_GPL(hrtimer_cancel); |
| @@ -1631,6 +1659,9 @@ static void __cpuinit init_hrtimers_cpu(int cpu) |
| cpu_base->clock_base[i].cpu_base = cpu_base; |
| |
| hrtimer_init_hres(cpu_base); |
| +#ifdef CONFIG_PREEMPT_SOFTIRQS |
| + init_waitqueue_head(&cpu_base->wait); |
| +#endif |
| } |
| |
| #ifdef CONFIG_HOTPLUG_CPU |
| diff --git a/kernel/itimer.c b/kernel/itimer.c |
| index d802883..2c582fc 100644 |
| --- a/kernel/itimer.c |
| +++ b/kernel/itimer.c |
| @@ -214,6 +214,7 @@ again: |
| /* We are sharing ->siglock with it_real_fn() */ |
| if (hrtimer_try_to_cancel(timer) < 0) { |
| spin_unlock_irq(&tsk->sighand->siglock); |
| + hrtimer_wait_for_timer(&tsk->signal->real_timer); |
| goto again; |
| } |
| expires = timeval_to_ktime(value->it_value); |
| diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c |
| index 2114ed0..d2818dd 100644 |
| --- a/kernel/posix-timers.c |
| +++ b/kernel/posix-timers.c |
| @@ -831,6 +831,7 @@ retry: |
| |
| unlock_timer(timr, flag); |
| if (error == TIMER_RETRY) { |
| + hrtimer_wait_for_timer(&timr->it.real.timer); |
| rtn = NULL; // We already got the old time... |
| goto retry; |
| } |
| @@ -869,6 +870,7 @@ retry_delete: |
| |
| if (timer_delete_hook(timer) == TIMER_RETRY) { |
| unlock_timer(timer, flags); |
| + hrtimer_wait_for_timer(&timer->it.real.timer); |
| goto retry_delete; |
| } |
| |
| @@ -898,6 +900,7 @@ retry_delete: |
| |
| if (timer_delete_hook(timer) == TIMER_RETRY) { |
| unlock_timer(timer, flags); |
| + hrtimer_wait_for_timer(&timer->it.real.timer); |
| goto retry_delete; |
| } |
| list_del(&timer->list); |
| -- |
| 1.7.1.1 |
| |