NOHZ: prevent multiplication overflow - stop timer for huge timeouts

get_next_timer_interrupt() returns a delta of (LONG_MAX > 1) in case
there is no timer pending. On 64 bit machines this results in a
multiplication overflow in tick_nohz_stop_sched_tick().

Reported by: Dave Miller <davem@davemloft.net>

Make the return value a constant and limit the return value to a 32 bit
value.

When the max timeout value is returned, we can safely stop the tick
timer device. The max jiffies delta results in a 12 days timeout for
HZ=1000.

In the long term the get_next_timer_interrupt() code needs to be
reworked to return ktime instead of jiffies, but we have to wait until
the last users of the original NO_IDLE_HZ code are converted.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Thomas Gleixner and committed by Linus Torvalds eaad084b 6e98ee75

+30 -2
+6
include/linux/timer.h
··· 69 69 extern int mod_timer(struct timer_list *timer, unsigned long expires); 70 70 71 71 /* 72 + * The jiffies value which is added to now, when there is no timer 73 + * in the timer wheel: 74 + */ 75 + #define NEXT_TIMER_MAX_DELTA ((1UL << 30) - 1) 76 + 77 + /* 72 78 * Return when the next timer-wheel timeout occurs (in absolute jiffies), 73 79 * locks the timer base: 74 80 */
+15 -1
kernel/time/tick-sched.c
··· 247 247 if (cpu == tick_do_timer_cpu) 248 248 tick_do_timer_cpu = -1; 249 249 250 + ts->idle_sleeps++; 251 + 252 + /* 253 + * delta_jiffies >= NEXT_TIMER_MAX_DELTA signals that 254 + * there is no timer pending or at least extremly far 255 + * into the future (12 days for HZ=1000). In this case 256 + * we simply stop the tick timer: 257 + */ 258 + if (unlikely(delta_jiffies >= NEXT_TIMER_MAX_DELTA)) { 259 + ts->idle_expires.tv64 = KTIME_MAX; 260 + if (ts->nohz_mode == NOHZ_MODE_HIGHRES) 261 + hrtimer_cancel(&ts->sched_timer); 262 + goto out; 263 + } 264 + 250 265 /* 251 266 * calculate the expiry time for the next timer wheel 252 267 * timer ··· 269 254 expires = ktime_add_ns(last_update, tick_period.tv64 * 270 255 delta_jiffies); 271 256 ts->idle_expires = expires; 272 - ts->idle_sleeps++; 273 257 274 258 if (ts->nohz_mode == NOHZ_MODE_HIGHRES) { 275 259 hrtimer_start(&ts->sched_timer, expires,
+9 -1
kernel/timer.c
··· 666 666 static unsigned long __next_timer_interrupt(tvec_base_t *base) 667 667 { 668 668 unsigned long timer_jiffies = base->timer_jiffies; 669 - unsigned long expires = timer_jiffies + (LONG_MAX >> 1); 669 + unsigned long expires = timer_jiffies + NEXT_TIMER_MAX_DELTA; 670 670 int index, slot, array, found = 0; 671 671 struct timer_list *nte; 672 672 tvec_t *varray[4]; ··· 752 752 753 753 tsdelta = ktime_to_timespec(hr_delta); 754 754 delta = timespec_to_jiffies(&tsdelta); 755 + 756 + /* 757 + * Limit the delta to the max value, which is checked in 758 + * tick_nohz_stop_sched_tick(): 759 + */ 760 + if (delta > NEXT_TIMER_MAX_DELTA) 761 + delta = NEXT_TIMER_MAX_DELTA; 762 + 755 763 /* 756 764 * Take rounding errors in to account and make sure, that it 757 765 * expires in the next tick. Otherwise we go into an endless