Commit 66e4b636 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'timers-urgent-2020-07-19' of...

Merge tag 'timers-urgent-2020-07-19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip into master

Pull timer fixes from Thomas Gleixner:
 "Two fixes for the timer wheel:

   - A timer which is already expired at enqueue time can set the
     base->next_expiry value backwards. As a consequence base->clk can
     be set back as well. This can lead to timers expiring early. Add a
     sanity check to prevent this.

   - When a timer is queued with an expiry time beyond the wheel
     capacity then it should be queued in the bucket of the last wheel
     level which is expiring last.

     The code adjusted the expiry time to the maximum wheel capacity,
     which is only correct when the wheel clock is 0. Aside of that the
     check whether the delta is larger than wheel capacity does not
     check the delta, it checks the expiry value itself. As a result
     timers can expire at random.

     Fix this by checking the right variable and adjust expiry time so
     it becomes base->clock plus capacity which places it into the
     outmost bucket in the last wheel level"

* tag 'timers-urgent-2020-07-19' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  timer: Fix wheel index calculation on last level
  timer: Prevent base->clk from moving backward
parents 43768f7c e2a71bde
...@@ -521,8 +521,8 @@ static int calc_wheel_index(unsigned long expires, unsigned long clk) ...@@ -521,8 +521,8 @@ static int calc_wheel_index(unsigned long expires, unsigned long clk)
* Force expire obscene large timeouts to expire at the * Force expire obscene large timeouts to expire at the
* capacity limit of the wheel. * capacity limit of the wheel.
*/ */
if (expires >= WHEEL_TIMEOUT_CUTOFF) if (delta >= WHEEL_TIMEOUT_CUTOFF)
expires = WHEEL_TIMEOUT_MAX; expires = clk + WHEEL_TIMEOUT_MAX;
idx = calc_index(expires, LVL_DEPTH - 1); idx = calc_index(expires, LVL_DEPTH - 1);
} }
...@@ -584,7 +584,15 @@ trigger_dyntick_cpu(struct timer_base *base, struct timer_list *timer) ...@@ -584,7 +584,15 @@ trigger_dyntick_cpu(struct timer_base *base, struct timer_list *timer)
* Set the next expiry time and kick the CPU so it can reevaluate the * Set the next expiry time and kick the CPU so it can reevaluate the
* wheel: * wheel:
*/ */
base->next_expiry = timer->expires; if (time_before(timer->expires, base->clk)) {
/*
* Prevent from forward_timer_base() moving the base->clk
* backward
*/
base->next_expiry = base->clk;
} else {
base->next_expiry = timer->expires;
}
wake_up_nohz_cpu(base->cpu); wake_up_nohz_cpu(base->cpu);
} }
...@@ -896,10 +904,13 @@ static inline void forward_timer_base(struct timer_base *base) ...@@ -896,10 +904,13 @@ static inline void forward_timer_base(struct timer_base *base)
* If the next expiry value is > jiffies, then we fast forward to * If the next expiry value is > jiffies, then we fast forward to
* jiffies otherwise we forward to the next expiry value. * jiffies otherwise we forward to the next expiry value.
*/ */
if (time_after(base->next_expiry, jnow)) if (time_after(base->next_expiry, jnow)) {
base->clk = jnow; base->clk = jnow;
else } else {
if (WARN_ON_ONCE(time_before(base->next_expiry, base->clk)))
return;
base->clk = base->next_expiry; base->clk = base->next_expiry;
}
#endif #endif
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment