Commit 5db9fa95 authored by Nathan Lynch's avatar Nathan Lynch Committed by Paul Mackerras

[POWERPC] Fix gettimeofday inaccuracies

There are two problems in the powerpc gettimeofday code which can
cause incorrect results to be returned.

The first is that there is a race between do_gettimeofday and the
timer interrupt:

1. do_gettimeofday does get_tb()

2. decrementer exception on boot cpu which runs timer_recalc_offset,
   which also samples the timebase and updates the do_gtod structure
   with a greater timebase value.

3. do_gettimeofday calls __do_gettimeofday, which leads to the
   negative result from tb_val - temp_varp->tb_orig_stamp.

The second is caused by taking the boot cpu offline, which can cause
the value of tb_last_jiffy to be increased past the currently
available timebase, causing the same underflow as above.

[paulus@samba.org - define and use data_barrier() instead of mb().]
Signed-off-by: default avatarNathan Lynch <ntl@pobox.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent aa74a30b
...@@ -417,7 +417,7 @@ static __inline__ void timer_check_rtc(void) ...@@ -417,7 +417,7 @@ static __inline__ void timer_check_rtc(void)
/* /*
* This version of gettimeofday has microsecond resolution. * This version of gettimeofday has microsecond resolution.
*/ */
static inline void __do_gettimeofday(struct timeval *tv, u64 tb_val) static inline void __do_gettimeofday(struct timeval *tv)
{ {
unsigned long sec, usec; unsigned long sec, usec;
u64 tb_ticks, xsec; u64 tb_ticks, xsec;
...@@ -431,7 +431,12 @@ static inline void __do_gettimeofday(struct timeval *tv, u64 tb_val) ...@@ -431,7 +431,12 @@ static inline void __do_gettimeofday(struct timeval *tv, u64 tb_val)
* without a divide (and in fact, without a multiply) * without a divide (and in fact, without a multiply)
*/ */
temp_varp = do_gtod.varp; temp_varp = do_gtod.varp;
tb_ticks = tb_val - temp_varp->tb_orig_stamp;
/* Sampling the time base must be done after loading
* do_gtod.varp in order to avoid racing with update_gtod.
*/
data_barrier(temp_varp);
tb_ticks = get_tb() - temp_varp->tb_orig_stamp;
temp_tb_to_xs = temp_varp->tb_to_xs; temp_tb_to_xs = temp_varp->tb_to_xs;
temp_stamp_xsec = temp_varp->stamp_xsec; temp_stamp_xsec = temp_varp->stamp_xsec;
xsec = temp_stamp_xsec + mulhdu(tb_ticks, temp_tb_to_xs); xsec = temp_stamp_xsec + mulhdu(tb_ticks, temp_tb_to_xs);
...@@ -464,7 +469,7 @@ void do_gettimeofday(struct timeval *tv) ...@@ -464,7 +469,7 @@ void do_gettimeofday(struct timeval *tv)
tv->tv_usec = usec; tv->tv_usec = usec;
return; return;
} }
__do_gettimeofday(tv, get_tb()); __do_gettimeofday(tv);
} }
EXPORT_SYMBOL(do_gettimeofday); EXPORT_SYMBOL(do_gettimeofday);
...@@ -650,6 +655,7 @@ void timer_interrupt(struct pt_regs * regs) ...@@ -650,6 +655,7 @@ void timer_interrupt(struct pt_regs * regs)
int next_dec; int next_dec;
int cpu = smp_processor_id(); int cpu = smp_processor_id();
unsigned long ticks; unsigned long ticks;
u64 tb_next_jiffy;
#ifdef CONFIG_PPC32 #ifdef CONFIG_PPC32
if (atomic_read(&ppc_n_lost_interrupts) != 0) if (atomic_read(&ppc_n_lost_interrupts) != 0)
...@@ -691,11 +697,14 @@ void timer_interrupt(struct pt_regs * regs) ...@@ -691,11 +697,14 @@ void timer_interrupt(struct pt_regs * regs)
continue; continue;
write_seqlock(&xtime_lock); write_seqlock(&xtime_lock);
tb_last_jiffy += tb_ticks_per_jiffy; tb_next_jiffy = tb_last_jiffy + tb_ticks_per_jiffy;
tb_last_stamp = per_cpu(last_jiffy, cpu); if (per_cpu(last_jiffy, cpu) >= tb_next_jiffy) {
do_timer(regs); tb_last_jiffy = tb_next_jiffy;
timer_recalc_offset(tb_last_jiffy); tb_last_stamp = per_cpu(last_jiffy, cpu);
timer_check_rtc(); do_timer(regs);
timer_recalc_offset(tb_last_jiffy);
timer_check_rtc();
}
write_sequnlock(&xtime_lock); write_sequnlock(&xtime_lock);
} }
......
...@@ -53,6 +53,15 @@ ...@@ -53,6 +53,15 @@
#define smp_read_barrier_depends() do { } while(0) #define smp_read_barrier_depends() do { } while(0)
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
/*
* This is a barrier which prevents following instructions from being
* started until the value of the argument x is known. For example, if
* x is a variable loaded from memory, this prevents following
* instructions from being executed until the load has been performed.
*/
#define data_barrier(x) \
asm volatile("twi 0,%0,0; isync" : : "r" (x) : "memory");
struct task_struct; struct task_struct;
struct pt_regs; struct pt_regs;
......
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