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 4f20a5f15d49..4a27218a086c 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c | |||
@@ -503,9 +503,9 @@ static inline void update_gtod(u64 new_tb_stamp, u64 new_stamp_xsec, | |||
503 | * the two values of tb_update_count match and are even then the | 503 | * the two values of tb_update_count match and are even then the |
504 | * tb_to_xs and stamp_xsec values are consistent. If not, then it | 504 | * tb_to_xs and stamp_xsec values are consistent. If not, then it |
505 | * loops back and reads them again until this criteria is met. | 505 | * loops back and reads them again until this criteria is met. |
506 | * We expect the caller to have done the first increment of | ||
507 | * vdso_data->tb_update_count already. | ||
506 | */ | 508 | */ |
507 | ++(vdso_data->tb_update_count); | ||
508 | smp_wmb(); | ||
509 | vdso_data->tb_orig_stamp = new_tb_stamp; | 509 | vdso_data->tb_orig_stamp = new_tb_stamp; |
510 | vdso_data->stamp_xsec = new_stamp_xsec; | 510 | vdso_data->stamp_xsec = new_stamp_xsec; |
511 | vdso_data->tb_to_xs = new_tb_to_xs; | 511 | vdso_data->tb_to_xs = new_tb_to_xs; |
@@ -530,20 +530,15 @@ static __inline__ void timer_recalc_offset(u64 cur_tb) | |||
530 | unsigned long offset; | 530 | unsigned long offset; |
531 | u64 new_stamp_xsec; | 531 | u64 new_stamp_xsec; |
532 | u64 tlen, t2x; | 532 | u64 tlen, t2x; |
533 | u64 tb, xsec_old, xsec_new; | ||
534 | struct gettimeofday_vars *varp; | ||
533 | 535 | ||
534 | if (__USE_RTC()) | 536 | if (__USE_RTC()) |
535 | return; | 537 | return; |
536 | tlen = current_tick_length(); | 538 | tlen = current_tick_length(); |
537 | offset = cur_tb - do_gtod.varp->tb_orig_stamp; | 539 | offset = cur_tb - do_gtod.varp->tb_orig_stamp; |
538 | if (tlen == last_tick_len && offset < 0x80000000u) { | 540 | if (tlen == last_tick_len && offset < 0x80000000u) |
539 | /* check that we're still in sync; if not, resync */ | 541 | return; |
540 | struct timeval tv; | ||
541 | __do_gettimeofday(&tv, cur_tb); | ||
542 | if (tv.tv_sec <= xtime.tv_sec && | ||
543 | (tv.tv_sec < xtime.tv_sec || | ||
544 | tv.tv_usec * 1000 <= xtime.tv_nsec)) | ||
545 | return; | ||
546 | } | ||
547 | if (tlen != last_tick_len) { | 542 | if (tlen != last_tick_len) { |
548 | t2x = mulhdu(tlen << TICKLEN_SHIFT, ticklen_to_xs); | 543 | t2x = mulhdu(tlen << TICKLEN_SHIFT, ticklen_to_xs); |
549 | last_tick_len = tlen; | 544 | last_tick_len = tlen; |
@@ -552,6 +547,21 @@ static __inline__ void timer_recalc_offset(u64 cur_tb) | |||
552 | new_stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC; | 547 | new_stamp_xsec = (u64) xtime.tv_nsec * XSEC_PER_SEC; |
553 | do_div(new_stamp_xsec, 1000000000); | 548 | do_div(new_stamp_xsec, 1000000000); |
554 | new_stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC; | 549 | new_stamp_xsec += (u64) xtime.tv_sec * XSEC_PER_SEC; |
550 | |||
551 | ++vdso_data->tb_update_count; | ||
552 | smp_mb(); | ||
553 | |||
554 | /* | ||
555 | * Make sure time doesn't go backwards for userspace gettimeofday. | ||
556 | */ | ||
557 | tb = get_tb(); | ||
558 | varp = do_gtod.varp; | ||
559 | xsec_old = mulhdu(tb - varp->tb_orig_stamp, varp->tb_to_xs) | ||
560 | + varp->stamp_xsec; | ||
561 | xsec_new = mulhdu(tb - cur_tb, t2x) + new_stamp_xsec; | ||
562 | if (xsec_new < xsec_old) | ||
563 | new_stamp_xsec += xsec_old - xsec_new; | ||
564 | |||
555 | update_gtod(cur_tb, new_stamp_xsec, t2x); | 565 | update_gtod(cur_tb, new_stamp_xsec, t2x); |
556 | } | 566 | } |
557 | 567 | ||
@@ -800,6 +810,10 @@ int do_settimeofday(struct timespec *tv) | |||
800 | } | 810 | } |
801 | #endif | 811 | #endif |
802 | 812 | ||
813 | /* Make userspace gettimeofday spin until we're done. */ | ||
814 | ++vdso_data->tb_update_count; | ||
815 | smp_mb(); | ||
816 | |||
803 | /* | 817 | /* |
804 | * Subtract off the number of nanoseconds since the | 818 | * Subtract off the number of nanoseconds since the |
805 | * beginning of the last tick. | 819 | * beginning of the last tick. |
@@ -961,10 +975,16 @@ void __init time_init(void) | |||
961 | * It is computed as: | 975 | * It is computed as: |
962 | * ticklen_to_xs = 2^N / (tb_ticks_per_jiffy * 1e9) | 976 | * ticklen_to_xs = 2^N / (tb_ticks_per_jiffy * 1e9) |
963 | * where N = 64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT | 977 | * where N = 64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT |
964 | * so as to give the result as a 0.64 fixed-point fraction. | 978 | * which turns out to be N = 51 - SHIFT_HZ. |
979 | * This gives the result as a 0.64 fixed-point fraction. | ||
980 | * That value is reduced by an offset amounting to 1 xsec per | ||
981 | * 2^31 timebase ticks to avoid problems with time going backwards | ||
982 | * by 1 xsec when we do timer_recalc_offset due to losing the | ||
983 | * fractional xsec. That offset is equal to ppc_tb_freq/2^51 | ||
984 | * since there are 2^20 xsec in a second. | ||
965 | */ | 985 | */ |
966 | div128_by_32(1ULL << (64 + 20 - TICKLEN_SCALE - TICKLEN_SHIFT), 0, | 986 | div128_by_32((1ULL << 51) - ppc_tb_freq, 0, |
967 | tb_ticks_per_jiffy, &res); | 987 | tb_ticks_per_jiffy << SHIFT_HZ, &res); |
968 | div128_by_32(res.result_high, res.result_low, NSEC_PER_SEC, &res); | 988 | div128_by_32(res.result_high, res.result_low, NSEC_PER_SEC, &res); |
969 | ticklen_to_xs = res.result_low; | 989 | ticklen_to_xs = res.result_low; |
970 | 990 | ||