Commit 9501b6cf5541f0d576d566a463f1e7d3eaaab4eb
Committed by
Linus Torvalds
1 parent
317ec6cd00
Exists in
master
and in
39 other branches
[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
kernel/timer.c
... | ... | @@ -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; |