1/*	$OpenBSD: t_clock_gettime.c,v 1.3 2021/12/13 16:56:48 deraadt Exp $	*/
2/* $NetBSD: t_clock_gettime.c,v 1.3 2017/01/13 21:30:41 christos Exp $ */
3
4/*-
5 * Copyright (c) 2008 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Frank Kardel.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*-
34 * Copyright (c) 2006 Frank Kardel
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 *
46 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
47 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
48 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
49 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
50 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
51 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
52 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
53 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
54 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
56 * POSSIBILITY OF SUCH DAMAGE.
57 */
58
59#include "macros.h"
60
61#include <sys/sysctl.h>
62
63
64#include "atf-c.h"
65#include <errno.h>
66#include <limits.h>
67#include <stdio.h>
68#include <stdint.h>
69#include <stdlib.h>
70#include <string.h>
71#include <time.h>
72#include <unistd.h>
73
74#include "h_macros.h"
75
76#define MINPOSDIFF	15000000	/* 15 ms for now */
77#define TIMEOUT		5
78
79#define TC_HARDWARE	"kern.timecounter.hardware"
80#define TC_CHOICE	"kern.timecounter.choice"
81
82static void
83check_timecounter(void)
84{
85	struct timespec tsa, tsb, tsl, res;
86	long long mindiff = INTMAX_MAX;
87	time_t endlimit;
88
89#define CL(x) \
90	do { \
91		if ((x) != -1) \
92			break; \
93		atf_tc_fail_nonfatal("%s: %s", #x, strerror(errno)); \
94		return; \
95	} while (0)
96
97	CL(clock_gettime(CLOCK_REALTIME, &tsa));
98	tsl = tsa;
99
100	CL(time(&endlimit));
101	endlimit += TIMEOUT + 1;
102
103	while ((time_t)tsa.tv_sec < endlimit) {
104		long long diff;
105
106		CL(clock_gettime(CLOCK_REALTIME, &tsb));
107		diff = 1000000000LL * (tsb.tv_sec - tsa.tv_sec)
108		    + tsb.tv_nsec - tsa.tv_nsec;
109
110		if (diff > 0 && mindiff > diff)
111			mindiff = diff;
112
113		if (diff < 0 || diff > MINPOSDIFF) {
114			long long elapsed;
115			(void)printf("%stime TSA: 0x%jx.%08jx, TSB: 0x%jx.%08jx, "
116			    "diff = %lld nsec, ", (diff < 0) ? "BAD " : "",
117			    (uintmax_t)tsa.tv_sec, (uintmax_t)tsa.tv_nsec,
118			    (uintmax_t)tsb.tv_sec, (uintmax_t)tsb.tv_nsec, diff);
119
120			elapsed = 1000000000LL * (tsb.tv_sec - tsl.tv_sec)
121			    + tsb.tv_nsec - tsl.tv_nsec;
122
123
124			(void)printf("%lld nsec\n", elapsed);
125			tsl = tsb;
126
127			ATF_CHECK(diff >= 0);
128			if (diff < 0)
129				return;
130		}
131
132		tsa.tv_sec = tsb.tv_sec;
133		tsa.tv_nsec = tsb.tv_nsec;
134	}
135
136	if (clock_getres(CLOCK_REALTIME, &res) == 0) {
137		long long r = res.tv_sec * 1000000000 + res.tv_nsec;
138
139		(void)printf("Claimed resolution: %lld nsec (%f Hz) or "
140		    "better\n", r, 1.0 / r * 1e9);
141		(void)printf("Observed minimum non zero delta: %lld "
142		    "nsec\n", mindiff);
143	}
144
145#undef CL
146}
147
148ATF_TC(clock_gettime_real);
149ATF_TC_HEAD(clock_gettime_real, tc)
150{
151	atf_tc_set_md_var(tc, "require.user", "root");
152	atf_tc_set_md_var(tc, "descr",
153	    "Checks the monotonicity of the CLOCK_REALTIME implementation");
154	atf_tc_set_md_var(tc, "timeout", "300");
155}
156
157ATF_TC_BODY(clock_gettime_real, tc)
158{
159	char name[128], cbuf[512], ctrbuf[10240];
160	size_t cbufsiz = sizeof(cbuf);
161	size_t ctrbufsiz = sizeof(ctrbuf);
162	const char *p;
163	char *save;
164	int quality, n;
165
166	if (sysctlbyname(TC_HARDWARE, cbuf, &cbufsiz, NULL, 0) != 0) {
167		(void)printf("\nChecking legacy time implementation "
168		    "for %d seconds\n", TIMEOUT);
169		check_timecounter();
170		return;
171		/* NOTREACHED */
172	}
173	(void)printf("%s = %s\n", TC_HARDWARE, cbuf);
174	REQUIRE_LIBC(save = strdup(cbuf), NULL);
175
176	RL(sysctlbyname(TC_CHOICE, ctrbuf, &ctrbufsiz, NULL, 0));
177	(void)printf("%s = %s\n", TC_CHOICE, ctrbuf);
178
179	for (p = ctrbuf, n = 0; sscanf(p, "%127[^(](q=%d, f=%*u Hz)%*[ ]%n",
180	    name, &quality, &n) == 2; p += n) {
181		struct timespec ts;
182		int ret;
183
184		if (quality < 0)
185			continue;
186
187		(void)printf("\nChecking %s for %d seconds\n", name, TIMEOUT);
188		CHECK_LIBC(ret = sysctlbyname(TC_HARDWARE, NULL, 0,
189		    name, strlen(name)), -1);
190		if (ret == -1)
191			continue;
192
193		/* wait a bit to select new counter in clockinterrupt */
194		ts.tv_sec = 0;
195		ts.tv_nsec = 100000000;
196		(void)nanosleep(&ts, NULL);
197
198		check_timecounter();
199	}
200
201	RL(sysctlbyname(TC_HARDWARE, NULL, 0, save, strlen(save)));
202}
203
204ATF_TP_ADD_TCS(tp)
205{
206
207	ATF_TP_ADD_TC(tp, clock_gettime_real);
208
209	return atf_no_error();
210}
211