Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/timers: Add clock skew estimation test from timetest suite

This adds my clock skew estimation test from the timetest suite.
It measures the drift between CLOCK_MONOTONIC and CLOCK_MONOTONIC_RAW
and compares it with the current frequency value from adjtimex.

It sometimes can trigger false failures when ntpd isn't in a
steady state, but its a useful too when doing adjtimex testing.

Cc: Shuah Khan <shuahkh@osg.samsung.com>
Cc: Prarit Bhargava <prarit@redhat.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Richard Cochran <richardcochran@gmail.com>
Signed-off-by: John Stultz <john.stultz@linaro.org>
Tested-by: Prarit Bhargava <prarit@redhat.com>
Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com>

authored by

John Stultz and committed by
Shuah Khan
51f91cbd c5fffcb2

+156 -1
+2 -1
tools/testing/selftests/timers/Makefile
··· 2 2 BUILD_FLAGS = -DKTEST 3 3 CFLAGS += -O3 -Wl,-no-as-needed -Wall $(BUILD_FLAGS) 4 4 LDFLAGS += -lrt -lpthread 5 - bins = posix_timers nanosleep inconsistency-check nsleep-lat 5 + bins = posix_timers nanosleep inconsistency-check nsleep-lat raw_skew 6 6 7 7 all: ${bins} 8 8 ··· 11 11 ./nanosleep 12 12 ./nsleep-lat 13 13 ./inconsistency-check 14 + ./raw_skew 14 15 clean: 15 16 rm -f ${bins}
+154
tools/testing/selftests/timers/raw_skew.c
··· 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 31 + static inline int ksft_exit_pass(void) 32 + { 33 + exit(0); 34 + } 35 + static 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 + 51 + long long llabs(long long val) 52 + { 53 + if (val < 0) 54 + val = -val; 55 + return val; 56 + } 57 + 58 + unsigned long long ts_to_nsec(struct timespec ts) 59 + { 60 + return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; 61 + } 62 + 63 + struct 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 + 72 + long 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 + 81 + void 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 + 104 + int 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 + }