Commit 9501b6cf authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Linus Torvalds

[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: default avatarEmil Karlson <jkarlson@cc.hut.fi>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 317ec6cd
...@@ -695,15 +695,28 @@ static unsigned long cmp_next_hrtimer_event(unsigned long now, ...@@ -695,15 +695,28 @@ static unsigned long cmp_next_hrtimer_event(unsigned long now,
{ {
ktime_t hr_delta = hrtimer_get_next_event(); ktime_t hr_delta = hrtimer_get_next_event();
struct timespec tsdelta; struct timespec tsdelta;
unsigned long delta;
if (hr_delta.tv64 == KTIME_MAX) if (hr_delta.tv64 == KTIME_MAX)
return expires; return expires;
if (hr_delta.tv64 <= TICK_NSEC) /*
return now; * Expired timer available, let it expire in the next tick
*/
if (hr_delta.tv64 <= 0)
return now + 1;
tsdelta = ktime_to_timespec(hr_delta); tsdelta = ktime_to_timespec(hr_delta);
now += timespec_to_jiffies(&tsdelta); delta = timespec_to_jiffies(&tsdelta);
/*
* Take rounding errors in to account and make sure, that it
* expires in the next tick. Otherwise we go into an endless
* ping pong due to tick_nohz_stop_sched_tick() retriggering
* the timer softirq
*/
if (delta < 1)
delta = 1;
now += delta;
if (time_before(now, expires)) if (time_before(now, expires))
return now; return now;
return expires; return expires;
......
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