linux/tools/testing/selftests/timers/adjtick.c
<<
>>
Prefs
   1/* adjtimex() tick adjustment test
   2 *              by:   John Stultz <john.stultz@linaro.org>
   3 *              (C) Copyright Linaro Limited 2015
   4 *              Licensed under the GPLv2
   5 *
   6 *  To build:
   7 *      $ gcc adjtick.c -o adjtick -lrt
   8 *
   9 *   This program is free software: you can redistribute it and/or modify
  10 *   it under the terms of the GNU General Public License as published by
  11 *   the Free Software Foundation, either version 2 of the License, or
  12 *   (at your option) any later version.
  13 *
  14 *   This program is distributed in the hope that it will be useful,
  15 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 *   GNU General Public License for more details.
  18 */
  19#include <stdio.h>
  20#include <unistd.h>
  21#include <stdlib.h>
  22#include <sys/time.h>
  23#include <sys/timex.h>
  24#include <time.h>
  25
  26#ifdef KTEST
  27#include "../kselftest.h"
  28#else
  29static inline int ksft_exit_pass(void)
  30{
  31        exit(0);
  32}
  33static inline int ksft_exit_fail(void)
  34{
  35        exit(1);
  36}
  37#endif
  38
  39#define CLOCK_MONOTONIC_RAW     4
  40
  41#define NSEC_PER_SEC            1000000000LL
  42#define USEC_PER_SEC            1000000
  43
  44#define MILLION                 1000000
  45
  46long systick;
  47
  48long long llabs(long long val)
  49{
  50        if (val < 0)
  51                val = -val;
  52        return val;
  53}
  54
  55unsigned long long ts_to_nsec(struct timespec ts)
  56{
  57        return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;
  58}
  59
  60struct timespec nsec_to_ts(long long ns)
  61{
  62        struct timespec ts;
  63
  64        ts.tv_sec = ns/NSEC_PER_SEC;
  65        ts.tv_nsec = ns%NSEC_PER_SEC;
  66
  67        return ts;
  68}
  69
  70long long diff_timespec(struct timespec start, struct timespec end)
  71{
  72        long long start_ns, end_ns;
  73
  74        start_ns = ts_to_nsec(start);
  75        end_ns = ts_to_nsec(end);
  76
  77        return end_ns - start_ns;
  78}
  79
  80void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)
  81{
  82        struct timespec start, mid, end;
  83        long long diff = 0, tmp;
  84        int i;
  85
  86        clock_gettime(CLOCK_MONOTONIC, mon);
  87        clock_gettime(CLOCK_MONOTONIC_RAW, raw);
  88
  89        /* Try to get a more tightly bound pairing */
  90        for (i = 0; i < 3; i++) {
  91                long long newdiff;
  92
  93                clock_gettime(CLOCK_MONOTONIC, &start);
  94                clock_gettime(CLOCK_MONOTONIC_RAW, &mid);
  95                clock_gettime(CLOCK_MONOTONIC, &end);
  96
  97                newdiff = diff_timespec(start, end);
  98                if (diff == 0 || newdiff < diff) {
  99                        diff = newdiff;
 100                        *raw = mid;
 101                        tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;
 102                        *mon = nsec_to_ts(tmp);
 103                }
 104        }
 105}
 106
 107long long get_ppm_drift(void)
 108{
 109        struct timespec mon_start, raw_start, mon_end, raw_end;
 110        long long delta1, delta2, eppm;
 111
 112        get_monotonic_and_raw(&mon_start, &raw_start);
 113
 114        sleep(15);
 115
 116        get_monotonic_and_raw(&mon_end, &raw_end);
 117
 118        delta1 = diff_timespec(mon_start, mon_end);
 119        delta2 = diff_timespec(raw_start, raw_end);
 120
 121        eppm = (delta1*MILLION)/delta2 - MILLION;
 122
 123        return eppm;
 124}
 125
 126int check_tick_adj(long tickval)
 127{
 128        long long eppm, ppm;
 129        struct timex tx1;
 130
 131        tx1.modes        = ADJ_TICK;
 132        tx1.modes       |= ADJ_OFFSET;
 133        tx1.modes       |= ADJ_FREQUENCY;
 134        tx1.modes       |= ADJ_STATUS;
 135
 136        tx1.status      = STA_PLL;
 137        tx1.offset      = 0;
 138        tx1.freq        = 0;
 139        tx1.tick        = tickval;
 140
 141        adjtimex(&tx1);
 142
 143        sleep(1);
 144
 145        ppm = ((long long)tickval * MILLION)/systick - MILLION;
 146        printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);
 147
 148        eppm = get_ppm_drift();
 149        printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);
 150
 151        tx1.modes = 0;
 152        adjtimex(&tx1);
 153
 154        if (tx1.offset || tx1.freq || tx1.tick != tickval) {
 155                printf("        [ERROR]\n");
 156                printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");
 157                return -1;
 158        }
 159
 160        /*
 161         * Here we use 100ppm difference as an error bound.
 162         * We likely should see better, but some coarse clocksources
 163         * cannot match the HZ tick size accurately, so we have a
 164         * internal correction factor that doesn't scale exactly
 165         * with the adjustment, resulting in > 10ppm error during
 166         * a 10% adjustment. 100ppm also gives us more breathing
 167         * room for interruptions during the measurement.
 168         */
 169        if (llabs(eppm - ppm) > 100) {
 170                printf("        [FAILED]\n");
 171                return -1;
 172        }
 173        printf("        [OK]\n");
 174
 175        return  0;
 176}
 177
 178int main(int argv, char **argc)
 179{
 180        struct timespec raw;
 181        long tick, max, interval, err;
 182        struct timex tx1;
 183
 184        err = 0;
 185        setbuf(stdout, NULL);
 186
 187        if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {
 188                printf("ERR: NO CLOCK_MONOTONIC_RAW\n");
 189                return -1;
 190        }
 191
 192        printf("Each iteration takes about 15 seconds\n");
 193
 194        systick = sysconf(_SC_CLK_TCK);
 195        systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);
 196        max = systick/10; /* +/- 10% */
 197        interval = max/4; /* in 4 steps each side */
 198
 199        for (tick = (systick - max); tick < (systick + max); tick += interval) {
 200                if (check_tick_adj(tick)) {
 201                        err = 1;
 202                        break;
 203                }
 204        }
 205
 206        /* Reset things to zero */
 207        tx1.modes        = ADJ_TICK;
 208        tx1.modes       |= ADJ_OFFSET;
 209        tx1.modes       |= ADJ_FREQUENCY;
 210
 211        tx1.offset       = 0;
 212        tx1.freq         = 0;
 213        tx1.tick         = systick;
 214
 215        adjtimex(&tx1);
 216
 217        if (err)
 218                return ksft_exit_fail();
 219
 220        return ksft_exit_pass();
 221}
 222