2012-06-17 19:52:06 +00:00
|
|
|
From 421669c70501857774ee79e66db3c8272145bedf Mon Sep 17 00:00:00 2001
|
2011-11-20 00:01:55 +00:00
|
|
|
From: Ingo Molnar <mingo@elte.hu>
|
|
|
|
Date: Fri, 3 Jul 2009 08:29:34 -0500
|
2012-06-17 19:52:06 +00:00
|
|
|
Subject: [108/254] timers: prepare for full preemption
|
2011-11-20 00:01:55 +00:00
|
|
|
|
|
|
|
When softirqs can be preempted we need to make sure that cancelling
|
|
|
|
the timer from the active thread can not deadlock vs. a running timer
|
|
|
|
callback. Add a waitqueue to resolve that.
|
|
|
|
|
|
|
|
Signed-off-by: Ingo Molnar <mingo@elte.hu>
|
|
|
|
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
|
|
|
|
---
|
|
|
|
include/linux/timer.h | 2 +-
|
|
|
|
kernel/timer.c | 35 ++++++++++++++++++++++++++++++++---
|
|
|
|
2 files changed, 33 insertions(+), 4 deletions(-)
|
|
|
|
|
2012-06-17 19:52:06 +00:00
|
|
|
diff --git a/include/linux/timer.h b/include/linux/timer.h
|
|
|
|
index 6abd913..b703477 100644
|
|
|
|
--- a/include/linux/timer.h
|
|
|
|
+++ b/include/linux/timer.h
|
|
|
|
@@ -276,7 +276,7 @@ extern void add_timer(struct timer_list *timer);
|
2011-11-20 00:01:55 +00:00
|
|
|
|
|
|
|
extern int try_to_del_timer_sync(struct timer_list *timer);
|
|
|
|
|
|
|
|
-#ifdef CONFIG_SMP
|
|
|
|
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL)
|
|
|
|
extern int del_timer_sync(struct timer_list *timer);
|
|
|
|
#else
|
|
|
|
# define del_timer_sync(t) del_timer(t)
|
2012-06-17 19:52:06 +00:00
|
|
|
diff --git a/kernel/timer.c b/kernel/timer.c
|
|
|
|
index a297ffc..d7ad9d0 100644
|
|
|
|
--- a/kernel/timer.c
|
|
|
|
+++ b/kernel/timer.c
|
2011-11-20 00:01:55 +00:00
|
|
|
@@ -75,6 +75,7 @@ struct tvec_root {
|
|
|
|
struct tvec_base {
|
|
|
|
spinlock_t lock;
|
|
|
|
struct timer_list *running_timer;
|
|
|
|
+ wait_queue_head_t wait_for_running_timer;
|
|
|
|
unsigned long timer_jiffies;
|
|
|
|
unsigned long next_timer;
|
|
|
|
struct tvec_root tv1;
|
2012-06-17 19:52:06 +00:00
|
|
|
@@ -725,12 +726,15 @@ __mod_timer(struct timer_list *timer, unsigned long expires,
|
2011-11-20 00:01:55 +00:00
|
|
|
|
|
|
|
debug_activate(timer, expires);
|
|
|
|
|
|
|
|
+ preempt_disable_rt();
|
|
|
|
cpu = smp_processor_id();
|
|
|
|
|
|
|
|
#if defined(CONFIG_NO_HZ) && defined(CONFIG_SMP)
|
|
|
|
if (!pinned && get_sysctl_timer_migration() && idle_cpu(cpu))
|
|
|
|
cpu = get_nohz_timer_target();
|
|
|
|
#endif
|
|
|
|
+ preempt_enable_rt();
|
|
|
|
+
|
|
|
|
new_base = per_cpu(tvec_bases, cpu);
|
|
|
|
|
|
|
|
if (base != new_base) {
|
2012-06-17 19:52:06 +00:00
|
|
|
@@ -931,6 +935,29 @@ void add_timer_on(struct timer_list *timer, int cpu)
|
2011-11-20 00:01:55 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(add_timer_on);
|
|
|
|
|
|
|
|
+#ifdef CONFIG_PREEMPT_RT_FULL
|
|
|
|
+/*
|
|
|
|
+ * Wait for a running timer
|
|
|
|
+ */
|
|
|
|
+static void wait_for_running_timer(struct timer_list *timer)
|
|
|
|
+{
|
|
|
|
+ struct tvec_base *base = timer->base;
|
|
|
|
+
|
|
|
|
+ if (base->running_timer == timer)
|
|
|
|
+ wait_event(base->wait_for_running_timer,
|
|
|
|
+ base->running_timer != timer);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+# define wakeup_timer_waiters(b) wake_up(&(b)->wait_for_tunning_timer)
|
|
|
|
+#else
|
|
|
|
+static inline void wait_for_running_timer(struct timer_list *timer)
|
|
|
|
+{
|
|
|
|
+ cpu_relax();
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+# define wakeup_timer_waiters(b) do { } while (0)
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
/**
|
|
|
|
* del_timer - deactive a timer.
|
|
|
|
* @timer: the timer to be deactivated
|
2012-06-04 21:20:09 +00:00
|
|
|
@@ -1003,7 +1030,7 @@ out:
|
2011-11-20 00:01:55 +00:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(try_to_del_timer_sync);
|
|
|
|
|
|
|
|
-#ifdef CONFIG_SMP
|
|
|
|
+#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT_FULL)
|
|
|
|
/**
|
|
|
|
* del_timer_sync - deactivate a timer and wait for the handler to finish.
|
|
|
|
* @timer: the timer to be deactivated
|
2012-06-17 19:52:06 +00:00
|
|
|
@@ -1063,7 +1090,7 @@ int del_timer_sync(struct timer_list *timer)
|
2011-11-20 00:01:55 +00:00
|
|
|
int ret = try_to_del_timer_sync(timer);
|
|
|
|
if (ret >= 0)
|
|
|
|
return ret;
|
|
|
|
- cpu_relax();
|
|
|
|
+ wait_for_running_timer(timer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(del_timer_sync);
|
2012-06-17 19:52:06 +00:00
|
|
|
@@ -1174,10 +1201,11 @@ static inline void __run_timers(struct tvec_base *base)
|
2011-11-20 00:01:55 +00:00
|
|
|
|
|
|
|
spin_unlock_irq(&base->lock);
|
|
|
|
call_timer_fn(timer, fn, data);
|
|
|
|
+ base->running_timer = NULL;
|
|
|
|
spin_lock_irq(&base->lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
- base->running_timer = NULL;
|
|
|
|
+ wake_up(&base->wait_for_running_timer);
|
|
|
|
spin_unlock_irq(&base->lock);
|
|
|
|
}
|
|
|
|
|
2012-06-17 19:52:06 +00:00
|
|
|
@@ -1684,6 +1712,7 @@ static int __cpuinit init_timers_cpu(int cpu)
|
2011-11-20 00:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_init(&base->lock);
|
|
|
|
+ init_waitqueue_head(&base->wait_for_running_timer);
|
|
|
|
|
|
|
|
for (j = 0; j < TVN_SIZE; j++) {
|
|
|
|
INIT_LIST_HEAD(base->tv5.vec + j);
|