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