Skip to content

Commit d4cfb11

Browse files
paulusmackmpe
authored andcommitted
powerpc: Convert VDSO update function to use new update_vsyscall interface
This converts the powerpc VDSO time update function to use the new interface introduced in commit 576094b ("time: Introduce new GENERIC_TIME_VSYSCALL", 2012-09-11). Where the old interface gave us the time as of the last update in seconds and whole nanoseconds, with the new interface we get the nanoseconds part effectively in a binary fixed-point format with tk->tkr_mono.shift bits to the right of the binary point. With the old interface, the fractional nanoseconds got truncated, meaning that the value returned by the VDSO clock_gettime function would have about 1ns of jitter in it compared to the value computed by the generic timekeeping code in the kernel. The powerpc VDSO time functions (clock_gettime and gettimeofday) already work in units of 2^-32 seconds, or 0.23283 ns, because that makes it simple to split the result into seconds and fractional seconds, and represent the fractional seconds in either microseconds or nanoseconds. This is good enough accuracy for now, so this patch avoids changing how the VDSO works or the interface in the VDSO data page. This patch converts the powerpc update_vsyscall_old to be called update_vsyscall and use the new interface. We convert the fractional second to units of 2^-32 seconds without truncating to whole nanoseconds. (There is still a conversion to whole nanoseconds for any legacy users of the vdso_data/systemcfg stamp_xtime field.) In addition, this improves the accuracy of the computation of tb_to_xs for those systems with high-frequency timebase clocks (>= 268.5 MHz) by doing the right shift in two parts, one before the multiplication and one after, rather than doing the right shift before the multiplication. (We can't do all of the right shift after the multiplication unless we use 128-bit arithmetic.) Signed-off-by: Paul Mackerras <paulus@ozlabs.org> Acked-by: John Stultz <john.stultz@linaro.org> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
1 parent 6b847d7 commit d4cfb11

File tree

2 files changed

+53
-17
lines changed

2 files changed

+53
-17
lines changed

arch/powerpc/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ config PPC
155155
select GENERIC_SMP_IDLE_THREAD
156156
select GENERIC_STRNCPY_FROM_USER
157157
select GENERIC_STRNLEN_USER
158-
select GENERIC_TIME_VSYSCALL_OLD
158+
select GENERIC_TIME_VSYSCALL
159159
select HAVE_ARCH_AUDITSYSCALL
160160
select HAVE_ARCH_JUMP_LABEL
161161
select HAVE_ARCH_KGDB

arch/powerpc/kernel/time.c

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -841,28 +841,66 @@ static notrace u64 timebase_read(struct clocksource *cs)
841841
return (u64)get_tb();
842842
}
843843

