blob: 07122e3ee2e3210a4db25bcc4db620d9239856e2 [file] [log] [blame]
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