212 lines · c
1/* adjtimex() tick adjustment test2 * by: John Stultz <john.stultz@linaro.org>3 * (C) Copyright Linaro Limited 20154 * Licensed under the GPLv25 *6 * To build:7 * $ gcc adjtick.c -o adjtick -lrt8 *9 * This program is free software: you can redistribute it and/or modify10 * it under the terms of the GNU General Public License as published by11 * the Free Software Foundation, either version 2 of the License, or12 * (at your option) any later version.13 *14 * This program is distributed in the hope that it will be useful,15 * but WITHOUT ANY WARRANTY; without even the implied warranty of16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17 * GNU General Public License for more details.18 */19#include <stdio.h>20#include <unistd.h>21#include <stdlib.h>22#include <sys/time.h>23#include <sys/timex.h>24#include <time.h>25 26#include "../kselftest.h"27 28#define CLOCK_MONOTONIC_RAW 429 30#define NSEC_PER_SEC 1000000000LL31#define USEC_PER_SEC 100000032 33#define MILLION 100000034 35long systick;36 37long long llabs(long long val)38{39 if (val < 0)40 val = -val;41 return val;42}43 44unsigned long long ts_to_nsec(struct timespec ts)45{46 return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;47}48 49struct timespec nsec_to_ts(long long ns)50{51 struct timespec ts;52 53 ts.tv_sec = ns/NSEC_PER_SEC;54 ts.tv_nsec = ns%NSEC_PER_SEC;55 56 return ts;57}58 59long long diff_timespec(struct timespec start, struct timespec end)60{61 long long start_ns, end_ns;62 63 start_ns = ts_to_nsec(start);64 end_ns = ts_to_nsec(end);65 66 return end_ns - start_ns;67}68 69void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)70{71 struct timespec start, mid, end;72 long long diff = 0, tmp;73 int i;74 75 clock_gettime(CLOCK_MONOTONIC, mon);76 clock_gettime(CLOCK_MONOTONIC_RAW, raw);77 78 /* Try to get a more tightly bound pairing */79 for (i = 0; i < 3; i++) {80 long long newdiff;81 82 clock_gettime(CLOCK_MONOTONIC, &start);83 clock_gettime(CLOCK_MONOTONIC_RAW, &mid);84 clock_gettime(CLOCK_MONOTONIC, &end);85 86 newdiff = diff_timespec(start, end);87 if (diff == 0 || newdiff < diff) {88 diff = newdiff;89 *raw = mid;90 tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;91 *mon = nsec_to_ts(tmp);92 }93 }94}95 96long long get_ppm_drift(void)97{98 struct timespec mon_start, raw_start, mon_end, raw_end;99 long long delta1, delta2, eppm;100 101 get_monotonic_and_raw(&mon_start, &raw_start);102 103 sleep(15);104 105 get_monotonic_and_raw(&mon_end, &raw_end);106 107 delta1 = diff_timespec(mon_start, mon_end);108 delta2 = diff_timespec(raw_start, raw_end);109 110 eppm = (delta1*MILLION)/delta2 - MILLION;111 112 return eppm;113}114 115int check_tick_adj(long tickval)116{117 long long eppm, ppm;118 struct timex tx1;119 120 tx1.modes = ADJ_TICK;121 tx1.modes |= ADJ_OFFSET;122 tx1.modes |= ADJ_FREQUENCY;123 tx1.modes |= ADJ_STATUS;124 125 tx1.status = STA_PLL;126 tx1.offset = 0;127 tx1.freq = 0;128 tx1.tick = tickval;129 130 adjtimex(&tx1);131 132 sleep(1);133 134 ppm = ((long long)tickval * MILLION)/systick - MILLION;135 printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);136 137 eppm = get_ppm_drift();138 printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);139 fflush(stdout);140 141 tx1.modes = 0;142 adjtimex(&tx1);143 144 if (tx1.offset || tx1.freq || tx1.tick != tickval) {145 printf(" [ERROR]\n");146 printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");147 return -1;148 }149 150 /*151 * Here we use 100ppm difference as an error bound.152 * We likely should see better, but some coarse clocksources153 * cannot match the HZ tick size accurately, so we have a154 * internal correction factor that doesn't scale exactly155 * with the adjustment, resulting in > 10ppm error during156 * a 10% adjustment. 100ppm also gives us more breathing157 * room for interruptions during the measurement.158 */159 if (llabs(eppm - ppm) > 100) {160 printf(" [FAILED]\n");161 return -1;162 }163 printf(" [OK]\n");164 165 return 0;166}167 168int main(int argc, char **argv)169{170 struct timespec raw;171 long tick, max, interval, err;172 struct timex tx1;173 174 err = 0;175 setbuf(stdout, NULL);176 177 if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {178 printf("ERR: NO CLOCK_MONOTONIC_RAW\n");179 return -1;180 }181 182 printf("Each iteration takes about 15 seconds\n");183 184 systick = sysconf(_SC_CLK_TCK);185 systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);186 max = systick/10; /* +/- 10% */187 interval = max/4; /* in 4 steps each side */188 189 for (tick = (systick - max); tick < (systick + max); tick += interval) {190 if (check_tick_adj(tick)) {191 err = 1;192 break;193 }194 }195 196 /* Reset things to zero */197 tx1.modes = ADJ_TICK;198 tx1.modes |= ADJ_OFFSET;199 tx1.modes |= ADJ_FREQUENCY;200 201 tx1.offset = 0;202 tx1.freq = 0;203 tx1.tick = systick;204 205 adjtimex(&tx1);206 207 if (err)208 ksft_exit_fail();209 210 ksft_exit_pass();211}212