Commit 20634762 authored by Ingo Molnar's avatar Ingo Molnar Committed by Thomas Gleixner

hrtimers: prepare full preemption

Make cancellation of a running callback in softirq context safe
against preemption.
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 6dce6f3f
...@@ -177,6 +177,9 @@ struct hrtimer_cpu_base { ...@@ -177,6 +177,9 @@ struct hrtimer_cpu_base {
int hres_active; int hres_active;
unsigned long nr_events; unsigned long nr_events;
#endif #endif
#ifdef CONFIG_PREEMPT_SOFTIRQS
wait_queue_head_t wait;
#endif
}; };
static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
...@@ -364,6 +367,13 @@ static inline int hrtimer_restart(struct hrtimer *timer) ...@@ -364,6 +367,13 @@ static inline int hrtimer_restart(struct hrtimer *timer)
return hrtimer_start_expires(timer, HRTIMER_MODE_ABS); 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: */ /* Query timers: */
extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer); extern ktime_t hrtimer_get_remaining(const struct hrtimer *timer);
extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp); extern int hrtimer_get_res(const clockid_t which_clock, struct timespec *tp);
......
...@@ -837,6 +837,32 @@ static int enqueue_hrtimer(struct hrtimer *timer, ...@@ -837,6 +837,32 @@ static int enqueue_hrtimer(struct hrtimer *timer,
return leftmost; 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 * __remove_hrtimer - internal function to remove a timer
* *
...@@ -865,6 +891,8 @@ static void __remove_hrtimer(struct hrtimer *timer, ...@@ -865,6 +891,8 @@ static void __remove_hrtimer(struct hrtimer *timer,
rb_erase(&timer->node, &base->active); rb_erase(&timer->node, &base->active);
} }
timer->state = newstate; timer->state = newstate;
wake_up_timer_waiters(base->cpu_base);
} }
/* /*
...@@ -1023,7 +1051,7 @@ int hrtimer_cancel(struct hrtimer *timer) ...@@ -1023,7 +1051,7 @@ int hrtimer_cancel(struct hrtimer *timer)
if (ret >= 0) if (ret >= 0)
return ret; return ret;
cpu_relax(); hrtimer_wait_for_timer(timer);
} }
} }
EXPORT_SYMBOL_GPL(hrtimer_cancel); EXPORT_SYMBOL_GPL(hrtimer_cancel);
...@@ -1558,6 +1586,9 @@ static void __cpuinit init_hrtimers_cpu(int cpu) ...@@ -1558,6 +1586,9 @@ static void __cpuinit init_hrtimers_cpu(int cpu)
cpu_base->clock_base[i].cpu_base = cpu_base; cpu_base->clock_base[i].cpu_base = cpu_base;
hrtimer_init_hres(cpu_base); hrtimer_init_hres(cpu_base);
#ifdef CONFIG_PREEMPT_SOFTIRQS
init_waitqueue_head(&cpu_base->wait);
#endif
} }
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
......
...@@ -161,6 +161,7 @@ again: ...@@ -161,6 +161,7 @@ again:
/* We are sharing ->siglock with it_real_fn() */ /* We are sharing ->siglock with it_real_fn() */
if (hrtimer_try_to_cancel(timer) < 0) { if (hrtimer_try_to_cancel(timer) < 0) {
spin_unlock_irq(&tsk->sighand->siglock); spin_unlock_irq(&tsk->sighand->siglock);
hrtimer_wait_for_timer(&tsk->signal->real_timer);
goto again; goto again;
} }
expires = timeval_to_ktime(value->it_value); expires = timeval_to_ktime(value->it_value);
......
...@@ -789,6 +789,7 @@ retry: ...@@ -789,6 +789,7 @@ retry:
unlock_timer(timr, flag); unlock_timer(timr, flag);
if (error == TIMER_RETRY) { if (error == TIMER_RETRY) {
hrtimer_wait_for_timer(&timr->it.real.timer);
rtn = NULL; // We already got the old time... rtn = NULL; // We already got the old time...
goto retry; goto retry;
} }
...@@ -827,6 +828,7 @@ retry_delete: ...@@ -827,6 +828,7 @@ retry_delete:
if (timer_delete_hook(timer) == TIMER_RETRY) { if (timer_delete_hook(timer) == TIMER_RETRY) {
unlock_timer(timer, flags); unlock_timer(timer, flags);
hrtimer_wait_for_timer(&timer->it.real.timer);
goto retry_delete; goto retry_delete;
} }
...@@ -856,6 +858,7 @@ retry_delete: ...@@ -856,6 +858,7 @@ retry_delete:
if (timer_delete_hook(timer) == TIMER_RETRY) { if (timer_delete_hook(timer) == TIMER_RETRY) {
unlock_timer(timer, flags); unlock_timer(timer, flags);
hrtimer_wait_for_timer(&timer->it.real.timer);
goto retry_delete; goto retry_delete;
} }
list_del(&timer->list); list_del(&timer->list);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment