Subject: timers: Avoid the switch timers base set to NULL trick on RT
From: Thomas Gleixner <tglx@linutronix.de>
Date: Thu, 21 Jul 2011 15:23:39 +0200
Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/4.6/older/patches-4.6.2-rt5.tar.xz

On RT that code is preemptible, so we cannot assign NULL to timers
base as a preempter would spin forever in lock_timer_base().

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
---
 kernel/time/timer.c |   45 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 35 insertions(+), 10 deletions(-)

--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -780,6 +780,39 @@ static struct tvec_base *lock_timer_base
 		cpu_relax();
 	}
 }
+#ifdef CONFIG_PREEMPT_RT_FULL
+static inline struct tvec_base *switch_timer_base(struct timer_list *timer,
+						  struct tvec_base *old,
+						  struct tvec_base *new)
+{
+	/*
+	 * We cannot do the below because we might be preempted and
+	 * then the preempter would see NULL and loop forever.
+	 */
+	if (spin_trylock(&new->lock)) {
+		WRITE_ONCE(timer->flags,
+			   (timer->flags & ~TIMER_BASEMASK) | new->cpu);
+		spin_unlock(&old->lock);
+		return new;
+	}
+	return old;
+}
+
+#else
+static inline struct tvec_base *switch_timer_base(struct timer_list *timer,
+						  struct tvec_base *old,
+						  struct tvec_base *new)
+{
+	/* See the comment in lock_timer_base() */
+	timer->flags |= TIMER_MIGRATING;
+
+	spin_unlock(&old->lock);
+	spin_lock(&new->lock);
+	WRITE_ONCE(timer->flags,
+		   (timer->flags & ~TIMER_BASEMASK) | new->cpu);
+	return new;
+}
+#endif
 
 static inline int
 __mod_timer(struct timer_list *timer, unsigned long expires,
@@ -810,16 +843,8 @@ static inline int
 		 * handler yet has not finished. This also guarantees that
 		 * the timer is serialized wrt itself.
 		 */
-		if (likely(base->running_timer != timer)) {
-			/* See the comment in lock_timer_base() */
-			timer->flags |= TIMER_MIGRATING;
-
-			spin_unlock(&base->lock);
-			base = new_base;
-			spin_lock(&base->lock);
-			WRITE_ONCE(timer->flags,
-				   (timer->flags & ~TIMER_BASEMASK) | base->cpu);
-		}
+		if (likely(base->running_timer != timer))
+			base = switch_timer_base(timer, base, new_base);
 	}
 
 	timer->expires = expires;
