~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/timers/freq-step.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  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.
  8  *
  9  * Copyright (C) Miroslav Lichvar <mlichvar@redhat.com>  2017
 10  */
 11 
 12 #include <math.h>
 13 #include <stdio.h>
 14 #include <sys/timex.h>
 15 #include <time.h>
 16 #include <unistd.h>
 17 
 18 #include "../kselftest.h"
 19 
 20 #define SAMPLES 100
 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
 27 
 28 #ifndef ADJ_SETOFFSET
 29   #define ADJ_SETOFFSET 0x0100
 30 #endif
 31 
 32 struct sample {
 33         double offset;
 34         double time;
 35 };
 36 
 37 static time_t mono_raw_base;
 38 static time_t mono_base;
 39 static long user_hz;
 40 static double precision;
 41 static double mono_freq_offset;
 42 
 43 static double diff_timespec(struct timespec *ts1, struct timespec *ts2)
 44 {
 45         return ts1->tv_sec - ts2->tv_sec + (ts1->tv_nsec - ts2->tv_nsec) / 1e9;
 46 }
 47 
 48 static double get_sample(struct sample *sample)
 49 {
 50         double delay, mindelay = 0.0;
 51         struct timespec ts1, ts2, ts3;
 52         int i;
 53 
 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);
 58 
 59                 ts1.tv_sec -= mono_raw_base;
 60                 ts2.tv_sec -= mono_base;
 61                 ts3.tv_sec -= mono_raw_base;
 62 
 63                 delay = diff_timespec(&ts3, &ts1);
 64                 if (delay <= 1e-9) {
 65                         i--;
 66                         continue;
 67                 }
 68 
 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;
 73                         mindelay = delay;
 74                 }
 75         }
 76 
 77         return mindelay;
 78 }
 79 
 80 static void reset_ntp_error(void)
 81 {
 82         struct timex txc;
 83 
 84         txc.modes = ADJ_SETOFFSET;
 85         txc.time.tv_sec = 0;
 86         txc.time.tv_usec = 0;
 87 
 88         if (adjtimex(&txc) < 0) {
 89                 perror("[FAIL] adjtimex");
 90                 ksft_exit_fail();
 91         }
 92 }
 93 
 94 static void set_frequency(double freq)
 95 {
 96         struct timex txc;
 97         int tick_offset;
 98 
 99         tick_offset = 1e6 * freq / user_hz;
100 
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);
104 
105         if (adjtimex(&txc) < 0) {
106                 perror("[FAIL] adjtimex");
107                 ksft_exit_fail();
108         }
109 }
110 
111 static void regress(struct sample *samples, int n, double *intercept,
112                     double *slope, double *r_stddev, double *r_max)
113 {
114         double x, y, r, x_sum, y_sum, xy_sum, x2_sum, r2_sum;
115         int i;
116 
117         x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0;
118 
119         for (i = 0; i < n; i++) {
120                 x = samples[i].time;
121                 y = samples[i].offset;
122 
123                 x_sum += x;
124                 y_sum += y;
125                 xy_sum += x * y;
126                 x2_sum += x * x;
127         }
128 
129         *slope = (xy_sum - x_sum * y_sum / n) / (x2_sum - x_sum * x_sum / n);
130         *intercept = (y_sum - *slope * x_sum) / n;
131 
132         *r_max = 0.0, r2_sum = 0.0;
133 
134         for (i = 0; i < n; i++) {
135                 x = samples[i].time;
136                 y = samples[i].offset;
137                 r = fabs(x * *slope + *intercept - y);
138                 if (*r_max < r)
139                         *r_max = r;
140                 r2_sum += r * r;
141         }
142 
143         *r_stddev = sqrt(r2_sum / n);
144 }
145 
146 static int run_test(int calibration, double freq_base, double freq_step)
147 {
148         struct sample samples[SAMPLES];
149         double intercept, slope, stddev1, max1, stddev2, max2;
150         double freq_error1, freq_error2;
151         int i;
152 
153         set_frequency(freq_base);
154 
155         for (i = 0; i < 10; i++)
156                 usleep(1e6 * MEAN_SAMPLE_INTERVAL / 10);
157 
158         reset_ntp_error();
159 
160         set_frequency(freq_base + freq_step);
161 
162         for (i = 0; i < 10; i++)
163                 usleep(rand() % 2000000 * STEP_INTERVAL / 10);
164 
165         set_frequency(freq_base);
166 
167         for (i = 0; i < SAMPLES; i++) {
168                 usleep(rand() % 2000000 * MEAN_SAMPLE_INTERVAL);
169                 get_sample(&samples[i]);
170         }
171 
172         if (calibration) {
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);
177                 return 0;
178         }
179 
180         regress(samples, SAMPLES / 2, &intercept, &slope, &stddev1, &max1);
181         freq_error1 = slope * (1.0 - mono_freq_offset) - mono_freq_offset -
182                         freq_base;
183 
184         regress(samples + SAMPLES / 2, SAMPLES / 2, &intercept, &slope,
185                 &stddev2, &max2);
186         freq_error2 = slope * (1.0 - mono_freq_offset) - mono_freq_offset -
187                         freq_base;
188 
189         printf("%6.0f %+10.3f %6.0f %7.0f %+10.3f %6.0f %7.0f\t",
190                1e6 * freq_step,
191                1e6 * freq_error1, 1e9 * stddev1, 1e9 * max1,
192                1e6 * freq_error2, 1e9 * stddev2, 1e9 * max2);
193 
194         if (fabs(freq_error2) > MAX_FREQ_ERROR || stddev2 > MAX_STDDEV) {
195                 printf("[FAIL]\n");
196                 return 1;
197         }
198 
199         printf("[OK]\n");
200         return 0;
201 }
202 
203 static void init_test(void)
204 {
205         struct timespec ts;
206         struct sample sample;
207 
208         if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) {
209                 perror("[FAIL] clock_gettime(CLOCK_MONOTONIC_RAW)");
210                 ksft_exit_fail();
211         }
212 
213         mono_raw_base = ts.tv_sec;
214 
215         if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
216                 perror("[FAIL] clock_gettime(CLOCK_MONOTONIC)");
217                 ksft_exit_fail();
218         }
219 
220         mono_base = ts.tv_sec;
221 
222         user_hz = sysconf(_SC_CLK_TCK);
223 
224         precision = get_sample(&sample) / 2.0;
225         printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t",
226                1e9 * precision);
227 
228         if (precision > MAX_PRECISION)
229                 ksft_exit_skip("precision: %.0f ns > MAX_PRECISION: %.0f ns\n",
230                                 1e9 * precision, 1e9 * MAX_PRECISION);
231 
232         printf("[OK]\n");
233         srand(ts.tv_sec ^ ts.tv_nsec);
234 
235         run_test(1, 0.0, 0.0);
236 }
237 
238 int main(int argc, char **argv)
239 {
240         double freq_base, freq_step;
241         int i, j, fails = 0;
242 
243         init_test();
244 
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");
248 
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);
254                 }
255         }
256 
257         set_frequency(0.0);
258 
259         if (fails)
260                 ksft_exit_fail();
261 
262         ksft_exit_pass();
263 }
264 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php