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