diff options
author | Anton Blanchard <anton@samba.org> | 2009-05-10 09:37:36 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2009-05-21 01:44:24 -0400 |
commit | 8d165db10772f238103c3e8f955c54145e5c07f3 (patch) | |
tree | 54f287e003df92f2d7b237a2382e4f68db9821cd /arch/powerpc/kernel/time.c | |
parent | 9aa4e7b1699d0fa197778da96de7e03fa2374f0a (diff) |
powerpc: Improve decrementer accuracy
I have been looking at sources of OS jitter and notice that after a long
NO_HZ idle period we wakeup too early:
relative time (us) event
timer irq exit
999946.405 timer irq entry
4.835 timer irq exit
21.685 timer irq entry
3.540 timer (tick_sched_timer) entry
Here we slept for just under a second then took a timer interrupt that did
nothing. 21.685 us later we wake up again and do the work.
We set a rather low shift value of 16 for the decrementer clockevent, which I
think is causing this issue. On this box we have a 207MHz decrementer and see:
clockevent: decrementer mult[3501] shift[16] cpu[0]
For calculations of large intervals this mult/shift combination could be
off by a significant amount. I notice the sparc code has a loop that iterates
to find a mult/shift combination that maximises the shift value while
keeping mult under 32bit. With the patch below we get:
clockevent: decrementer mult[35015c20] shift[32] cpu[15]
And we no longer see the spurious wakeups.
Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/time.c')
-rw-r--r-- | arch/powerpc/kernel/time.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 48571ac56fb7..bee1443da763 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c | |||
@@ -109,7 +109,7 @@ static void decrementer_set_mode(enum clock_event_mode mode, | |||
109 | static struct clock_event_device decrementer_clockevent = { | 109 | static struct clock_event_device decrementer_clockevent = { |
110 | .name = "decrementer", | 110 | .name = "decrementer", |
111 | .rating = 200, | 111 | .rating = 200, |
112 | .shift = 16, | 112 | .shift = 0, /* To be filled in */ |
113 | .mult = 0, /* To be filled in */ | 113 | .mult = 0, /* To be filled in */ |
114 | .irq = 0, | 114 | .irq = 0, |
115 | .set_next_event = decrementer_set_next_event, | 115 | .set_next_event = decrementer_set_next_event, |
@@ -843,6 +843,22 @@ static void decrementer_set_mode(enum clock_event_mode mode, | |||
843 | decrementer_set_next_event(DECREMENTER_MAX, dev); | 843 | decrementer_set_next_event(DECREMENTER_MAX, dev); |
844 | } | 844 | } |
845 | 845 | ||
846 | static void __init setup_clockevent_multiplier(unsigned long hz) | ||
847 | { | ||
848 | u64 mult, shift = 32; | ||
849 | |||
850 | while (1) { | ||
851 | mult = div_sc(hz, NSEC_PER_SEC, shift); | ||
852 | if (mult && (mult >> 32UL) == 0UL) | ||
853 | break; | ||
854 | |||
855 | shift--; | ||
856 | } | ||
857 | |||
858 | decrementer_clockevent.shift = shift; | ||
859 | decrementer_clockevent.mult = mult; | ||
860 | } | ||
861 | |||
846 | static void register_decrementer_clockevent(int cpu) | 862 | static void register_decrementer_clockevent(int cpu) |
847 | { | 863 | { |
848 | struct clock_event_device *dec = &per_cpu(decrementers, cpu).event; | 864 | struct clock_event_device *dec = &per_cpu(decrementers, cpu).event; |
@@ -860,8 +876,7 @@ static void __init init_decrementer_clockevent(void) | |||
860 | { | 876 | { |
861 | int cpu = smp_processor_id(); | 877 | int cpu = smp_processor_id(); |
862 | 878 | ||
863 | decrementer_clockevent.mult = div_sc(ppc_tb_freq, NSEC_PER_SEC, | 879 | setup_clockevent_multiplier(ppc_tb_freq); |
864 | decrementer_clockevent.shift); | ||
865 | decrementer_clockevent.max_delta_ns = | 880 | decrementer_clockevent.max_delta_ns = |
866 | clockevent_delta2ns(DECREMENTER_MAX, &decrementer_clockevent); | 881 | clockevent_delta2ns(DECREMENTER_MAX, &decrementer_clockevent); |
867 | decrementer_clockevent.min_delta_ns = | 882 | decrementer_clockevent.min_delta_ns = |