18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This test checks the response of the system clock to frequency 48c2ecf20Sopenharmony_ci * steps made with adjtimex(). The frequency error and stability of 58c2ecf20Sopenharmony_ci * the CLOCK_MONOTONIC clock relative to the CLOCK_MONOTONIC_RAW clock 68c2ecf20Sopenharmony_ci * is measured in two intervals following the step. The test fails if 78c2ecf20Sopenharmony_ci * values from the second interval exceed specified limits. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (C) Miroslav Lichvar <mlichvar@redhat.com> 2017 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <math.h> 138c2ecf20Sopenharmony_ci#include <stdio.h> 148c2ecf20Sopenharmony_ci#include <sys/timex.h> 158c2ecf20Sopenharmony_ci#include <time.h> 168c2ecf20Sopenharmony_ci#include <unistd.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "../kselftest.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define SAMPLES 100 218c2ecf20Sopenharmony_ci#define SAMPLE_READINGS 10 228c2ecf20Sopenharmony_ci#define MEAN_SAMPLE_INTERVAL 0.1 238c2ecf20Sopenharmony_ci#define STEP_INTERVAL 1.0 248c2ecf20Sopenharmony_ci#define MAX_PRECISION 500e-9 258c2ecf20Sopenharmony_ci#define MAX_FREQ_ERROR 0.02e-6 268c2ecf20Sopenharmony_ci#define MAX_STDDEV 50e-9 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#ifndef ADJ_SETOFFSET 298c2ecf20Sopenharmony_ci #define ADJ_SETOFFSET 0x0100 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct sample { 338c2ecf20Sopenharmony_ci double offset; 348c2ecf20Sopenharmony_ci double time; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic time_t mono_raw_base; 388c2ecf20Sopenharmony_cistatic time_t mono_base; 398c2ecf20Sopenharmony_cistatic long user_hz; 408c2ecf20Sopenharmony_cistatic double precision; 418c2ecf20Sopenharmony_cistatic double mono_freq_offset; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic double diff_timespec(struct timespec *ts1, struct timespec *ts2) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return ts1->tv_sec - ts2->tv_sec + (ts1->tv_nsec - ts2->tv_nsec) / 1e9; 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic double get_sample(struct sample *sample) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci double delay, mindelay = 0.0; 518c2ecf20Sopenharmony_ci struct timespec ts1, ts2, ts3; 528c2ecf20Sopenharmony_ci int i; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci for (i = 0; i < SAMPLE_READINGS; i++) { 558c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, &ts1); 568c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &ts2); 578c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, &ts3); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ts1.tv_sec -= mono_raw_base; 608c2ecf20Sopenharmony_ci ts2.tv_sec -= mono_base; 618c2ecf20Sopenharmony_ci ts3.tv_sec -= mono_raw_base; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci delay = diff_timespec(&ts3, &ts1); 648c2ecf20Sopenharmony_ci if (delay <= 1e-9) { 658c2ecf20Sopenharmony_ci i--; 668c2ecf20Sopenharmony_ci continue; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (!i || delay < mindelay) { 708c2ecf20Sopenharmony_ci sample->offset = diff_timespec(&ts2, &ts1); 718c2ecf20Sopenharmony_ci sample->offset -= delay / 2.0; 728c2ecf20Sopenharmony_ci sample->time = ts1.tv_sec + ts1.tv_nsec / 1e9; 738c2ecf20Sopenharmony_ci mindelay = delay; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return mindelay; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void reset_ntp_error(void) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct timex txc; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci txc.modes = ADJ_SETOFFSET; 858c2ecf20Sopenharmony_ci txc.time.tv_sec = 0; 868c2ecf20Sopenharmony_ci txc.time.tv_usec = 0; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (adjtimex(&txc) < 0) { 898c2ecf20Sopenharmony_ci perror("[FAIL] adjtimex"); 908c2ecf20Sopenharmony_ci ksft_exit_fail(); 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void set_frequency(double freq) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct timex txc; 978c2ecf20Sopenharmony_ci int tick_offset; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci tick_offset = 1e6 * freq / user_hz; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci txc.modes = ADJ_TICK | ADJ_FREQUENCY; 1028c2ecf20Sopenharmony_ci txc.tick = 1000000 / user_hz + tick_offset; 1038c2ecf20Sopenharmony_ci txc.freq = (1e6 * freq - user_hz * tick_offset) * (1 << 16); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (adjtimex(&txc) < 0) { 1068c2ecf20Sopenharmony_ci perror("[FAIL] adjtimex"); 1078c2ecf20Sopenharmony_ci ksft_exit_fail(); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void regress(struct sample *samples, int n, double *intercept, 1128c2ecf20Sopenharmony_ci double *slope, double *r_stddev, double *r_max) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci double x, y, r, x_sum, y_sum, xy_sum, x2_sum, r2_sum; 1158c2ecf20Sopenharmony_ci int i; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 1208c2ecf20Sopenharmony_ci x = samples[i].time; 1218c2ecf20Sopenharmony_ci y = samples[i].offset; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci x_sum += x; 1248c2ecf20Sopenharmony_ci y_sum += y; 1258c2ecf20Sopenharmony_ci xy_sum += x * y; 1268c2ecf20Sopenharmony_ci x2_sum += x * x; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci *slope = (xy_sum - x_sum * y_sum / n) / (x2_sum - x_sum * x_sum / n); 1308c2ecf20Sopenharmony_ci *intercept = (y_sum - *slope * x_sum) / n; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci *r_max = 0.0, r2_sum = 0.0; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) { 1358c2ecf20Sopenharmony_ci x = samples[i].time; 1368c2ecf20Sopenharmony_ci y = samples[i].offset; 1378c2ecf20Sopenharmony_ci r = fabs(x * *slope + *intercept - y); 1388c2ecf20Sopenharmony_ci if (*r_max < r) 1398c2ecf20Sopenharmony_ci *r_max = r; 1408c2ecf20Sopenharmony_ci r2_sum += r * r; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci *r_stddev = sqrt(r2_sum / n); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic int run_test(int calibration, double freq_base, double freq_step) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct sample samples[SAMPLES]; 1498c2ecf20Sopenharmony_ci double intercept, slope, stddev1, max1, stddev2, max2; 1508c2ecf20Sopenharmony_ci double freq_error1, freq_error2; 1518c2ecf20Sopenharmony_ci int i; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci set_frequency(freq_base); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) 1568c2ecf20Sopenharmony_ci usleep(1e6 * MEAN_SAMPLE_INTERVAL / 10); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci reset_ntp_error(); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci set_frequency(freq_base + freq_step); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for (i = 0; i < 10; i++) 1638c2ecf20Sopenharmony_ci usleep(rand() % 2000000 * STEP_INTERVAL / 10); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci set_frequency(freq_base); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (i = 0; i < SAMPLES; i++) { 1688c2ecf20Sopenharmony_ci usleep(rand() % 2000000 * MEAN_SAMPLE_INTERVAL); 1698c2ecf20Sopenharmony_ci get_sample(&samples[i]); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (calibration) { 1738c2ecf20Sopenharmony_ci regress(samples, SAMPLES, &intercept, &slope, &stddev1, &max1); 1748c2ecf20Sopenharmony_ci mono_freq_offset = slope; 1758c2ecf20Sopenharmony_ci printf("CLOCK_MONOTONIC_RAW frequency offset: %11.3f ppm\n", 1768c2ecf20Sopenharmony_ci 1e6 * mono_freq_offset); 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci regress(samples, SAMPLES / 2, &intercept, &slope, &stddev1, &max1); 1818c2ecf20Sopenharmony_ci freq_error1 = slope * (1.0 - mono_freq_offset) - mono_freq_offset - 1828c2ecf20Sopenharmony_ci freq_base; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci regress(samples + SAMPLES / 2, SAMPLES / 2, &intercept, &slope, 1858c2ecf20Sopenharmony_ci &stddev2, &max2); 1868c2ecf20Sopenharmony_ci freq_error2 = slope * (1.0 - mono_freq_offset) - mono_freq_offset - 1878c2ecf20Sopenharmony_ci freq_base; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci printf("%6.0f %+10.3f %6.0f %7.0f %+10.3f %6.0f %7.0f\t", 1908c2ecf20Sopenharmony_ci 1e6 * freq_step, 1918c2ecf20Sopenharmony_ci 1e6 * freq_error1, 1e9 * stddev1, 1e9 * max1, 1928c2ecf20Sopenharmony_ci 1e6 * freq_error2, 1e9 * stddev2, 1e9 * max2); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (fabs(freq_error2) > MAX_FREQ_ERROR || stddev2 > MAX_STDDEV) { 1958c2ecf20Sopenharmony_ci printf("[FAIL]\n"); 1968c2ecf20Sopenharmony_ci return 1; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci printf("[OK]\n"); 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void init_test(void) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct timespec ts; 2068c2ecf20Sopenharmony_ci struct sample sample; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) { 2098c2ecf20Sopenharmony_ci perror("[FAIL] clock_gettime(CLOCK_MONOTONIC_RAW)"); 2108c2ecf20Sopenharmony_ci ksft_exit_fail(); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci mono_raw_base = ts.tv_sec; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC, &ts)) { 2168c2ecf20Sopenharmony_ci perror("[FAIL] clock_gettime(CLOCK_MONOTONIC)"); 2178c2ecf20Sopenharmony_ci ksft_exit_fail(); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci mono_base = ts.tv_sec; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci user_hz = sysconf(_SC_CLK_TCK); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci precision = get_sample(&sample) / 2.0; 2258c2ecf20Sopenharmony_ci printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t", 2268c2ecf20Sopenharmony_ci 1e9 * precision); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (precision > MAX_PRECISION) 2298c2ecf20Sopenharmony_ci ksft_exit_skip("precision: %.0f ns > MAX_PRECISION: %.0f ns\n", 2308c2ecf20Sopenharmony_ci 1e9 * precision, 1e9 * MAX_PRECISION); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci printf("[OK]\n"); 2338c2ecf20Sopenharmony_ci srand(ts.tv_sec ^ ts.tv_nsec); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci run_test(1, 0.0, 0.0); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciint main(int argc, char **argv) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci double freq_base, freq_step; 2418c2ecf20Sopenharmony_ci int i, j, fails = 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci init_test(); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci printf("Checking response to frequency step:\n"); 2468c2ecf20Sopenharmony_ci printf(" Step 1st interval 2nd interval\n"); 2478c2ecf20Sopenharmony_ci printf(" Freq Dev Max Freq Dev Max\n"); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci for (i = 2; i >= 0; i--) { 2508c2ecf20Sopenharmony_ci for (j = 0; j < 5; j++) { 2518c2ecf20Sopenharmony_ci freq_base = (rand() % (1 << 24) - (1 << 23)) / 65536e6; 2528c2ecf20Sopenharmony_ci freq_step = 10e-6 * (1 << (6 * i)); 2538c2ecf20Sopenharmony_ci fails += run_test(0, freq_base, freq_step); 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci set_frequency(0.0); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (fails) 2608c2ecf20Sopenharmony_ci return ksft_exit_fail(); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return ksft_exit_pass(); 2638c2ecf20Sopenharmony_ci} 264