2 * This test checks the response of the system clock to frequency
3 * steps made with adjtimex(). The frequency error and stability of
4 * the CLOCK_MONOTONIC clock relative to the CLOCK_MONOTONIC_RAW clock
5 * is measured in two intervals following the step. The test fails if
6 * values from the second interval exceed specified limits.
8 * Copyright (C) Miroslav Lichvar <mlichvar@redhat.com> 2017
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of version 2 of the GNU General Public License as
12 * published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
22 #include <sys/timex.h>
26 #include "../kselftest.h"
29 #define SAMPLE_READINGS 10
30 #define MEAN_SAMPLE_INTERVAL 0.1
31 #define STEP_INTERVAL 1.0
32 #define MAX_PRECISION 100e-9
33 #define MAX_FREQ_ERROR 10e-6
34 #define MAX_STDDEV 1000e-9
37 #define ADJ_SETOFFSET 0x0100
45 static time_t mono_raw_base
;
46 static time_t mono_base
;
48 static double precision
;
49 static double mono_freq_offset
;
51 static double diff_timespec(struct timespec
*ts1
, struct timespec
*ts2
)
53 return ts1
->tv_sec
- ts2
->tv_sec
+ (ts1
->tv_nsec
- ts2
->tv_nsec
) / 1e9
;
56 static double get_sample(struct sample
*sample
)
58 double delay
, mindelay
= 0.0;
59 struct timespec ts1
, ts2
, ts3
;
62 for (i
= 0; i
< SAMPLE_READINGS
; i
++) {
63 clock_gettime(CLOCK_MONOTONIC_RAW
, &ts1
);
64 clock_gettime(CLOCK_MONOTONIC
, &ts2
);
65 clock_gettime(CLOCK_MONOTONIC_RAW
, &ts3
);
67 ts1
.tv_sec
-= mono_raw_base
;
68 ts2
.tv_sec
-= mono_base
;
69 ts3
.tv_sec
-= mono_raw_base
;
71 delay
= diff_timespec(&ts3
, &ts1
);
77 if (!i
|| delay
< mindelay
) {
78 sample
->offset
= diff_timespec(&ts2
, &ts1
);
79 sample
->offset
-= delay
/ 2.0;
80 sample
->time
= ts1
.tv_sec
+ ts1
.tv_nsec
/ 1e9
;
88 static void reset_ntp_error(void)
92 txc
.modes
= ADJ_SETOFFSET
;
96 if (adjtimex(&txc
) < 0) {
97 perror("[FAIL] adjtimex");
102 static void set_frequency(double freq
)
107 tick_offset
= 1e6
* freq
/ user_hz
;
109 txc
.modes
= ADJ_TICK
| ADJ_FREQUENCY
;
110 txc
.tick
= 1000000 / user_hz
+ tick_offset
;
111 txc
.freq
= (1e6
* freq
- user_hz
* tick_offset
) * (1 << 16);
113 if (adjtimex(&txc
) < 0) {
114 perror("[FAIL] adjtimex");
119 static void regress(struct sample
*samples
, int n
, double *intercept
,
120 double *slope
, double *r_stddev
, double *r_max
)
122 double x
, y
, r
, x_sum
, y_sum
, xy_sum
, x2_sum
, r2_sum
;
125 x_sum
= 0.0, y_sum
= 0.0, xy_sum
= 0.0, x2_sum
= 0.0;
127 for (i
= 0; i
< n
; i
++) {
129 y
= samples
[i
].offset
;
137 *slope
= (xy_sum
- x_sum
* y_sum
/ n
) / (x2_sum
- x_sum
* x_sum
/ n
);
138 *intercept
= (y_sum
- *slope
* x_sum
) / n
;
140 *r_max
= 0.0, r2_sum
= 0.0;
142 for (i
= 0; i
< n
; i
++) {
144 y
= samples
[i
].offset
;
145 r
= fabs(x
* *slope
+ *intercept
- y
);
151 *r_stddev
= sqrt(r2_sum
/ n
);
154 static int run_test(int calibration
, double freq_base
, double freq_step
)
156 struct sample samples
[SAMPLES
];
157 double intercept
, slope
, stddev1
, max1
, stddev2
, max2
;
158 double freq_error1
, freq_error2
;
161 set_frequency(freq_base
);
163 for (i
= 0; i
< 10; i
++)
164 usleep(1e6
* MEAN_SAMPLE_INTERVAL
/ 10);
168 set_frequency(freq_base
+ freq_step
);
170 for (i
= 0; i
< 10; i
++)
171 usleep(rand() % 2000000 * STEP_INTERVAL
/ 10);
173 set_frequency(freq_base
);
175 for (i
= 0; i
< SAMPLES
; i
++) {
176 usleep(rand() % 2000000 * MEAN_SAMPLE_INTERVAL
);
177 get_sample(&samples
[i
]);
181 regress(samples
, SAMPLES
, &intercept
, &slope
, &stddev1
, &max1
);
182 mono_freq_offset
= slope
;
183 printf("CLOCK_MONOTONIC_RAW frequency offset: %11.3f ppm\n",
184 1e6
* mono_freq_offset
);
188 regress(samples
, SAMPLES
/ 2, &intercept
, &slope
, &stddev1
, &max1
);
189 freq_error1
= slope
* (1.0 - mono_freq_offset
) - mono_freq_offset
-
192 regress(samples
+ SAMPLES
/ 2, SAMPLES
/ 2, &intercept
, &slope
,
194 freq_error2
= slope
* (1.0 - mono_freq_offset
) - mono_freq_offset
-
197 printf("%6.0f %+10.3f %6.0f %7.0f %+10.3f %6.0f %7.0f\t",
199 1e6
* freq_error1
, 1e9
* stddev1
, 1e9
* max1
,
200 1e6
* freq_error2
, 1e9
* stddev2
, 1e9
* max2
);
202 if (fabs(freq_error2
) > MAX_FREQ_ERROR
|| stddev2
> MAX_STDDEV
) {
211 static void init_test(void)
214 struct sample sample
;
216 if (clock_gettime(CLOCK_MONOTONIC_RAW
, &ts
)) {
217 perror("[FAIL] clock_gettime(CLOCK_MONOTONIC_RAW)");
221 mono_raw_base
= ts
.tv_sec
;
223 if (clock_gettime(CLOCK_MONOTONIC
, &ts
)) {
224 perror("[FAIL] clock_gettime(CLOCK_MONOTONIC)");
228 mono_base
= ts
.tv_sec
;
230 user_hz
= sysconf(_SC_CLK_TCK
);
232 precision
= get_sample(&sample
) / 2.0;
233 printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t",
236 if (precision
> MAX_PRECISION
)
237 ksft_exit_skip("precision: %.0f ns > MAX_PRECISION: %.0f ns\n",
238 1e9
* precision
, 1e9
* MAX_PRECISION
);
241 srand(ts
.tv_sec
^ ts
.tv_nsec
);
243 run_test(1, 0.0, 0.0);
246 int main(int argc
, char **argv
)
248 double freq_base
, freq_step
;
253 printf("Checking response to frequency step:\n");
254 printf(" Step 1st interval 2nd interval\n");
255 printf(" Freq Dev Max Freq Dev Max\n");
257 for (i
= 2; i
>= 0; i
--) {
258 for (j
= 0; j
< 5; j
++) {
259 freq_base
= (rand() % (1 << 24) - (1 << 23)) / 65536e6
;
260 freq_step
= 10e-6 * (1 << (6 * i
));
261 fails
+= run_test(0, freq_base
, freq_step
);
268 return ksft_exit_fail();
270 return ksft_exit_pass();