18c2ecf20Sopenharmony_ci/* CLOCK_MONOTONIC vs CLOCK_MONOTONIC_RAW skew test 28c2ecf20Sopenharmony_ci * by: john stultz (johnstul@us.ibm.com) 38c2ecf20Sopenharmony_ci * John Stultz <john.stultz@linaro.org> 48c2ecf20Sopenharmony_ci * (C) Copyright IBM 2012 58c2ecf20Sopenharmony_ci * (C) Copyright Linaro Limited 2015 68c2ecf20Sopenharmony_ci * Licensed under the GPLv2 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * To build: 98c2ecf20Sopenharmony_ci * $ gcc raw_skew.c -o raw_skew -lrt 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This program is free software: you can redistribute it and/or modify 128c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 138c2ecf20Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 148c2ecf20Sopenharmony_ci * (at your option) any later version. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 178c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 198c2ecf20Sopenharmony_ci * GNU General Public License for more details. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <stdio.h> 238c2ecf20Sopenharmony_ci#include <unistd.h> 248c2ecf20Sopenharmony_ci#include <stdlib.h> 258c2ecf20Sopenharmony_ci#include <sys/time.h> 268c2ecf20Sopenharmony_ci#include <sys/timex.h> 278c2ecf20Sopenharmony_ci#include <time.h> 288c2ecf20Sopenharmony_ci#include "../kselftest.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define CLOCK_MONOTONIC_RAW 4 318c2ecf20Sopenharmony_ci#define NSEC_PER_SEC 1000000000LL 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define shift_right(x, s) ({ \ 348c2ecf20Sopenharmony_ci __typeof__(x) __x = (x); \ 358c2ecf20Sopenharmony_ci __typeof__(s) __s = (s); \ 368c2ecf20Sopenharmony_ci __x < 0 ? -(-__x >> __s) : __x >> __s; \ 378c2ecf20Sopenharmony_ci}) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cilong long llabs(long long val) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci if (val < 0) 428c2ecf20Sopenharmony_ci val = -val; 438c2ecf20Sopenharmony_ci return val; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciunsigned long long ts_to_nsec(struct timespec ts) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistruct timespec nsec_to_ts(long long ns) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct timespec ts; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ts.tv_sec = ns/NSEC_PER_SEC; 568c2ecf20Sopenharmony_ci ts.tv_nsec = ns%NSEC_PER_SEC; 578c2ecf20Sopenharmony_ci return ts; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cilong long diff_timespec(struct timespec start, struct timespec end) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci long long start_ns, end_ns; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci start_ns = ts_to_nsec(start); 658c2ecf20Sopenharmony_ci end_ns = ts_to_nsec(end); 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 for (i = 0; i < 3; i++) { 768c2ecf20Sopenharmony_ci long long newdiff; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &start); 798c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC_RAW, &mid); 808c2ecf20Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &end); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci newdiff = diff_timespec(start, end); 838c2ecf20Sopenharmony_ci if (diff == 0 || newdiff < diff) { 848c2ecf20Sopenharmony_ci diff = newdiff; 858c2ecf20Sopenharmony_ci *raw = mid; 868c2ecf20Sopenharmony_ci tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2; 878c2ecf20Sopenharmony_ci *mon = nsec_to_ts(tmp); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciint main(int argv, char **argc) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct timespec mon, raw, start, end; 958c2ecf20Sopenharmony_ci long long delta1, delta2, interval, eppm, ppm; 968c2ecf20Sopenharmony_ci struct timex tx1, tx2; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci setbuf(stdout, NULL); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) { 1018c2ecf20Sopenharmony_ci printf("ERR: NO CLOCK_MONOTONIC_RAW\n"); 1028c2ecf20Sopenharmony_ci return -1; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci tx1.modes = 0; 1068c2ecf20Sopenharmony_ci adjtimex(&tx1); 1078c2ecf20Sopenharmony_ci get_monotonic_and_raw(&mon, &raw); 1088c2ecf20Sopenharmony_ci start = mon; 1098c2ecf20Sopenharmony_ci delta1 = diff_timespec(mon, raw); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (tx1.offset) 1128c2ecf20Sopenharmony_ci printf("WARNING: ADJ_OFFSET in progress, this will cause inaccurate results\n"); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci printf("Estimating clock drift: "); 1158c2ecf20Sopenharmony_ci fflush(stdout); 1168c2ecf20Sopenharmony_ci sleep(120); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci get_monotonic_and_raw(&mon, &raw); 1198c2ecf20Sopenharmony_ci end = mon; 1208c2ecf20Sopenharmony_ci tx2.modes = 0; 1218c2ecf20Sopenharmony_ci adjtimex(&tx2); 1228c2ecf20Sopenharmony_ci delta2 = diff_timespec(mon, raw); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci interval = diff_timespec(start, end); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* calculate measured ppm between MONOTONIC and MONOTONIC_RAW */ 1278c2ecf20Sopenharmony_ci eppm = ((delta2-delta1)*NSEC_PER_SEC)/interval; 1288c2ecf20Sopenharmony_ci eppm = -eppm; 1298c2ecf20Sopenharmony_ci printf("%lld.%i(est)", eppm/1000, abs((int)(eppm%1000))); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Avg the two actual freq samples adjtimex gave us */ 1328c2ecf20Sopenharmony_ci ppm = (tx1.freq + tx2.freq) * 1000 / 2; 1338c2ecf20Sopenharmony_ci ppm = (long long)tx1.freq * 1000; 1348c2ecf20Sopenharmony_ci ppm = shift_right(ppm, 16); 1358c2ecf20Sopenharmony_ci printf(" %lld.%i(act)", ppm/1000, abs((int)(ppm%1000))); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (llabs(eppm - ppm) > 1000) { 1388c2ecf20Sopenharmony_ci if (tx1.offset || tx2.offset || 1398c2ecf20Sopenharmony_ci tx1.freq != tx2.freq || tx1.tick != tx2.tick) { 1408c2ecf20Sopenharmony_ci printf(" [SKIP]\n"); 1418c2ecf20Sopenharmony_ci return ksft_exit_skip("The clock was adjusted externally. Shutdown NTPd or other time sync daemons\n"); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci printf(" [FAILED]\n"); 1448c2ecf20Sopenharmony_ci return ksft_exit_fail(); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci printf(" [OK]\n"); 1478c2ecf20Sopenharmony_ci return ksft_exit_pass(); 1488c2ecf20Sopenharmony_ci} 149