Skip to content

Commit 6bad6bc

Browse files
committed
timers: Prevent base clock corruption when forwarding
When a timer is enqueued we try to forward the timer base clock. This mechanism has two issues: 1) Forwarding a remote base unlocked The forwarding function is called from get_target_base() with the current timer base lock held. But if the new target base is a different base than the current base (can happen with NOHZ, sigh!) then the forwarding is done on an unlocked base. This can lead to corruption of base->clk. Solution is simple: Invoke the forwarding after the target base is locked. 2) Possible corruption due to jiffies advancing This is similar to the issue in get_net_timer_interrupt() which was fixed in the previous patch. jiffies can advance between check and assignement and therefore advancing base->clk beyond the next expiry value. So we need to read jiffies into a local variable once and do the checks and assignment with the local copy. Fixes: a683f39("timers: Forward the wheel clock whenever possible") Reported-by: Ashton Holmes <scoopta@gmail.com> Reported-by: Michael Thayer <michael.thayer@oracle.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Cc: Michal Necasek <michal.necasek@oracle.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: knut.osmundsen@oracle.com Cc: stable@vger.kernel.org Cc: stern@rowland.harvard.edu Cc: rt@linutronix.de Link: http://lkml.kernel.org/r/20161022110552.253640125@linutronix.de Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
1 parent 041ad7b commit 6bad6bc

1 file changed

Lines changed: 10 additions & 13 deletions

File tree

kernel/time/timer.c

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ static inline struct timer_base *get_timer_base(u32 tflags)
878878

879879
#ifdef CONFIG_NO_HZ_COMMON
880880
static inline struct timer_base *
881-
__get_target_base(struct timer_base *base, unsigned tflags)
881+
get_target_base(struct timer_base *base, unsigned tflags)
882882
{
883883
#ifdef CONFIG_SMP
884884
if ((tflags & TIMER_PINNED) || !base->migration_enabled)
@@ -891,40 +891,34 @@ __get_target_base(struct timer_base *base, unsigned tflags)
891891

892892
static inline void forward_timer_base(struct timer_base *base)
893893
{
894+
unsigned long jnow = READ_ONCE(jiffies);
895+
894896
/*
895897
* We only forward the base when it's idle and we have a delta between
896898
* base clock and jiffies.
897899
*/
898-
if (!base->is_idle || (long) (jiffies - base->clk) < 2)
900+
if (!base->is_idle || (long) (jnow - base->clk) < 2)
899901
return;
900902

901903
/*
902904
* If the next expiry value is > jiffies, then we fast forward to
903905
* jiffies otherwise we forward to the next expiry value.
904906
*/
905-
if (time_after(base->next_expiry, jiffies))
906-
base->clk = jiffies;
907+
if (time_after(base->next_expiry, jnow))
908+
base->clk = jnow;
907909
else
908910
base->clk = base->next_expiry;
909911
}
910912
#else
911913
static inline struct timer_base *
912-
__get_target_base(struct timer_base *base, unsigned tflags)
914+
get_target_base(struct timer_base *base, unsigned tflags)
913915
{
914916
return get_timer_this_cpu_base(tflags);
915917
}
916918

917919
static inline void forward_timer_base(struct timer_base *base) { }
918920
#endif
919921

920-
static inline struct timer_base *
921-
get_target_base(struct timer_base *base, unsigned tflags)
922-
{
923-
struct timer_base *target = __get_target_base(base, tflags);
924-
925-
forward_timer_base(target);
926-
return target;
927-
}
928922

929923
/*
930924
* We are using hashed locking: Holding per_cpu(timer_bases[x]).lock means
@@ -1037,6 +1031,9 @@ __mod_timer(struct timer_list *timer, unsigned long expires, bool pending_only)
10371031
}
10381032
}
10391033

1034+
/* Try to forward a stale timer base clock */
1035+
forward_timer_base(base);
1036+
10401037
timer->expires = expires;
10411038
/*
10421039
* If 'idx' was calculated above and the base time did not advance

0 commit comments

Comments
 (0)