Commit 9501b6cf5541f0d576d566a463f1e7d3eaaab4eb

Authored by Thomas Gleixner
Committed by Linus Torvalds
1 parent 317ec6cd00

[PATCH] dynticks: fix hrtimer rounding error in next_timer_interrupt

The rework of next_timer_interrupt() fixed the timer wheel bugs, but
invented a rounding error versus the next hrtimer event. This is caused
by the conversion of the hrtimer internal representation to relative
jiffies.

This causes bug #8100:
http://bugzilla.kernel.org/show_bug.cgi?id=8100

next_timer_interrupt() returns "now" in such a case and causes the code
in tick_nohz_stop_sched_tick() to trigger the timer softirq, which is
bogus as no timer is due for expiry. This results in an endless context
switching between idle and ksoftirqd until a timer is due for expiry.

Modify the hrtimer evaluation so that, it returns now + 1, when the
conversion results in a delta < 1 jiffie.

It's confirmed to resolve bug #8100

Reported-by: Emil Karlson <jkarlson@cc.hut.fi>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 1 changed file with 16 additions and 3 deletions Side-by-side Diff

... ... @@ -695,15 +695,28 @@
695 695 {
696 696 ktime_t hr_delta = hrtimer_get_next_event();
697 697 struct timespec tsdelta;
  698 + unsigned long delta;
698 699  
699 700 if (hr_delta.tv64 == KTIME_MAX)
700 701 return expires;
701 702  
702   - if (hr_delta.tv64 <= TICK_NSEC)
703   - return now;
  703 + /*
  704 + * Expired timer available, let it expire in the next tick
  705 + */
  706 + if (hr_delta.tv64 <= 0)
  707 + return now + 1;
704 708  
705 709 tsdelta = ktime_to_timespec(hr_delta);
706   - now += timespec_to_jiffies(&tsdelta);
  710 + delta = timespec_to_jiffies(&tsdelta);
  711 + /*
  712 + * Take rounding errors in to account and make sure, that it
  713 + * expires in the next tick. Otherwise we go into an endless
  714 + * ping pong due to tick_nohz_stop_sched_tick() retriggering
  715 + * the timer softirq
  716 + */
  717 + if (delta < 1)
  718 + delta = 1;
  719 + now += delta;
707 720 if (time_before(now, expires))
708 721 return now;
709 722 return expires;