162306a36Sopenharmony_ci/* adjtimex() tick adjustment test 262306a36Sopenharmony_ci * by: John Stultz <john.stultz@linaro.org> 362306a36Sopenharmony_ci * (C) Copyright Linaro Limited 2015 462306a36Sopenharmony_ci * Licensed under the GPLv2 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * To build: 762306a36Sopenharmony_ci * $ gcc adjtick.c -o adjtick -lrt 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is free software: you can redistribute it and/or modify 1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1162306a36Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 1262306a36Sopenharmony_ci * (at your option) any later version. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1562306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1662306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1762306a36Sopenharmony_ci * GNU General Public License for more details. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#include <stdio.h> 2062306a36Sopenharmony_ci#include <unistd.h> 2162306a36Sopenharmony_ci#include <stdlib.h> 2262306a36Sopenharmony_ci#include <sys/time.h> 2362306a36Sopenharmony_ci#include <sys/timex.h> 2462306a36Sopenharmony_ci#include <time.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "../kselftest.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define CLOCK_MONOTONIC_RAW 4 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define NSEC_PER_SEC 1000000000LL 3162306a36Sopenharmony_ci#define USEC_PER_SEC 1000000 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define MILLION 1000000 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cilong systick; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cilong long llabs(long long val) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if (val < 0) 4062306a36Sopenharmony_ci val = -val; 4162306a36Sopenharmony_ci return val; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciunsigned long long ts_to_nsec(struct timespec ts) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct timespec nsec_to_ts(long long ns) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct timespec ts; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ts.tv_sec = ns/NSEC_PER_SEC; 5462306a36Sopenharmony_ci ts.tv_nsec = ns%NSEC_PER_SEC; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return ts; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cilong long diff_timespec(struct timespec start, struct timespec end) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci long long start_ns, end_ns; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci start_ns = ts_to_nsec(start); 6462306a36Sopenharmony_ci end_ns = ts_to_nsec(end); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return end_ns - start_ns; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_civoid get_monotonic_and_raw(struct timespec *mon, struct timespec *raw) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci struct timespec start, mid, end; 7262306a36Sopenharmony_ci long long diff = 0, tmp; 7362306a36Sopenharmony_ci int i; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, mon); 7662306a36Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, raw); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* Try to get a more tightly bound pairing */ 7962306a36Sopenharmony_ci for (i = 0; i < 3; i++) { 8062306a36Sopenharmony_ci long long newdiff; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &start); 8362306a36Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, &mid); 8462306a36Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &end); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci newdiff = diff_timespec(start, end); 8762306a36Sopenharmony_ci if (diff == 0 || newdiff < diff) { 8862306a36Sopenharmony_ci diff = newdiff; 8962306a36Sopenharmony_ci *raw = mid; 9062306a36Sopenharmony_ci tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2; 9162306a36Sopenharmony_ci *mon = nsec_to_ts(tmp); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cilong long get_ppm_drift(void) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct timespec mon_start, raw_start, mon_end, raw_end; 9962306a36Sopenharmony_ci long long delta1, delta2, eppm; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci get_monotonic_and_raw(&mon_start, &raw_start); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci sleep(15); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci get_monotonic_and_raw(&mon_end, &raw_end); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci delta1 = diff_timespec(mon_start, mon_end); 10862306a36Sopenharmony_ci delta2 = diff_timespec(raw_start, raw_end); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci eppm = (delta1*MILLION)/delta2 - MILLION; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return eppm; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint check_tick_adj(long tickval) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci long long eppm, ppm; 11862306a36Sopenharmony_ci struct timex tx1; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci tx1.modes = ADJ_TICK; 12162306a36Sopenharmony_ci tx1.modes |= ADJ_OFFSET; 12262306a36Sopenharmony_ci tx1.modes |= ADJ_FREQUENCY; 12362306a36Sopenharmony_ci tx1.modes |= ADJ_STATUS; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci tx1.status = STA_PLL; 12662306a36Sopenharmony_ci tx1.offset = 0; 12762306a36Sopenharmony_ci tx1.freq = 0; 12862306a36Sopenharmony_ci tx1.tick = tickval; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci adjtimex(&tx1); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci sleep(1); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ppm = ((long long)tickval * MILLION)/systick - MILLION; 13562306a36Sopenharmony_ci printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci eppm = get_ppm_drift(); 13862306a36Sopenharmony_ci printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm); 13962306a36Sopenharmony_ci fflush(stdout); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci tx1.modes = 0; 14262306a36Sopenharmony_ci adjtimex(&tx1); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (tx1.offset || tx1.freq || tx1.tick != tickval) { 14562306a36Sopenharmony_ci printf(" [ERROR]\n"); 14662306a36Sopenharmony_ci printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n"); 14762306a36Sopenharmony_ci return -1; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Here we use 100ppm difference as an error bound. 15262306a36Sopenharmony_ci * We likely should see better, but some coarse clocksources 15362306a36Sopenharmony_ci * cannot match the HZ tick size accurately, so we have a 15462306a36Sopenharmony_ci * internal correction factor that doesn't scale exactly 15562306a36Sopenharmony_ci * with the adjustment, resulting in > 10ppm error during 15662306a36Sopenharmony_ci * a 10% adjustment. 100ppm also gives us more breathing 15762306a36Sopenharmony_ci * room for interruptions during the measurement. 15862306a36Sopenharmony_ci */ 15962306a36Sopenharmony_ci if (llabs(eppm - ppm) > 100) { 16062306a36Sopenharmony_ci printf(" [FAILED]\n"); 16162306a36Sopenharmony_ci return -1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci printf(" [OK]\n"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint main(int argc, char **argv) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct timespec raw; 17162306a36Sopenharmony_ci long tick, max, interval, err; 17262306a36Sopenharmony_ci struct timex tx1; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci err = 0; 17562306a36Sopenharmony_ci setbuf(stdout, NULL); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) { 17862306a36Sopenharmony_ci printf("ERR: NO CLOCK_MONOTONIC_RAW\n"); 17962306a36Sopenharmony_ci return -1; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci printf("Each iteration takes about 15 seconds\n"); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci systick = sysconf(_SC_CLK_TCK); 18562306a36Sopenharmony_ci systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK); 18662306a36Sopenharmony_ci max = systick/10; /* +/- 10% */ 18762306a36Sopenharmony_ci interval = max/4; /* in 4 steps each side */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci for (tick = (systick - max); tick < (systick + max); tick += interval) { 19062306a36Sopenharmony_ci if (check_tick_adj(tick)) { 19162306a36Sopenharmony_ci err = 1; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Reset things to zero */ 19762306a36Sopenharmony_ci tx1.modes = ADJ_TICK; 19862306a36Sopenharmony_ci tx1.modes |= ADJ_OFFSET; 19962306a36Sopenharmony_ci tx1.modes |= ADJ_FREQUENCY; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci tx1.offset = 0; 20262306a36Sopenharmony_ci tx1.freq = 0; 20362306a36Sopenharmony_ci tx1.tick = systick; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci adjtimex(&tx1); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (err) 20862306a36Sopenharmony_ci return ksft_exit_fail(); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return ksft_exit_pass(); 21162306a36Sopenharmony_ci} 212