1 // SPDX-License-Identifier: GPL-2.0-only
3 * This test checks the response of the system clock to frequency
4 * steps made with adjtimex(). The frequency error and stability of
5 * the CLOCK_MONOTONIC clock relative to the CLOCK_MONOTONIC_RAW clock
6 * is measured in two intervals following the step. The test fails if
7 * values from the second interval exceed specified limits.
9 * Copyright (C) Miroslav Lichvar <mlichvar@redhat.com> 2017
14 #include <sys/timex.h>
18 #include "../kselftest.h"
21 #define SAMPLE_READINGS 10
22 #define MEAN_SAMPLE_INTERVAL 0.1
23 #define STEP_INTERVAL 1.0
24 #define MAX_PRECISION 500e-9
25 #define MAX_FREQ_ERROR 0.02e-6
26 #define MAX_STDDEV 50e-9
29 #define ADJ_SETOFFSET 0x0100
37 static time_t mono_raw_base
;
38 static time_t mono_base
;
40 static double precision
;
41 static double mono_freq_offset
;
43 static double diff_timespec(struct timespec
*ts1
, struct timespec
*ts2
)
45 return ts1
->tv_sec
- ts2
->tv_sec
+ (ts1
->tv_nsec
- ts2
->tv_nsec
) / 1e9
;
48 static double get_sample(struct sample
*sample
)
50 double delay
, mindelay
= 0.0;
51 struct timespec ts1
, ts2
, ts3
;
54 for (i
= 0; i
< SAMPLE_READINGS
; i
++) {
55 clock_gettime(CLOCK_MONOTONIC_RAW
, &ts1
);
56 clock_gettime(CLOCK_MONOTONIC
, &ts2
);
57 clock_gettime(CLOCK_MONOTONIC_RAW
, &ts3
);
59 ts1
.tv_sec
-= mono_raw_base
;
60 ts2
.tv_sec
-= mono_base
;
61 ts3
.tv_sec
-= mono_raw_base
;
63 delay
= diff_timespec(&ts3
, &ts1
);
69 if (!i
|| delay
< mindelay
) {
70 sample
->offset
= diff_timespec(&ts2
, &ts1
);
71 sample
->offset
-= delay
/ 2.0;
72 sample
->time
= ts1
.tv_sec
+ ts1
.tv_nsec
/ 1e9
;
80 static void reset_ntp_error(void)
84 txc
.modes
= ADJ_SETOFFSET
;
88 if (adjtimex(&txc
) < 0) {
89 perror("[FAIL] adjtimex");
94 static void set_frequency(double freq
)
99 tick_offset
= 1e6
* freq
/ user_hz
;
101 txc
.modes
= ADJ_TICK
| ADJ_FREQUENCY
;
102 txc
.tick
= 1000000 / user_hz
+ tick_offset
;
103 txc
.freq
= (1e6
* freq
- user_hz
* tick_offset
) * (1 << 16);
105 if (adjtimex(&txc
) < 0) {
106 perror("[FAIL] adjtimex");
111 static void regress(struct sample
*samples
, int n
, double *intercept
,
112 double *slope
, double *r_stddev
, double *r_max
)
114 double x
, y
, r
, x_sum
, y_sum
, xy_sum
, x2_sum
, r2_sum
;
117 x_sum
= 0.0, y_sum
= 0.0, xy_sum
= 0.0, x2_sum
= 0.0;
119 for (i
= 0; i
< n
; i
++) {
121 y
= samples
[i
].offset
;
129 *slope
= (xy_sum
- x_sum
* y_sum
/ n
) / (x2_sum
- x_sum
* x_sum
/ n
);
130 *intercept
= (y_sum
- *slope
* x_sum
) / n
;
132 *r_max
= 0.0, r2_sum
= 0.0;
134 for (i
= 0; i
< n
; i
++) {
136 y
= samples
[i
].offset
;
137 r
= fabs(x
* *slope
+ *intercept
- y
);
143 *r_stddev
= sqrt(r2_sum
/ n
);
146 static int run_test(int calibration
, double freq_base
, double freq_step
)
148 struct sample samples
[SAMPLES
];
149 double intercept
, slope
, stddev1
, max1
, stddev2
, max2
;
150 double freq_error1
, freq_error2
;
153 set_frequency(freq_base
);
155 for (i
= 0; i
< 10; i
++)
156 usleep(1e6
* MEAN_SAMPLE_INTERVAL
/ 10);
160 set_frequency(freq_base
+ freq_step
);
162 for (i
= 0; i
< 10; i
++)
163 usleep(rand() % 2000000 * STEP_INTERVAL
/ 10);
165 set_frequency(freq_base
);
167 for (i
= 0; i
< SAMPLES
; i
++) {
168 usleep(rand() % 2000000 * MEAN_SAMPLE_INTERVAL
);
169 get_sample(&samples
[i
]);
173 regress(samples
, SAMPLES
, &intercept
, &slope
, &stddev1
, &max1
);
174 mono_freq_offset
= slope
;
175 printf("CLOCK_MONOTONIC_RAW frequency offset: %11.3f ppm\n",
176 1e6
* mono_freq_offset
);
180 regress(samples
, SAMPLES
/ 2, &intercept
, &slope
, &stddev1
, &max1
);
181 freq_error1
= slope
* (1.0 - mono_freq_offset
) - mono_freq_offset
-
184 regress(samples
+ SAMPLES
/ 2, SAMPLES
/ 2, &intercept
, &slope
,
186 freq_error2
= slope
* (1.0 - mono_freq_offset
) - mono_freq_offset
-
189 printf("%6.0f %+10.3f %6.0f %7.0f %+10.3f %6.0f %7.0f\t",
191 1e6
* freq_error1
, 1e9
* stddev1
, 1e9
* max1
,
192 1e6
* freq_error2
, 1e9
* stddev2
, 1e9
* max2
);
194 if (fabs(freq_error2
) > MAX_FREQ_ERROR
|| stddev2
> MAX_STDDEV
) {
203 static void init_test(void)
206 struct sample sample
;
208 if (clock_gettime(CLOCK_MONOTONIC_RAW
, &ts
)) {
209 perror("[FAIL] clock_gettime(CLOCK_MONOTONIC_RAW)");
213 mono_raw_base
= ts
.tv_sec
;
215 if (clock_gettime(CLOCK_MONOTONIC
, &ts
)) {
216 perror("[FAIL] clock_gettime(CLOCK_MONOTONIC)");
220 mono_base
= ts
.tv_sec
;
222 user_hz
= sysconf(_SC_CLK_TCK
);
224 precision
= get_sample(&sample
) / 2.0;
225 printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t",
228 if (precision
> MAX_PRECISION
)
229 ksft_exit_skip("precision: %.0f ns > MAX_PRECISION: %.0f ns\n",
230 1e9
* precision
, 1e9
* MAX_PRECISION
);
233 srand(ts
.tv_sec
^ ts
.tv_nsec
);
235 run_test(1, 0.0, 0.0);
238 int main(int argc
, char **argv
)
240 double freq_base
, freq_step
;
245 printf("Checking response to frequency step:\n");
246 printf(" Step 1st interval 2nd interval\n");
247 printf(" Freq Dev Max Freq Dev Max\n");
249 for (i
= 2; i
>= 0; i
--) {
250 for (j
= 0; j
< 5; j
++) {
251 freq_base
= (rand() % (1 << 24) - (1 << 23)) / 65536e6
;
252 freq_step
= 10e-6 * (1 << (6 * i
));
253 fails
+= run_test(0, freq_base
, freq_step
);
260 return ksft_exit_fail();
262 return ksft_exit_pass();