844-
void update_vsyscall_old(struct timespec *wall_time, struct timespec *wtm,
845-
struct clocksource *clock, u32 mult, u64 cycle_last)
844+
845+
void update_vsyscall(struct timekeeper *tk)
846846
{
847+
struct timespec xt;
848+
struct clocksource *clock = tk->tkr_mono.clock;
849+
u32 mult = tk->tkr_mono.mult;
850+
u32 shift = tk->tkr_mono.shift;
851+
u64 cycle_last = tk->tkr_mono.cycle_last;
847852
u64 new_tb_to_xs, new_stamp_xsec;
848-
u32 frac_sec;
853+
u64 frac_sec;
849854

850855
if (clock != &clocksource_timebase)
851856
return;
852857

858+
xt.tv_sec = tk->xtime_sec;
859+
xt.tv_nsec = (long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
860+
853861
/* Make userspace gettimeofday spin until we're done. */
854862
++vdso_data->tb_update_count;
855863
smp_mb();
856864

857-
/* 19342813113834067 ~= 2^(20+64) / 1e9 */
858-
new_tb_to_xs = (u64) mult * (19342813113834067ULL >> clock->shift);
859-
new_stamp_xsec = (u64) wall_time->tv_nsec * XSEC_PER_SEC;
860-
do_div(new_stamp_xsec, 1000000000);
861-
new_stamp_xsec += (u64) wall_time->tv_sec * XSEC_PER_SEC;
865+
/*
866+
* This computes ((2^20 / 1e9) * mult) >> shift as a
867+
* 0.64 fixed-point fraction.
868+
* The computation in the else clause below won't overflow
869+
* (as long as the timebase frequency is >= 1.049 MHz)
870+
* but loses precision because we lose the low bits of the constant
871+
* in the shift. Note that 19342813113834067 ~= 2^(20+64) / 1e9.
872+
* For a shift of 24 the error is about 0.5e-9, or about 0.5ns
873+
* over a second. (Shift values are usually 22, 23 or 24.)
874+
* For high frequency clocks such as the 512MHz timebase clock
875+
* on POWER[6789], the mult value is small (e.g. 32768000)
876+
* and so we can shift the constant by 16 initially
877+
* (295147905179 ~= 2^(20+64-16) / 1e9) and then do the
878+
* remaining shifts after the multiplication, which gives a
879+
* more accurate result (e.g. with mult = 32768000, shift = 24,
880+
* the error is only about 1.2e-12, or 0.7ns over 10 minutes).
881+
*/
882+
if (mult <= 62500000 && clock->shift >= 16)
883+
new_tb_to_xs = ((u64) mult * 295147905179ULL) >> (clock->shift - 16);
884+
else
885+
new_tb_to_xs = (u64) mult * (19342813113834067ULL >> clock->shift);
886+
887+
/*
888+
* Compute the fractional second in units of 2^-32 seconds.
889+
* The fractional second is tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift
890+
* in nanoseconds, so multiplying that by 2^32 / 1e9 gives
891+
* it in units of 2^-32 seconds.
892+
* We assume shift <= 32 because clocks_calc_mult_shift()
893+
* generates shift values in the range 0 - 32.
894+
*/
895+
frac_sec = tk->tkr_mono.xtime_nsec << (32 - shift);
896+
do_div(frac_sec, NSEC_PER_SEC);
862897

863-
BUG_ON(wall_time->tv_nsec >= NSEC_PER_SEC);
864-
/* this is tv_nsec / 1e9 as a 0.32 fraction */
865-
frac_sec = ((u64) wall_time->tv_nsec * 18446744073ULL) >> 32;
898+
/*
899+
* Work out new stamp_xsec value for any legacy users of systemcfg.
900+
* stamp_xsec is in units of 2^-20 seconds.
901+
*/
902+
new_stamp_xsec = frac_sec >> 12;
903+
new_stamp_xsec += tk->xtime_sec * XSEC_PER_SEC;
866904

867905
/*
868906
* tb_update_count is used to allow the userspace gettimeofday code
@@ -872,15 +910,13 @@ void update_vsyscall_old(struct timespec *wall_time, struct timespec *wtm,
872910
* the two values of tb_update_count match and are even then the
873911
* tb_to_xs and stamp_xsec values are consistent. If not, then it
874912
* loops back and reads them again until this criteria is met.
875-
* We expect the caller to have done the first increment of
876-
* vdso_data->tb_update_count already.
877913
*/
878914
vdso_data->tb_orig_stamp = cycle_last;
879915
vdso_data->stamp_xsec = new_stamp_xsec;
880916
vdso_data->tb_to_xs = new_tb_to_xs;
881-
vdso_data->wtom_clock_sec = wtm->tv_sec;
882-
vdso_data->wtom_clock_nsec = wtm->tv_nsec;
883-
vdso_data->stamp_xtime = *wall_time;
917+
vdso_data->wtom_clock_sec = tk->wall_to_monotonic.tv_sec;
918+
vdso_data->wtom_clock_nsec = tk->wall_to_monotonic.tv_nsec;
919+
vdso_data->stamp_xtime = xt;
884920
vdso_data->stamp_sec_fraction = frac_sec;
885921
smp_wmb();
886922
++(vdso_data->tb_update_count);

0 commit comments

Comments
 (0)