brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · 205b76a Raw
212 lines · c
1/* adjtimex() tick adjustment test2 *		by:   John Stultz <john.stultz@linaro.org>3 *		(C) Copyright Linaro Limited 20154 *		Licensed under the GPLv25 *6 *  To build:7 *	$ gcc adjtick.c -o adjtick -lrt8 *9 *   This program is free software: you can redistribute it and/or modify10 *   it under the terms of the GNU General Public License as published by11 *   the Free Software Foundation, either version 2 of the License, or12 *   (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 of16 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the17 *   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#include "../kselftest.h"27 28#define CLOCK_MONOTONIC_RAW	429 30#define NSEC_PER_SEC		1000000000LL31#define USEC_PER_SEC		100000032 33#define MILLION			100000034 35long systick;36 37long long llabs(long long val)38{39	if (val < 0)40		val = -val;41	return val;42}43 44unsigned long long ts_to_nsec(struct timespec ts)45{46	return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec;47}48 49struct timespec nsec_to_ts(long long ns)50{51	struct timespec ts;52 53	ts.tv_sec = ns/NSEC_PER_SEC;54	ts.tv_nsec = ns%NSEC_PER_SEC;55 56	return ts;57}58 59long long diff_timespec(struct timespec start, struct timespec end)60{61	long long start_ns, end_ns;62 63	start_ns = ts_to_nsec(start);64	end_ns = ts_to_nsec(end);65 66	return end_ns - start_ns;67}68 69void get_monotonic_and_raw(struct timespec *mon, struct timespec *raw)70{71	struct timespec start, mid, end;72	long long diff = 0, tmp;73	int i;74 75	clock_gettime(CLOCK_MONOTONIC, mon);76	clock_gettime(CLOCK_MONOTONIC_RAW, raw);77 78	/* Try to get a more tightly bound pairing */79	for (i = 0; i < 3; i++) {80		long long newdiff;81 82		clock_gettime(CLOCK_MONOTONIC, &start);83		clock_gettime(CLOCK_MONOTONIC_RAW, &mid);84		clock_gettime(CLOCK_MONOTONIC, &end);85 86		newdiff = diff_timespec(start, end);87		if (diff == 0 || newdiff < diff) {88			diff = newdiff;89			*raw = mid;90			tmp = (ts_to_nsec(start) + ts_to_nsec(end))/2;91			*mon = nsec_to_ts(tmp);92		}93	}94}95 96long long get_ppm_drift(void)97{98	struct timespec mon_start, raw_start, mon_end, raw_end;99	long long delta1, delta2, eppm;100 101	get_monotonic_and_raw(&mon_start, &raw_start);102 103	sleep(15);104 105	get_monotonic_and_raw(&mon_end, &raw_end);106 107	delta1 = diff_timespec(mon_start, mon_end);108	delta2 = diff_timespec(raw_start, raw_end);109 110	eppm = (delta1*MILLION)/delta2 - MILLION;111 112	return eppm;113}114 115int check_tick_adj(long tickval)116{117	long long eppm, ppm;118	struct timex tx1;119 120	tx1.modes	 = ADJ_TICK;121	tx1.modes	|= ADJ_OFFSET;122	tx1.modes	|= ADJ_FREQUENCY;123	tx1.modes	|= ADJ_STATUS;124 125	tx1.status	= STA_PLL;126	tx1.offset	= 0;127	tx1.freq	= 0;128	tx1.tick	= tickval;129 130	adjtimex(&tx1);131 132	sleep(1);133 134	ppm = ((long long)tickval * MILLION)/systick - MILLION;135	printf("Estimating tick (act: %ld usec, %lld ppm): ", tickval, ppm);136 137	eppm = get_ppm_drift();138	printf("%lld usec, %lld ppm", systick + (systick * eppm / MILLION), eppm);139	fflush(stdout);140 141	tx1.modes = 0;142	adjtimex(&tx1);143 144	if (tx1.offset || tx1.freq || tx1.tick != tickval) {145		printf("	[ERROR]\n");146		printf("\tUnexpected adjtimex return values, make sure ntpd is not running.\n");147		return -1;148	}149 150	/*151	 * Here we use 100ppm difference as an error bound.152	 * We likely should see better, but some coarse clocksources153	 * cannot match the HZ tick size accurately, so we have a154	 * internal correction factor that doesn't scale exactly155	 * with the adjustment, resulting in > 10ppm error during156	 * a 10% adjustment. 100ppm also gives us more breathing157	 * room for interruptions during the measurement.158	 */159	if (llabs(eppm - ppm) > 100) {160		printf("	[FAILED]\n");161		return -1;162	}163	printf("	[OK]\n");164 165	return  0;166}167 168int main(int argc, char **argv)169{170	struct timespec raw;171	long tick, max, interval, err;172	struct timex tx1;173 174	err = 0;175	setbuf(stdout, NULL);176 177	if (clock_gettime(CLOCK_MONOTONIC_RAW, &raw)) {178		printf("ERR: NO CLOCK_MONOTONIC_RAW\n");179		return -1;180	}181 182	printf("Each iteration takes about 15 seconds\n");183 184	systick = sysconf(_SC_CLK_TCK);185	systick = USEC_PER_SEC/sysconf(_SC_CLK_TCK);186	max = systick/10; /* +/- 10% */187	interval = max/4; /* in 4 steps each side */188 189	for (tick = (systick - max); tick < (systick + max); tick += interval) {190		if (check_tick_adj(tick)) {191			err = 1;192			break;193		}194	}195 196	/* Reset things to zero */197	tx1.modes	 = ADJ_TICK;198	tx1.modes	|= ADJ_OFFSET;199	tx1.modes	|= ADJ_FREQUENCY;200 201	tx1.offset	 = 0;202	tx1.freq	 = 0;203	tx1.tick	 = systick;204 205	adjtimex(&tx1);206 207	if (err)208		ksft_exit_fail();209 210	ksft_exit_pass();211}212