1 /*
2  * Copyright the NTPsec project contributors
3  * SPDX-License-Identifier: BSD-2-Clause
4  */
5 
6 #include "config.h"
7 
8 #include <stdio.h>
9 #include <stdbool.h>
10 
11 #include "ntp_types.h"
12 #include "ntp_calendar.h"
13 #include "ntpfrob.h"
14 
15 #define	DEFAULT_SYS_PRECISION	-99
16 
17 int default_get_resolution(void);
18 int default_get_precision(void);
19 
precision(const iomode mode)20 void precision(const iomode mode)
21 {
22 	if (mode == json) {
23 		printf("{\"log2 of resolution\":%d, \"log2 of precision\":%d}\n",
24 		       default_get_resolution(),
25 		       default_get_precision());
26 	} else {
27 		printf("log2(resolution) = %d, log2(precision) = %d\n",
28 			default_get_resolution(),
29 			default_get_precision());
30 	}
31 }
32 
33 /* Find the resolution of the system clock by watching how the current time
34  * changes as we read it repeatedly.
35  *
36  * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
37  * probably use the "unused" low order bits as a counter (to force time to be
38  * a strictly increaing variable), incrementing it each time any process
39  * requests the time [[ or maybe time will stand still ? ]].
40  *
41  * SO: the logic goes:
42  *
43  *      IF      the difference from the last time is "small" (< MINSTEP)
44  *      THEN    this machine is "counting" with the low order bits
45  *      ELIF    this is not the first time round the loop
46  *      THEN    this machine *WAS* counting, and has now stepped
47  *      ELSE    this machine has resolution < time to read clock
48  *
49  * SO: if it exits on the first loop, assume "full accuracy" (1us)
50  *     otherwise, take the log2(observed difference, rounded UP)
51  *
52  * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
53  * and the first loop, it doesn't stop too early.
54  * Making it even greater allows MINSTEP to be reduced, assuming that the
55  * chance of MINSTEP-1 other processes getting in and calling clock_gettime()
56  * between this processes's calls.
57  * Reducing MINSTEP may be necessary as this sets an upper bound for the time
58  * to actually call clock_gettime().
59  */
60 
61 #define	DNSECS	1000000000L
62 #define	HUSECS	(1024 * 1024)
63 #define	MINSTEP	200	/* assume no system returns less than 200 nansec */
64 /* Don't use "1" as some *other* process may read too */
65 /* We assume no system actually *ANSWERS* in this time */
66 #define MAXSTEP 20000000   /* maximum clock increment (ns) */
67 #define MINLOOPS 5      /* minimum number of step samples */
68 #define	MAXLOOPS (HUSECS * 1024)	/* Assume precision < .1s ! */
69 
70 int
default_get_resolution(void)71 default_get_resolution(void)
72 {
73 	struct timespec tp;
74 	long last;
75 	int i;
76 	long diff;
77 	long val;
78 	int minsteps = MINLOOPS;	/* need at least this many steps */
79 
80 	clock_gettime(CLOCK_REALTIME, &tp);
81 	last = tp.tv_nsec;
82 	for (i = - --minsteps; i< MAXLOOPS; i++) {
83 		clock_gettime(CLOCK_REALTIME, &tp);
84 		diff = tp.tv_nsec - last;
85 		if (diff < 0) { diff += DNSECS;
86 		}
87 		if (diff > MINSTEP && minsteps-- <= 0) {
88 			break;
89 		}
90 		last = tp.tv_nsec;
91 	}
92 	diff /= 1000;	/* step down to milliseconds */
93 
94 	fprintf(stderr, "resolution = %ld usec after %d loop%s\n",
95 		diff, i, (i==1) ? "" : "s");
96 
97 	diff = (diff *3)/2;
98 	if (i >= MAXLOOPS) {
99 		fprintf(stderr,
100 			"     (Boy this machine is fast ! %d loops without a step)\n",
101 			MAXLOOPS);
102 		diff = 1; /* No STEP, so FAST machine */
103 	}
104 	if (i == 0) {
105 		fprintf(stderr,
106 			"     (The resolution is less than the time to read the clock -- Assume 1us)\n");
107 		diff = 1; /* time to read clock >= resolution */
108 	}
109 	for (i=0, val=HUSECS; val>0; i--, val >>= 1) { if (diff >= val) {
110 			return i;
111 		}
112 	}
113 	fprintf(stderr,
114 		"     (Oh dear -- that wasn't expected ! I'll guess !)\n");
115 	return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
116 }
117 
118 /* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
119 
120 /*
121  * This routine calculates the differences between successive calls to
122  * clock_gettime(REALTIME). If a difference is less than zero, the ns field
123  * has rolled over to the next second, so we add a second in ns. If
124  * the difference is greater than zero and less than MINSTEP, the
125  * clock has been advanced by a small amount to avoid standing still.
126  * If the clock has advanced by a greater amount, then a timer interrupt
127  * has occurred and this amount represents the precision of the clock.
128  * In order to guard against spurious values, which could occur if we
129  * happen to hit a fat interrupt, we do this for MINLOOPS times and
130  * keep the minimum value obtained.
131  */
132 int
default_get_precision(void)133 default_get_precision(void)
134 {
135 	struct timespec tp;
136 	long last;
137 	int i;
138 	long diff;
139 	long val;
140 	long nsec;
141 
142 	nsec = 0;
143 	val = MAXSTEP;
144 	clock_gettime(CLOCK_REALTIME, &tp);
145 	last = tp.tv_nsec;
146 	for (i = 0; i < MINLOOPS && nsec < HUSECS * 1024;) {
147 	    clock_gettime(CLOCK_REALTIME, &tp);
148 		diff = tp.tv_nsec - last;
149 		last = tp.tv_nsec;
150 		if (diff < 0) {
151 		    diff += DNSECS;
152 		}
153 		nsec += diff;
154 		if (diff > MINSTEP) {
155 			i++;
156 			if (diff < val) {
157 			    val = diff;
158 			}
159 		}
160 	}
161 	val /= 1000;	/* step down to milliseconds */
162 	fprintf(stderr, "precision  = %ld usec after %d loop%s\n",
163 	       val, i, (i == 1) ? "" : "s");
164 	if (nsec >= HUSECS * 1024) {
165 	    fprintf(stderr, "     (Boy this machine is fast! nsec was %ld)\n",
166 		       nsec);
167 		val = MINSTEP;	/* val <= MINSTEP; fast machine */
168 	}
169 	diff = HUSECS;
170 	for (i = 0; diff > val; i--) {
171 	    diff >>= 1;
172 	}
173 	return (i);
174 }
175 
176 /* end */
177