18c2ecf20Sopenharmony_ci/* adjtimex() tick adjustment test 28c2ecf20Sopenharmony_ci * by: John Stultz <john.stultz@linaro.org> 38c2ecf20Sopenharmony_ci * (C) Copyright Linaro Limited 2015 48c2ecf20Sopenharmony_ci * Licensed under the GPLv2 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * To build: 78c2ecf20Sopenharmony_ci * $ gcc adjtick.c -o adjtick -lrt 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software: you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 118c2ecf20Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 128c2ecf20Sopenharmony_ci * (at your option) any later version. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 158c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 178c2ecf20Sopenharmony_ci * GNU General Public License for more details. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci#include <stdio.h> 208c2ecf20Sopenharmony_ci#include <unistd.h> 218c2ecf20Sopenharmony_ci#include <stdlib.h> 228c2ecf20Sopenharmony_ci#include <sys/time.h> 238c2ecf20Sopenharmony_ci#include <sys/timex.h> 248c2ecf20Sopenharmony_ci#include <time.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "../kselftest.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define CLOCK_MONOTONIC_RAW 4 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define NSEC_PER_SEC 1000000000LL 318c2ecf20Sopenharmony_ci#define USEC_PER_SEC 1000000 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MILLION 1000000 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cilong systick; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cilong long llabs(long long val) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci if (val < 0) 408c2ecf20Sopenharmony_ci val = -val; 418c2ecf20Sopenharmony_ci return val; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciunsigned long long ts_to_nsec(struct timespec ts) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct timespec nsec_to_ts(long long ns) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct timespec ts; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ts.tv_sec = ns/NSEC_PER_SEC; 548c2ecf20Sopenharmony_ci ts.tv_nsec = ns%NSEC_PER_SEC; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return ts; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cilong long diff_timespec(struct timespec start, struct timespec end) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci long long start_ns, end_ns; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci start_ns = ts_to_nsec(start); 648c2ecf20Sopenharmony_ci end_ns = ts_to_nsec(end); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return end_ns - start_ns; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_civoid get_monotonic_and_raw(struct timespec *mon, struct timespec *raw) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct timespec start, mid, end; 728c2ecf20Sopenharmony_ci long long diff = 0, tmp; 738c2ecf20Sopenharmony_ci int i; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, mon); 768c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, raw); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* Try to get a more tightly bound pairing */ 798c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 808c2ecf20Sopenharmony_ci long long newdiff; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &start); 838c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, &mid); 848c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &end); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci newdiff = diff_timespec(start, end); 878c2ecf20Sopenharmony_ci if (diff == 0 || newdiff < diff) { 888c2ecf20Sopenharmony_ci diff = newdiff; 898c2ecf20Sopenharmony_ci *raw = mid; 908c2ecf20Sopenharmony_ci tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2; 918c2ecf20Sopenharmony_ci *mon = nsec_to_ts(tmp); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cilong long get_ppm_drift(void) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct timespec mon_start, raw_start, mon_end, raw_end; 998c2ecf20Sopenharmony_ci long long delta1, delta2, eppm; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci get_monotonic_and_raw(&mon_start, &raw_start); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci sleep(15); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci get_monotonic_and_raw(&mon_end, &raw_end); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci delta1 = diff_timespec(mon_start, mon_end); 1088c2ecf20Sopenharmony_ci delta2 = diff_timespec(raw_start, raw_end); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci eppm = (delta1*MILLION)/delta2 - MILLION; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return eppm; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ciint check_tick_adj(long tickval) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci long long eppm, ppm; 1188c2ecf20Sopenharmony_ci struct timex tx1; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci tx1.modes = ADJ_TICK; 1218c2ecf20Sopenharmony_ci tx1.modes |= ADJ_OFFSET; 1228c2ecf20Sopenharmony_ci tx1.modes |= ADJ_FREQUENCY; 1238c2ecf20Sopenharmony_ci tx1.modes |= ADJ_STATUS; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci tx1.status = STA_PLL; 1268c2ecf20Sopenharmony_ci tx1.offset = 0; 1278c2ecf20Sopenharmony_ci tx1.freq = 0; 1288c2ecf20Sopenharmony_ci tx1.tick = tickval; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci adjtimex(&tx1); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci sleep(1); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ppm = ((long long)tickval * MILLION)/systick - MILLION; 1358c2ecf20Sopenharmony_ci printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci eppm = get_ppm_drift(); 1388c2ecf20Sopenharmony_ci printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm); 1398c2ecf20Sopenharmony_ci fflush(stdout); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci tx1.modes = 0; 1428c2ecf20Sopenharmony_ci adjtimex(&tx1); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (tx1.offset || tx1.freq || tx1.tick != tickval) { 1458c2ecf20Sopenharmony_ci printf(" [ERROR]\n"); 1468c2ecf20Sopenharmony_ci printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n"); 1478c2ecf20Sopenharmony_ci return -1; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* 1518c2ecf20Sopenharmony_ci * Here we use 100ppm difference as an error bound. 1528c2ecf20Sopenharmony_ci * We likely should see better, but some coarse clocksources 1538c2ecf20Sopenharmony_ci * cannot match the HZ tick size accurately, so we have a 1548c2ecf20Sopenharmony_ci * internal correction factor that doesn't scale exactly 1558c2ecf20Sopenharmony_ci * with the adjustment, resulting in > 10ppm error during 1568c2ecf20Sopenharmony_ci * a 10% adjustment. 100ppm also gives us more breathing 1578c2ecf20Sopenharmony_ci * room for interruptions during the measurement. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci if (llabs(eppm - ppm) > 100) { 1608c2ecf20Sopenharmony_ci printf(" [FAILED]\n"); 1618c2ecf20Sopenharmony_ci return -1; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci printf(" [OK]\n"); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return 0; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ciint main(int argv, char **argc) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct timespec raw; 1718c2ecf20Sopenharmony_ci long tick, max, interval, err; 1728c2ecf20Sopenharmony_ci struct timex tx1; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci err = 0; 1758c2ecf20Sopenharmony_ci setbuf(stdout, NULL); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) { 1788c2ecf20Sopenharmony_ci printf("ERR: NO CLOCK_MONOTONIC_RAW\n"); 1798c2ecf20Sopenharmony_ci return -1; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci printf("Each iteration takes about 15 seconds\n"); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci systick = sysconf(_SC_CLK_TCK); 1858c2ecf20Sopenharmony_ci systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK); 1868c2ecf20Sopenharmony_ci max = systick/10; /* +/- 10% */ 1878c2ecf20Sopenharmony_ci interval = max/4; /* in 4 steps each side */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci for (tick = (systick - max); tick < (systick + max); tick += interval) { 1908c2ecf20Sopenharmony_ci if (check_tick_adj(tick)) { 1918c2ecf20Sopenharmony_ci err = 1; 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Reset things to zero */ 1978c2ecf20Sopenharmony_ci tx1.modes = ADJ_TICK; 1988c2ecf20Sopenharmony_ci tx1.modes |= ADJ_OFFSET; 1998c2ecf20Sopenharmony_ci tx1.modes |= ADJ_FREQUENCY; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci tx1.offset = 0; 2028c2ecf20Sopenharmony_ci tx1.freq = 0; 2038c2ecf20Sopenharmony_ci tx1.tick = systick; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci adjtimex(&tx1); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (err) 2088c2ecf20Sopenharmony_ci return ksft_exit_fail(); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return ksft_exit_pass(); 2118c2ecf20Sopenharmony_ci} 212