root/tools/testing/selftests/timers/freq-step.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. diff_timespec
  2. get_sample
  3. reset_ntp_error
  4. set_frequency
  5. regress
  6. run_test
  7. init_test
  8. main

   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                 return ksft_exit_fail();
 261 
 262         return ksft_exit_pass();
 263 }

/* [<][>][^][v][top][bottom][index][help] */