16035519fSJohn Stultz /* adjtimex() tick adjustment test
26035519fSJohn Stultz * by: John Stultz <john.stultz@linaro.org>
36035519fSJohn Stultz * (C) Copyright Linaro Limited 2015
46035519fSJohn Stultz * Licensed under the GPLv2
56035519fSJohn Stultz *
66035519fSJohn Stultz * To build:
76035519fSJohn Stultz * $ gcc adjtick.c -o adjtick -lrt
86035519fSJohn Stultz *
96035519fSJohn Stultz * This program is free software: you can redistribute it and/or modify
106035519fSJohn Stultz * it under the terms of the GNU General Public License as published by
116035519fSJohn Stultz * the Free Software Foundation, either version 2 of the License, or
126035519fSJohn Stultz * (at your option) any later version.
136035519fSJohn Stultz *
146035519fSJohn Stultz * This program is distributed in the hope that it will be useful,
156035519fSJohn Stultz * but WITHOUT ANY WARRANTY; without even the implied warranty of
166035519fSJohn Stultz * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
176035519fSJohn Stultz * GNU General Public License for more details.
186035519fSJohn Stultz */
196035519fSJohn Stultz #include <stdio.h>
206035519fSJohn Stultz #include <unistd.h>
216035519fSJohn Stultz #include <stdlib.h>
226035519fSJohn Stultz #include <sys/time.h>
236035519fSJohn Stultz #include <sys/timex.h>
246035519fSJohn Stultz #include <time.h>
256035519fSJohn Stultz
266035519fSJohn Stultz #include "../kselftest.h"
276035519fSJohn Stultz
286035519fSJohn Stultz #define CLOCK_MONOTONIC_RAW 4
296035519fSJohn Stultz
306035519fSJohn Stultz #define NSEC_PER_SEC 1000000000LL
316035519fSJohn Stultz #define USEC_PER_SEC 1000000
326035519fSJohn Stultz
336035519fSJohn Stultz #define MILLION 1000000
346035519fSJohn Stultz
356035519fSJohn Stultz long systick;
366035519fSJohn Stultz
llabs(long long val)376035519fSJohn Stultz long long llabs(long long val)
386035519fSJohn Stultz {
396035519fSJohn Stultz if (val < 0)
406035519fSJohn Stultz val = -val;
416035519fSJohn Stultz return val;
426035519fSJohn Stultz }
436035519fSJohn Stultz
ts_to_nsec(struct timespec ts)446035519fSJohn Stultz unsigned long long ts_to_nsec(struct timespec ts)
456035519fSJohn Stultz {
466035519fSJohn Stultz return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
476035519fSJohn Stultz }
486035519fSJohn Stultz
nsec_to_ts(long long ns)496035519fSJohn Stultz struct timespec nsec_to_ts(long long ns)
506035519fSJohn Stultz {
516035519fSJohn Stultz struct timespec ts;
526035519fSJohn Stultz
536035519fSJohn Stultz ts.tv_sec = ns/NSEC_PER_SEC;
546035519fSJohn Stultz ts.tv_nsec = ns%NSEC_PER_SEC;
556035519fSJohn Stultz
566035519fSJohn Stultz return ts;
576035519fSJohn Stultz }
586035519fSJohn Stultz
diff_timespec(struct timespec start,struct timespec end)596035519fSJohn Stultz long long diff_timespec(struct timespec start, struct timespec end)
606035519fSJohn Stultz {
616035519fSJohn Stultz long long start_ns, end_ns;
626035519fSJohn Stultz
636035519fSJohn Stultz start_ns = ts_to_nsec(start);
646035519fSJohn Stultz end_ns = ts_to_nsec(end);
656035519fSJohn Stultz
666035519fSJohn Stultz return end_ns - start_ns;
676035519fSJohn Stultz }
686035519fSJohn Stultz
get_monotonic_and_raw(struct timespec * mon,struct timespec * raw)696035519fSJohn Stultz void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
706035519fSJohn Stultz {
716035519fSJohn Stultz struct timespec start, mid, end;
726035519fSJohn Stultz long long diff = 0, tmp;
736035519fSJohn Stultz int i;
746035519fSJohn Stultz
756035519fSJohn Stultz clock_gettime(CLOCK_MONOTONIC, mon);
766035519fSJohn Stultz clock_gettime(CLOCK_MONOTONIC_RAW, raw);
776035519fSJohn Stultz
786035519fSJohn Stultz /* Try to get a more tightly bound pairing */
796035519fSJohn Stultz for (i = 0; i < 3; i++) {
806035519fSJohn Stultz long long newdiff;
816035519fSJohn Stultz
826035519fSJohn Stultz clock_gettime(CLOCK_MONOTONIC, &start);
836035519fSJohn Stultz clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
846035519fSJohn Stultz clock_gettime(CLOCK_MONOTONIC, &end);
856035519fSJohn Stultz
866035519fSJohn Stultz newdiff = diff_timespec(start, end);
876035519fSJohn Stultz if (diff == 0 || newdiff < diff) {
886035519fSJohn Stultz diff = newdiff;
896035519fSJohn Stultz *raw = mid;
906035519fSJohn Stultz tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
916035519fSJohn Stultz *mon = nsec_to_ts(tmp);
926035519fSJohn Stultz }
936035519fSJohn Stultz }
946035519fSJohn Stultz }
956035519fSJohn Stultz
get_ppm_drift(void)966035519fSJohn Stultz long long get_ppm_drift(void)
976035519fSJohn Stultz {
986035519fSJohn Stultz struct timespec mon_start, raw_start, mon_end, raw_end;
996035519fSJohn Stultz long long delta1, delta2, eppm;
1006035519fSJohn Stultz
1016035519fSJohn Stultz get_monotonic_and_raw(&mon_start, &raw_start);
1026035519fSJohn Stultz
1036035519fSJohn Stultz sleep(15);
1046035519fSJohn Stultz
1056035519fSJohn Stultz get_monotonic_and_raw(&mon_end, &raw_end);
1066035519fSJohn Stultz
1076035519fSJohn Stultz delta1 = diff_timespec(mon_start, mon_end);
1086035519fSJohn Stultz delta2 = diff_timespec(raw_start, raw_end);
1096035519fSJohn Stultz
1106035519fSJohn Stultz eppm = (delta1*MILLION)/delta2 - MILLION;
1116035519fSJohn Stultz
1126035519fSJohn Stultz return eppm;
1136035519fSJohn Stultz }
1146035519fSJohn Stultz
check_tick_adj(long tickval)1156035519fSJohn Stultz int check_tick_adj(long tickval)
1166035519fSJohn Stultz {
1176035519fSJohn Stultz long long eppm, ppm;
1186035519fSJohn Stultz struct timex tx1;
1196035519fSJohn Stultz
1206035519fSJohn Stultz tx1.modes = ADJ_TICK;
1216035519fSJohn Stultz tx1.modes |= ADJ_OFFSET;
1226035519fSJohn Stultz tx1.modes |= ADJ_FREQUENCY;
1236035519fSJohn Stultz tx1.modes |= ADJ_STATUS;
1246035519fSJohn Stultz
1256035519fSJohn Stultz tx1.status = STA_PLL;
1266035519fSJohn Stultz tx1.offset = 0;
1276035519fSJohn Stultz tx1.freq = 0;
1286035519fSJohn Stultz tx1.tick = tickval;
1296035519fSJohn Stultz
1306035519fSJohn Stultz adjtimex(&tx1);
1316035519fSJohn Stultz
1326035519fSJohn Stultz sleep(1);
1336035519fSJohn Stultz
1346035519fSJohn Stultz ppm = ((long long)tickval * MILLION)/systick - MILLION;
1356035519fSJohn Stultz printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);
1366035519fSJohn Stultz
1376035519fSJohn Stultz eppm = get_ppm_drift();
1386035519fSJohn Stultz printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
139fe483192SKees Cook fflush(stdout);
1406035519fSJohn Stultz
1416035519fSJohn Stultz tx1.modes = 0;
1426035519fSJohn Stultz adjtimex(&tx1);
1436035519fSJohn Stultz
1446035519fSJohn Stultz if (tx1.offset || tx1.freq || tx1.tick != tickval) {
1456035519fSJohn Stultz printf(" [ERROR]\n");
1466035519fSJohn Stultz printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");
1476035519fSJohn Stultz return -1;
1486035519fSJohn Stultz }
1496035519fSJohn Stultz
1506035519fSJohn Stultz /*
1516035519fSJohn Stultz * Here we use 100ppm difference as an error bound.
1526035519fSJohn Stultz * We likely should see better, but some coarse clocksources
1536035519fSJohn Stultz * cannot match the HZ tick size accurately, so we have a
1546035519fSJohn Stultz * internal correction factor that doesn't scale exactly
1556035519fSJohn Stultz * with the adjustment, resulting in > 10ppm error during
1566035519fSJohn Stultz * a 10% adjustment. 100ppm also gives us more breathing
1576035519fSJohn Stultz * room for interruptions during the measurement.
1586035519fSJohn Stultz */
1596035519fSJohn Stultz if (llabs(eppm - ppm) > 100) {
1606035519fSJohn Stultz printf(" [FAILED]\n");
1616035519fSJohn Stultz return -1;
1626035519fSJohn Stultz }
1636035519fSJohn Stultz printf(" [OK]\n");
1646035519fSJohn Stultz
1656035519fSJohn Stultz return 0;
1666035519fSJohn Stultz }
1676035519fSJohn Stultz
main(int argc,char ** argv)168a8d74fe7SWolfram Sang int main(int argc, char **argv)
1696035519fSJohn Stultz {
1706035519fSJohn Stultz struct timespec raw;
1716035519fSJohn Stultz long tick, max, interval, err;
1726035519fSJohn Stultz struct timex tx1;
1736035519fSJohn Stultz
1746035519fSJohn Stultz err = 0;
1756035519fSJohn Stultz setbuf(stdout, NULL);
1766035519fSJohn Stultz
1776035519fSJohn Stultz if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
1786035519fSJohn Stultz printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
1796035519fSJohn Stultz return -1;
1806035519fSJohn Stultz }
1816035519fSJohn Stultz
1826035519fSJohn Stultz printf("Each iteration takes about 15 seconds\n");
1836035519fSJohn Stultz
1846035519fSJohn Stultz systick = sysconf(_SC_CLK_TCK);
1856035519fSJohn Stultz systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);
1866035519fSJohn Stultz max = systick/10; /* +/- 10% */
1876035519fSJohn Stultz interval = max/4; /* in 4 steps each side */
1886035519fSJohn Stultz
1896035519fSJohn Stultz for (tick = (systick - max); tick < (systick + max); tick += interval) {
1906035519fSJohn Stultz if (check_tick_adj(tick)) {
1916035519fSJohn Stultz err = 1;
1926035519fSJohn Stultz break;
1936035519fSJohn Stultz }
1946035519fSJohn Stultz }
1956035519fSJohn Stultz
1966035519fSJohn Stultz /* Reset things to zero */
1976035519fSJohn Stultz tx1.modes = ADJ_TICK;
1986035519fSJohn Stultz tx1.modes |= ADJ_OFFSET;
1996035519fSJohn Stultz tx1.modes |= ADJ_FREQUENCY;
2006035519fSJohn Stultz
2016035519fSJohn Stultz tx1.offset = 0;
2026035519fSJohn Stultz tx1.freq = 0;
2036035519fSJohn Stultz tx1.tick = systick;
2046035519fSJohn Stultz
2056035519fSJohn Stultz adjtimex(&tx1);
2066035519fSJohn Stultz
2076035519fSJohn Stultz if (err)
208*bc7e5d23SNathan Chancellor ksft_exit_fail();
2096035519fSJohn Stultz
210*bc7e5d23SNathan Chancellor ksft_exit_pass();
2116035519fSJohn Stultz }
212