summaryrefslogtreecommitdiff
path: root/kernel/time/timer.c
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2016-06-23 17:28:38 -0400
committerSteven Rostedt <rostedt@goodmis.org>2016-06-23 17:28:38 -0400
commit2247d3fb0e36be462054e438909992967fa5d949 (patch)
treebab7ab0850e0fa55c770b10af9ea76c334483e9a /kernel/time/timer.c
parent794e782487a0e832cc18b99a6ffd5830c837a039 (diff)
parent3b6aa07b936b09d38c1bfcee1e06845b968df475 (diff)
downloadlinux-rt-2247d3fb0e36be462054e438909992967fa5d949.tar.gz
Merge tag 'v3.18.34' into v3.18-rt
Linux 3.18.34
Diffstat (limited to 'kernel/time/timer.c')
-rw-r--r--kernel/time/timer.c20
1 files changed, 17 insertions, 3 deletions
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index 78e39b644780..12d071d9063e 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -983,13 +983,27 @@ EXPORT_SYMBOL(add_timer);
*/
void add_timer_on(struct timer_list *timer, int cpu)
{
- struct tvec_base *base = per_cpu(tvec_bases, cpu);
+ struct tvec_base *new_base = per_cpu(tvec_bases, cpu);
+ struct tvec_base *base;
unsigned long flags;
timer_stats_timer_set_start_info(timer);
BUG_ON(timer_pending(timer) || !timer->function);
- spin_lock_irqsave(&base->lock, flags);
- timer_set_base(timer, base);
+
+ /*
+ * If @timer was on a different CPU, it should be migrated with the
+ * old base locked to prevent other operations proceeding with the
+ * wrong base locked. See lock_timer_base().
+ */
+ base = lock_timer_base(timer, &flags);
+ if (base != new_base) {
+ timer_set_base(timer, NULL);
+ spin_unlock(&base->lock);
+ base = new_base;
+ spin_lock(&base->lock);
+ timer_set_base(timer, base);
+ }
+
debug_activate(timer, timer->expires);
internal_add_timer(base, timer);
spin_unlock_irqrestore(&base->lock, flags);