Back to home page

OSCL-LXR

 
 

    


0001 /* adjtimex() tick adjustment test
0002  *      by:   John Stultz <john.stultz@linaro.org>
0003  *      (C) Copyright Linaro Limited 2015
0004  *      Licensed under the GPLv2
0005  *
0006  *  To build:
0007  *  $ gcc adjtick.c -o adjtick -lrt
0008  *
0009  *   This program is free software: you can redistribute it and/or modify
0010  *   it under the terms of the GNU General Public License as published by
0011  *   the Free Software Foundation, either version 2 of the License, or
0012  *   (at your option) any later version.
0013  *
0014  *   This program is distributed in the hope that it will be useful,
0015  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017  *   GNU General Public License for more details.
0018  */
0019 #include <stdio.h>
0020 #include <unistd.h>
0021 #include <stdlib.h>
0022 #include <sys/time.h>
0023 #include <sys/timex.h>
0024 #include <time.h>
0025 
0026 #include "../kselftest.h"
0027 
0028 #define CLOCK_MONOTONIC_RAW 4
0029 
0030 #define NSEC_PER_SEC        1000000000LL
0031 #define USEC_PER_SEC        1000000
0032 
0033 #define MILLION         1000000
0034 
0035 long systick;
0036 
0037 long long llabs(long long val)
0038 {
0039     if (val < 0)
0040         val = -val;
0041     return val;
0042 }
0043 
0044 unsigned long long ts_to_nsec(struct timespec ts)
0045 {
0046     return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
0047 }
0048 
0049 struct timespec nsec_to_ts(long long ns)
0050 {
0051     struct timespec ts;
0052 
0053     ts.tv_sec = ns/NSEC_PER_SEC;
0054     ts.tv_nsec = ns%NSEC_PER_SEC;
0055 
0056     return ts;
0057 }
0058 
0059 long long diff_timespec(struct timespec start, struct timespec end)
0060 {
0061     long long start_ns, end_ns;
0062 
0063     start_ns = ts_to_nsec(start);
0064     end_ns = ts_to_nsec(end);
0065 
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     clock_gettime(CLOCK_MONOTONIC, mon);
0076     clock_gettime(CLOCK_MONOTONIC_RAW, raw);
0077 
0078     /* Try to get a more tightly bound pairing */
0079     for (i = 0; i < 3; i++) {
0080         long long newdiff;
0081 
0082         clock_gettime(CLOCK_MONOTONIC, &start);
0083         clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
0084         clock_gettime(CLOCK_MONOTONIC, &end);
0085 
0086         newdiff = diff_timespec(start, end);
0087         if (diff == 0 || newdiff < diff) {
0088             diff = newdiff;
0089             *raw = mid;
0090             tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
0091             *mon = nsec_to_ts(tmp);
0092         }
0093     }
0094 }
0095 
0096 long long get_ppm_drift(void)
0097 {
0098     struct timespec mon_start, raw_start, mon_end, raw_end;
0099     long long delta1, delta2, eppm;
0100 
0101     get_monotonic_and_raw(&mon_start, &raw_start);
0102 
0103     sleep(15);
0104 
0105     get_monotonic_and_raw(&mon_end, &raw_end);
0106 
0107     delta1 = diff_timespec(mon_start, mon_end);
0108     delta2 = diff_timespec(raw_start, raw_end);
0109 
0110     eppm = (delta1*MILLION)/delta2 - MILLION;
0111 
0112     return eppm;
0113 }
0114 
0115 int check_tick_adj(long tickval)
0116 {
0117     long long eppm, ppm;
0118     struct timex tx1;
0119 
0120     tx1.modes    = ADJ_TICK;
0121     tx1.modes   |= ADJ_OFFSET;
0122     tx1.modes   |= ADJ_FREQUENCY;
0123     tx1.modes   |= ADJ_STATUS;
0124 
0125     tx1.status  = STA_PLL;
0126     tx1.offset  = 0;
0127     tx1.freq    = 0;
0128     tx1.tick    = tickval;
0129 
0130     adjtimex(&tx1);
0131 
0132     sleep(1);
0133 
0134     ppm = ((long long)tickval * MILLION)/systick - MILLION;
0135     printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);
0136 
0137     eppm = get_ppm_drift();
0138     printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
0139     fflush(stdout);
0140 
0141     tx1.modes = 0;
0142     adjtimex(&tx1);
0143 
0144     if (tx1.offset || tx1.freq || tx1.tick != tickval) {
0145         printf("    [ERROR]\n");
0146         printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");
0147         return -1;
0148     }
0149 
0150     /*
0151      * Here we use 100ppm difference as an error bound.
0152      * We likely should see better, but some coarse clocksources
0153      * cannot match the HZ tick size accurately, so we have a
0154      * internal correction factor that doesn't scale exactly
0155      * with the adjustment, resulting in > 10ppm error during
0156      * a 10% adjustment. 100ppm also gives us more breathing
0157      * room for interruptions during the measurement.
0158      */
0159     if (llabs(eppm - ppm) > 100) {
0160         printf("    [FAILED]\n");
0161         return -1;
0162     }
0163     printf("    [OK]\n");
0164 
0165     return  0;
0166 }
0167 
0168 int main(int argc, char **argv)
0169 {
0170     struct timespec raw;
0171     long tick, max, interval, err;
0172     struct timex tx1;
0173 
0174     err = 0;
0175     setbuf(stdout, NULL);
0176 
0177     if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
0178         printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
0179         return -1;
0180     }
0181 
0182     printf("Each iteration takes about 15 seconds\n");
0183 
0184     systick = sysconf(_SC_CLK_TCK);
0185     systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);
0186     max = systick/10; /* +/- 10% */
0187     interval = max/4; /* in 4 steps each side */
0188 
0189     for (tick = (systick - max); tick < (systick + max); tick += interval) {
0190         if (check_tick_adj(tick)) {
0191             err = 1;
0192             break;
0193         }
0194     }
0195 
0196     /* Reset things to zero */
0197     tx1.modes    = ADJ_TICK;
0198     tx1.modes   |= ADJ_OFFSET;
0199     tx1.modes   |= ADJ_FREQUENCY;
0200 
0201     tx1.offset   = 0;
0202     tx1.freq     = 0;
0203     tx1.tick     = systick;
0204 
0205     adjtimex(&tx1);
0206 
0207     if (err)
0208         return ksft_exit_fail();
0209 
0210     return ksft_exit_pass();
0211 }