Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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>

authored by

Thomas Gleixner and committed by
Linus Torvalds
9501b6cf 317ec6cd

+16 -3
+16 -3
kernel/timer.c
··· 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;