Back to home page

OSCL-LXR

 
 

    


0001 /* CLOCK_MONOTONIC vs CLOCK_MONOTONIC_RAW skew test
0002  *      by: john stultz (johnstul@us.ibm.com)
0003  *          John Stultz <john.stultz@linaro.org>
0004  *      (C) Copyright IBM 2012
0005  *      (C) Copyright Linaro Limited 2015
0006  *      Licensed under the GPLv2
0007  *
0008  *  To build:
0009  *  $ gcc raw_skew.c -o raw_skew -lrt
0010  *
0011  *   This program is free software: you can redistribute it and/or modify
0012  *   it under the terms of the GNU General Public License as published by
0013  *   the Free Software Foundation, either version 2 of the License, or
0014  *   (at your option) any later version.
0015  *
0016  *   This program is distributed in the hope that it will be useful,
0017  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0018  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0019  *   GNU General Public License for more details.
0020  */
0021 
0022 #include <stdio.h>
0023 #include <unistd.h>
0024 #include <stdlib.h>
0025 #include <sys/time.h>
0026 #include <sys/timex.h>
0027 #include <time.h>
0028 #include "../kselftest.h"
0029 
0030 #define CLOCK_MONOTONIC_RAW     4
0031 #define NSEC_PER_SEC 1000000000LL
0032 
0033 #define shift_right(x, s) ({        \
0034     __typeof__(x) __x = (x);    \
0035     __typeof__(s) __s = (s);    \
0036     __x < 0 ? -(-__x >> __s) : __x >> __s; \
0037 })
0038 
0039 long long llabs(long long val)
0040 {
0041     if (val < 0)
0042         val = -val;
0043     return val;
0044 }
0045 
0046 unsigned long long ts_to_nsec(struct timespec ts)
0047 {
0048     return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
0049 }
0050 
0051 struct timespec nsec_to_ts(long long ns)
0052 {
0053     struct timespec ts;
0054 
0055     ts.tv_sec = ns/NSEC_PER_SEC;
0056     ts.tv_nsec = ns%NSEC_PER_SEC;
0057     return ts;
0058 }
0059 
0060 long long diff_timespec(struct timespec start, struct timespec end)
0061 {
0062     long long start_ns, end_ns;
0063 
0064     start_ns = ts_to_nsec(start);
0065     end_ns = ts_to_nsec(end);
0066     return end_ns - start_ns;
0067 }
0068 
0069 void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
0070 {
0071     struct timespec start, mid, end;
0072     long long diff = 0, tmp;
0073     int i;
0074 
0075     for (i = 0; i < 3; i++) {
0076         long long newdiff;
0077 
0078         clock_gettime(CLOCK_MONOTONIC, &start);
0079         clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
0080         clock_gettime(CLOCK_MONOTONIC, &end);
0081 
0082         newdiff = diff_timespec(start, end);
0083         if (diff == 0 || newdiff < diff) {
0084             diff = newdiff;
0085             *raw = mid;
0086             tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
0087             *mon = nsec_to_ts(tmp);
0088         }
0089     }
0090 }
0091 
0092 int main(int argc, char **argv)
0093 {
0094     struct timespec mon, raw, start, end;
0095     long long delta1, delta2, interval, eppm, ppm;
0096     struct timex tx1, tx2;
0097 
0098     setbuf(stdout, NULL);
0099 
0100     if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
0101         printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
0102         return -1;
0103     }
0104 
0105     tx1.modes = 0;
0106     adjtimex(&tx1);
0107     get_monotonic_and_raw(&mon, &raw);
0108     start = mon;
0109     delta1 = diff_timespec(mon, raw);
0110 
0111     if (tx1.offset)
0112         printf("WARNING: ADJ_OFFSET in progress, this will cause inaccurate results\n");
0113 
0114     printf("Estimating clock drift: ");
0115     fflush(stdout);
0116     sleep(120);
0117 
0118     get_monotonic_and_raw(&mon, &raw);
0119     end = mon;
0120     tx2.modes = 0;
0121     adjtimex(&tx2);
0122     delta2 = diff_timespec(mon, raw);
0123 
0124     interval = diff_timespec(start, end);
0125 
0126     /* calculate measured ppm between MONOTONIC and MONOTONIC_RAW */
0127     eppm = ((delta2-delta1)*NSEC_PER_SEC)/interval;
0128     eppm = -eppm;
0129     printf("%lld.%i(est)", eppm/1000, abs((int)(eppm%1000)));
0130 
0131     /* Avg the two actual freq samples adjtimex gave us */
0132     ppm = (tx1.freq + tx2.freq) * 1000 / 2;
0133     ppm = (long long)tx1.freq * 1000;
0134     ppm = shift_right(ppm, 16);
0135     printf(" %lld.%i(act)", ppm/1000, abs((int)(ppm%1000)));
0136 
0137     if (llabs(eppm - ppm) > 1000) {
0138         if (tx1.offset || tx2.offset ||
0139             tx1.freq != tx2.freq || tx1.tick != tx2.tick) {
0140             printf("    [SKIP]\n");
0141             return ksft_exit_skip("The clock was adjusted externally. Shutdown NTPd or other time sync daemons\n");
0142         }
0143         printf("    [FAILED]\n");
0144         return ksft_exit_fail();
0145     }
0146     printf("    [OK]\n");
0147     return  ksft_exit_pass();
0148 }