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