diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2007-03-25 08:31:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-03-25 17:57:34 -0400 |
commit | 9501b6cf5541f0d576d566a463f1e7d3eaaab4eb (patch) | |
tree | 7983c4cc3c82e465cfe88a7fd67e39dbed72e1e0 /kernel/timer.c | |
parent | 317ec6cd00f25d05d153a780bc178c5335f320ee (diff) |
[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>
Diffstat (limited to 'kernel/timer.c')
-rw-r--r-- | kernel/timer.c | 19 |
1 files changed, 16 insertions, 3 deletions
diff --git a/kernel/timer.c b/kernel/timer.c index 797cccb86431..440048acaea1 100644 --- a/kernel/timer.c +++ b/kernel/timer.c | |||
@@ -695,15 +695,28 @@ static unsigned long cmp_next_hrtimer_event(unsigned long now, | |||
695 | { | 695 | { |
696 | ktime_t hr_delta = hrtimer_get_next_event(); | 696 | ktime_t hr_delta = hrtimer_get_next_event(); |
697 | struct timespec tsdelta; | 697 | struct timespec tsdelta; |
698 | unsigned long delta; | ||
698 | 699 | ||
699 | if (hr_delta.tv64 == KTIME_MAX) | 700 | if (hr_delta.tv64 == KTIME_MAX) |
700 | return expires; | 701 | return expires; |
701 | 702 | ||
702 | if (hr_delta.tv64 <= TICK_NSEC) | 703 | /* |
703 | return now; | 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 | tsdelta = ktime_to_timespec(hr_delta); | 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 | if (time_before(now, expires)) | 720 | if (time_before(now, expires)) |
708 | return now; | 721 | return now; |
709 | return expires; | 722 | return expires; |