1/* CLOCK_MONOTONIC vs CLOCK_MONOTONIC_RAW skew test 2 * by: john stultz (johnstul@us.ibm.com) 3 * John Stultz <john.stultz@linaro.org> 4 * (C) Copyright IBM 2012 5 * (C) Copyright Linaro Limited 2015 6 * Licensed under the GPLv2 7 * 8 * To build: 9 * $ gcc raw_skew.c -o raw_skew -lrt 10 * 11 * This program is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License as published by 13 * the Free Software Foundation, either version 2 of the License, or 14 * (at your option) any later version. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 */ 21 22#include <stdio.h> 23#include <unistd.h> 24#include <stdlib.h> 25#include <sys/time.h> 26#include <sys/timex.h> 27#include <time.h> 28#ifdef KTEST 29#include "../kselftest.h" 30#else 31static inline int ksft_exit_pass(void) 32{ 33 exit(0); 34} 35static inline int ksft_exit_fail(void) 36{ 37 exit(1); 38} 39#endif 40 41 42#define CLOCK_MONOTONIC_RAW 4 43#define NSEC_PER_SEC 1000000000LL 44 45#define shift_right(x, s) ({ \ 46 __typeof__(x) __x = (x); \ 47 __typeof__(s) __s = (s); \ 48 __x < 0 ? -(-__x >> __s) : __x >> __s; \ 49}) 50 51long long llabs(long long val) 52{ 53 if (val < 0) 54 val = -val; 55 return val; 56} 57 58unsigned long long ts_to_nsec(struct timespec ts) 59{ 60 return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; 61} 62 63struct timespec nsec_to_ts(long long ns) 64{ 65 struct timespec ts; 66 67 ts.tv_sec = ns/NSEC_PER_SEC; 68 ts.tv_nsec = ns%NSEC_PER_SEC; 69 return ts; 70} 71 72long long diff_timespec(struct timespec start, struct timespec end) 73{ 74 long long start_ns, end_ns; 75 76 start_ns = ts_to_nsec(start); 77 end_ns = ts_to_nsec(end); 78 return end_ns - start_ns; 79} 80 81void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw) 82{ 83 struct timespec start, mid, end; 84 long long diff = 0, tmp; 85 int i; 86 87 for (i = 0; i < 3; i++) { 88 long long newdiff; 89 90 clock_gettime(CLOCK_MONOTONIC, &start); 91 clock_gettime(CLOCK_MONOTONIC_RAW, &mid); 92 clock_gettime(CLOCK_MONOTONIC, &end); 93 94 newdiff = diff_timespec(start, end); 95 if (diff == 0 || newdiff < diff) { 96 diff = newdiff; 97 *raw = mid; 98 tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2; 99 *mon = nsec_to_ts(tmp); 100 } 101 } 102} 103 104int main(int argv, char **argc) 105{ 106 struct timespec mon, raw, start, end; 107 long long delta1, delta2, interval, eppm, ppm; 108 struct timex tx1, tx2; 109 110 setbuf(stdout, NULL); 111 112 if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) { 113 printf("ERR: NO CLOCK_MONOTONIC_RAW\n"); 114 return -1; 115 } 116 117 tx1.modes = 0; 118 adjtimex(&tx1); 119 get_monotonic_and_raw(&mon, &raw); 120 start = mon; 121 delta1 = diff_timespec(mon, raw); 122 123 if (tx1.offset) 124 printf("WARNING: ADJ_OFFSET in progress, this will cause inaccurate results\n"); 125 126 printf("Estimating clock drift: "); 127 sleep(120); 128 129 get_monotonic_and_raw(&mon, &raw); 130 end = mon; 131 tx2.modes = 0; 132 adjtimex(&tx2); 133 delta2 = diff_timespec(mon, raw); 134 135 interval = diff_timespec(start, end); 136 137 /* calculate measured ppm between MONOTONIC and MONOTONIC_RAW */ 138 eppm = ((delta2-delta1)*NSEC_PER_SEC)/interval; 139 eppm = -eppm; 140 printf("%lld.%i(est)", eppm/1000, abs((int)(eppm%1000))); 141 142 /* Avg the two actual freq samples adjtimex gave us */ 143 ppm = (tx1.freq + tx2.freq) * 1000 / 2; 144 ppm = (long long)tx1.freq * 1000; 145 ppm = shift_right(ppm, 16); 146 printf(" %lld.%i(act)", ppm/1000, abs((int)(ppm%1000))); 147 148 if (llabs(eppm - ppm) > 1000) { 149 printf(" [FAILED]\n"); 150 return ksft_exit_fail(); 151 } 152 printf(" [OK]\n"); 153 return ksft_exit_pass(); 154} 155