diff options
Diffstat (limited to 'arch/powerpc/kernel/time.c')
| -rw-r--r-- | arch/powerpc/kernel/time.c | 48 |
1 files changed, 34 insertions, 14 deletions
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 2a7ddc579379..86f7e3d154d8 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c | |||
| @@ -283,9 +283,9 @@ static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec, | |||
| 283 | * the two values of tb_update_count match and are even then the | 283 | * the two values of tb_update_count match and are even then the |
| 284 | * tb_to_xs and stamp_xsec values are consistent. If not, then it | 284 | * tb_to_xs and stamp_xsec values are consistent. If not, then it |
| 285 | * loops back and reads them again until this criteria is met. | 285 | * loops back and reads them again until this criteria is met. |
| 286 | * We expect the caller to have done the first increment of | ||
| 287 | * vdso_data->tb_update_count already. | ||
| 286 | */ | 288 | */ |
| 287 | ++(vdso_data->tb_update_count); | ||
| 288 | smp_wmb(); | ||
| 289 | vdso_data->tb_orig_stamp = new_tb_stamp; | 289 | vdso_data->tb_orig_stamp = new_tb_stamp; |
| 290 | vdso_data->stamp_xsec = new_stamp_xsec; | 290 | vdso_data->stamp_xsec = new_stamp_xsec; |
| 291 | vdso_data->tb_to_xs = new_tb_to_xs; | 291 | vdso_data->tb_to_xs = new_tb_to_xs; |
| @@ -310,20 +310,15 @@ static __inline__ void timer_recalc_offset(u64 cur_tb) | |||
| 310 | unsigned long offset; | 310 | unsigned long offset; |
| 311 | u64 new_stamp_xsec; | 311 | u64 new_stamp_xsec; |
| 312 | u64 tlen, t2x; | 312 | u64 tlen, t2x; |
| 313 | u64 tb, xsec_old, xsec_new; | ||
| 314 | struct gettimeofday_vars *varp; | ||
| 313 | 315 | ||
| 314 | if (__USE_RTC()) | 316 | if (__USE_RTC()) |
| 315 | return; | 317 | return; |
| 316 | tlen = current_tick_length(); | 318 | tlen = current_tick_length(); |
| 317 | offset = cur_tb - do_gtod.varp->tb_orig_stamp; | 319 | offset = cur_tb - do_gtod.varp->tb_orig_stamp; |
| 318 | if (tlen == last_tick_len && offset < 0x80000000u) { | 320 | if (tlen == last_tick_len && offset < 0x80000000u) |
| 319 | /* check that we're still in sync; if not, resync */ | 321 | return; |
| 320 | struct timeval tv; | ||
| 321 | __do_gettimeofday(&tv, cur_tb); | ||
| 322 | if (tv.tv_sec <= xtime.tv_sec && | ||
| 323 | (tv.tv_sec < xtime.tv_sec || | ||
| 324 | tv.tv_usec * 1000 <= xtime.tv_nsec)) | ||
| 325 | return; | ||
| 326 | } | ||
| 327 | if (tlen != last_tick_len) { | 322 | if (tlen != last_tick_len) { |
| 328 | t2x = mulhdu(tlen << TICKLEN_SHIFT, ticklen_to_xs); | 323 | t2x = mulhdu(tlen << TICKLEN_SHIFT, ticklen_to_xs); |
| 329 | last_tick_len = tlen; | 324 | last_tick_len = tlen; |
| @@ -332,6 +327,21 @@ static __inline__ void timer_recalc_offset(u64 cur_tb) | |||
| 332 | new_stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC; | 327 | new_stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC; |
| 333 | do_div(new_stamp_xsec, 1000000000); | 328 | do_div(new_stamp_xsec, 1000000000); |
| 334 | new_stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC; | 329 | new_stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC; |
| 330 | |||
| 331 | ++vdso_data->tb_update_count; | ||
| 332 | smp_mb(); | ||
| 333 | |||
| 334 | /* | ||
| 335 | * Make sure time doesn't go backwards for userspace gettimeofday. | ||
| 336 | */ | ||
| 337 | tb = get_tb(); | ||
| 338 | varp = do_gtod.varp; | ||
| 339 | xsec_old = mulhdu(tb - varp->tb_orig_stamp, varp->tb_to_xs) | ||
| 340 | + varp->stamp_xsec; | ||
| 341 | xsec_new = mulhdu(tb - cur_tb, t2x) + new_stamp_xsec; | ||
| 342 | if (xsec_new < xsec_old) | ||
| 343 | new_stamp_xsec += xsec_old - xsec_new; | ||
| 344 | |||
| 335 | update_gtod(cur_tb, new_stamp_xsec, t2x); | 345 | update_gtod(cur_tb, new_stamp_xsec, t2x); |
| 336 | } | 346 | } |
| 337 | 347 | ||
| @@ -564,6 +574,10 @@ int do_settimeofday(struct timespec *tv) | |||
| 564 | } | 574 | } |
| 565 | #endif | 575 | #endif |
| 566 | 576 | ||
| 577 | /* Make userspace gettimeofday spin until we're done. */ | ||
| 578 | ++vdso_data->tb_update_count; | ||
| 579 | smp_mb(); | ||
| 580 | |||
| 567 | /* | 581 | /* |
| 568 | * Subtract off the number of nanoseconds since the | 582 | * Subtract off the number of nanoseconds since the |
| 569 | * beginning of the last tick. | 583 | * beginning of the last tick. |
| @@ -724,10 +738,16 @@ void __init time_init(void) | |||
| 724 | * It is computed as: | 738 | * It is computed as: |
| 725 | * ticklen_to_xs = 2^N / (tb_ticks_per_jiffy * 1e9) | 739 | * ticklen_to_xs = 2^N / (tb_ticks_per_jiffy * 1e9) |
| 726 | * where N = 64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT | 740 | * where N = 64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT |
| 727 | * so as to give the result as a 0.64 fixed-point fraction. | 741 | * which turns out to be N = 51 - SHIFT_HZ. |
| 742 | * This gives the result as a 0.64 fixed-point fraction. | ||
| 743 | * That value is reduced by an offset amounting to 1 xsec per | ||
| 744 | * 2^31 timebase ticks to avoid problems with time going backwards | ||
| 745 | * by 1 xsec when we do timer_recalc_offset due to losing the | ||
| 746 | * fractional xsec. That offset is equal to ppc_tb_freq/2^51 | ||
| 747 | * since there are 2^20 xsec in a second. | ||
| 728 | */ | 748 | */ |
| 729 | div128_by_32(1ULL << (64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT), 0, | 749 | div128_by_32((1ULL << 51) - ppc_tb_freq, 0, |
| 730 | tb_ticks_per_jiffy, &res); | 750 | tb_ticks_per_jiffy << SHIFT_HZ, &res); |
| 731 | div128_by_32(res.result_high, res.result_low, NSEC_PER_SEC, &res); | 751 | div128_by_32(res.result_high, res.result_low, NSEC_PER_SEC, &res); |
| 732 | ticklen_to_xs = res.result_low; | 752 | ticklen_to_xs = res.result_low; |
| 733 | 753 | ||
