]> git.ipfire.org Git - thirdparty/chrony.git/blame - sys_generic.c
cmdmon: save NTS cookies and server keys on dump command
[thirdparty/chrony.git] / sys_generic.c
CommitLineData
fc235a3f
ML
1/*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
33967780 5 * Copyright (C) Miroslav Lichvar 2014-2015
fc235a3f
ML
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 **********************************************************************
21
22 =======================================================================
23
24 Generic driver functions to complete system-specific drivers
25 */
26
27#include "config.h"
28
29#include "sysincl.h"
30
31#include "sys_generic.h"
32
9cf78b97 33#include "conf.h"
fc235a3f
ML
34#include "local.h"
35#include "localp.h"
36#include "logging.h"
400820d3 37#include "privops.h"
fc235a3f
ML
38#include "sched.h"
39#include "util.h"
40
41/* ================================================== */
42
02cbe5e1 43/* System clock drivers */
fc235a3f
ML
44static lcl_ReadFrequencyDriver drv_read_freq;
45static lcl_SetFrequencyDriver drv_set_freq;
02cbe5e1 46static lcl_SetSyncStatusDriver drv_set_sync_status;
d6fdae5f
ML
47static lcl_AccrueOffsetDriver drv_accrue_offset;
48static lcl_OffsetCorrectionDriver drv_get_offset_correction;
fc235a3f
ML
49
50/* Current frequency as requested by the local module (in ppm) */
51static double base_freq;
52
53/* Maximum frequency that can be set by drv_set_freq (in ppm) */
54static double max_freq;
55
56/* Maximum expected delay in the actual frequency change (e.g. kernel ticks)
57 in local time */
58static double max_freq_change_delay;
59
9cf78b97
ML
60/* Maximum allowed frequency offset relative to the base frequency */
61static double max_corr_freq;
62
fc235a3f
ML
63/* Amount of outstanding offset to process */
64static double offset_register;
65
66/* Minimum offset to correct */
67#define MIN_OFFSET_CORRECTION 1.0e-9
68
69/* Current frequency offset between base_freq and the real clock frequency
70 as set by drv_set_freq (not in ppm) */
71static double slew_freq;
72
fc235a3f 73/* Time (raw) of last update of slewing frequency and offset */
d0dfa1de 74static struct timespec slew_start;
fc235a3f
ML
75
76/* Limits for the slew timeout */
77#define MIN_SLEW_TIMEOUT 1.0
78#define MAX_SLEW_TIMEOUT 1.0e4
79
0076458e 80/* Scheduler timeout ID for ending of the currently running slew */
fc235a3f 81static SCH_TimeoutID slew_timeout_id;
fc235a3f
ML
82
83/* Suggested offset correction rate (correction time * offset) */
84static double correction_rate;
85
86/* Maximum expected offset correction error caused by delayed change in the
87 real frequency of the clock */
88static double slew_error;
89
d6fdae5f
ML
90/* Minimum offset that the system driver can slew faster than the maximum
91 frequency offset that it allows to be set directly */
92static double fastslew_min_offset;
93
94/* Maximum slew rate of the system driver */
95static double fastslew_max_rate;
96
97/* Flag indicating that the system driver is currently slewing */
98static int fastslew_active;
99
fc235a3f
ML
100/* ================================================== */
101
102static void handle_end_of_slew(void *anything);
a33a9551 103static void update_slew(void);
fc235a3f
ML
104
105/* ================================================== */
106/* Adjust slew_start on clock step */
107
108static void
d0dfa1de 109handle_step(struct timespec *raw, struct timespec *cooked, double dfreq,
44c9744d 110 double doffset, LCL_ChangeType change_type, void *anything)
fc235a3f 111{
a33a9551
ML
112 if (change_type == LCL_ChangeUnknownStep) {
113 /* Reset offset and slewing */
114 slew_start = *raw;
115 offset_register = 0.0;
116 update_slew();
117 } else if (change_type == LCL_ChangeStep) {
d0dfa1de 118 UTI_AddDoubleToTimespec(&slew_start, -doffset, &slew_start);
a33a9551 119 }
fc235a3f
ML
120}
121
ad942e35
ML
122/* ================================================== */
123
d6fdae5f
ML
124static void
125start_fastslew(void)
126{
127 if (!drv_accrue_offset)
128 return;
129
130 drv_accrue_offset(offset_register, 0.0);
131
f282856c 132 DEBUG_LOG("fastslew offset=%e", offset_register);
d6fdae5f
ML
133
134 offset_register = 0.0;
135 fastslew_active = 1;
136}
137
138/* ================================================== */
139
140static void
d0dfa1de 141stop_fastslew(struct timespec *now)
d6fdae5f
ML
142{
143 double corr;
144
145 if (!drv_get_offset_correction || !fastslew_active)
146 return;
147
148 /* Cancel the remaining offset */
149 drv_get_offset_correction(now, &corr, NULL);
150 drv_accrue_offset(corr, 0.0);
151 offset_register -= corr;
152}
153
154/* ================================================== */
155
ad942e35
ML
156static double
157clamp_freq(double freq)
158{
159 if (freq > max_freq)
160 return max_freq;
161 if (freq < -max_freq)
162 return -max_freq;
163 return freq;
164}
165
fc235a3f
ML
166/* ================================================== */
167/* End currently running slew and start a new one */
168
169static void
170update_slew(void)
171{
d0dfa1de 172 struct timespec now, end_of_slew;
fc235a3f
ML
173 double old_slew_freq, total_freq, corr_freq, duration;
174
175 /* Remove currently running timeout */
0076458e 176 SCH_RemoveTimeout(slew_timeout_id);
fc235a3f
ML
177
178 LCL_ReadRawTime(&now);
179
180 /* Adjust the offset register by achieved slew */
cfe706f0 181 duration = UTI_DiffTimespecsToDouble(&now, &slew_start);
fc235a3f
ML
182 offset_register -= slew_freq * duration;
183
d6fdae5f
ML
184 stop_fastslew(&now);
185
fc235a3f
ML
186 /* Estimate how long should the next slew take */
187 if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
188 duration = MAX_SLEW_TIMEOUT;
189 } else {
190 duration = correction_rate / fabs(offset_register);
191 if (duration < MIN_SLEW_TIMEOUT)
192 duration = MIN_SLEW_TIMEOUT;
193 }
194
195 /* Get frequency offset needed to slew the offset in the duration
196 and clamp it to the allowed maximum */
197 corr_freq = offset_register / duration;
9cf78b97
ML
198 if (corr_freq < -max_corr_freq)
199 corr_freq = -max_corr_freq;
200 else if (corr_freq > max_corr_freq)
201 corr_freq = max_corr_freq;
fc235a3f 202
d6fdae5f
ML
203 /* Let the system driver perform the slew if the requested frequency
204 offset is too large for the frequency driver */
205 if (drv_accrue_offset && fabs(corr_freq) >= fastslew_max_rate &&
206 fabs(offset_register) > fastslew_min_offset) {
207 start_fastslew();
208 corr_freq = 0.0;
209 }
210
fc235a3f 211 /* Get the new real frequency and clamp it */
ad942e35 212 total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq));
fc235a3f
ML
213
214 /* Set the new frequency (the actual frequency returned by the call may be
215 slightly different from the requested frequency due to rounding) */
216 total_freq = (*drv_set_freq)(total_freq);
217
218 /* Compute the new slewing frequency, it's relative to the real frequency to
219 make the calculation in offset_convert() cheaper */
220 old_slew_freq = slew_freq;
221 slew_freq = (total_freq - base_freq) / (1.0e6 - total_freq);
222
223 /* Compute the dispersion introduced by changing frequency and add it
224 to all statistics held at higher levels in the system */
225 slew_error = fabs((old_slew_freq - slew_freq) * max_freq_change_delay);
e8bb95ba
ML
226 if (slew_error >= MIN_OFFSET_CORRECTION)
227 lcl_InvokeDispersionNotifyHandlers(slew_error);
fc235a3f
ML
228
229 /* Compute the duration of the slew and clamp it. If the slewing frequency
230 is zero or has wrong sign (e.g. due to rounding in the frequency driver or
d6fdae5f
ML
231 when base_freq is larger than max_freq, or fast slew is active), use the
232 maximum timeout and try again on the next update. */
f88a712d
ML
233 if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
234 offset_register * slew_freq <= 0.0) {
fc235a3f
ML
235 duration = MAX_SLEW_TIMEOUT;
236 } else {
237 duration = offset_register / slew_freq;
238 if (duration < MIN_SLEW_TIMEOUT)
239 duration = MIN_SLEW_TIMEOUT;
240 else if (duration > MAX_SLEW_TIMEOUT)
241 duration = MAX_SLEW_TIMEOUT;
242 }
243
244 /* Restart timer for the next update */
d0dfa1de 245 UTI_AddDoubleToTimespec(&now, duration, &end_of_slew);
fc235a3f 246 slew_timeout_id = SCH_AddTimeout(&end_of_slew, handle_end_of_slew, NULL);
fc235a3f 247 slew_start = now;
fc235a3f 248
f282856c 249 DEBUG_LOG("slew offset=%e corr_rate=%e base_freq=%f total_freq=%f slew_freq=%e duration=%f slew_error=%e",
e8bb95ba
ML
250 offset_register, correction_rate, base_freq, total_freq, slew_freq,
251 duration, slew_error);
fc235a3f
ML
252}
253
254/* ================================================== */
255
256static void
257handle_end_of_slew(void *anything)
258{
0076458e 259 slew_timeout_id = 0;
fc235a3f
ML
260 update_slew();
261}
262
263/* ================================================== */
264
265static double
266read_frequency(void)
267{
268 return base_freq;
269}
270
271/* ================================================== */
272
273static double
274set_frequency(double freq_ppm)
275{
276 base_freq = freq_ppm;
277 update_slew();
278
279 return base_freq;
280}
281
282/* ================================================== */
283
284static void
285accrue_offset(double offset, double corr_rate)
286{
287 offset_register += offset;
288 correction_rate = corr_rate;
289
290 update_slew();
291}
292
293/* ================================================== */
294/* Determine the correction to generate the cooked time for given raw time */
295
296static void
d0dfa1de 297offset_convert(struct timespec *raw,
fc235a3f
ML
298 double *corr, double *err)
299{
d6fdae5f 300 double duration, fastslew_corr, fastslew_err;
fc235a3f 301
cfe706f0 302 duration = UTI_DiffTimespecsToDouble(raw, &slew_start);
fc235a3f 303
d6fdae5f
ML
304 if (drv_get_offset_correction && fastslew_active) {
305 drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
306 if (fastslew_corr == 0.0 && fastslew_err == 0.0)
307 fastslew_active = 0;
308 } else {
309 fastslew_corr = fastslew_err = 0.0;
310 }
311
312 *corr = slew_freq * duration + fastslew_corr - offset_register;
313
314 if (err) {
315 *err = fastslew_err;
316 if (fabs(duration) <= max_freq_change_delay)
317 *err += slew_error;
318 }
fc235a3f
ML
319}
320
cf3c7b3b
ML
321/* ================================================== */
322/* Positive means currently fast of true time, i.e. jump backwards */
323
f6a9c5c1 324static int
cf3c7b3b
ML
325apply_step_offset(double offset)
326{
d0dfa1de
ML
327 struct timespec old_time, new_time;
328 struct timeval new_time_tv;
cf3c7b3b
ML
329 double err;
330
331 LCL_ReadRawTime(&old_time);
d0dfa1de
ML
332 UTI_AddDoubleToTimespec(&old_time, -offset, &new_time);
333 UTI_TimespecToTimeval(&new_time, &new_time_tv);
cf3c7b3b 334
d0dfa1de 335 if (PRV_SetTime(&new_time_tv, NULL) < 0) {
f282856c 336 DEBUG_LOG("settimeofday() failed");
f6a9c5c1 337 return 0;
cf3c7b3b
ML
338 }
339
340 LCL_ReadRawTime(&old_time);
cfe706f0 341 err = UTI_DiffTimespecsToDouble(&old_time, &new_time);
cf3c7b3b
ML
342
343 lcl_InvokeDispersionNotifyHandlers(fabs(err));
f6a9c5c1
ML
344
345 return 1;
cf3c7b3b
ML
346}
347
fc235a3f
ML
348/* ================================================== */
349
02cbe5e1
ML
350static void
351set_sync_status(int synchronised, double est_error, double max_error)
352{
353 double offset;
354
355 offset = fabs(offset_register);
356 if (est_error < offset)
357 est_error = offset;
358 max_error += offset;
359
360 if (drv_set_sync_status)
361 drv_set_sync_status(synchronised, est_error, max_error);
362}
363
364/* ================================================== */
365
fc235a3f
ML
366void
367SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_delay,
368 lcl_ReadFrequencyDriver sys_read_freq,
369 lcl_SetFrequencyDriver sys_set_freq,
370 lcl_ApplyStepOffsetDriver sys_apply_step_offset,
d6fdae5f
ML
371 double min_fastslew_offset, double max_fastslew_rate,
372 lcl_AccrueOffsetDriver sys_accrue_offset,
373 lcl_OffsetCorrectionDriver sys_get_offset_correction,
e14a03a1
ML
374 lcl_SetLeapDriver sys_set_leap,
375 lcl_SetSyncStatusDriver sys_set_sync_status)
fc235a3f
ML
376{
377 max_freq = max_set_freq_ppm;
378 max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6);
379 drv_read_freq = sys_read_freq;
380 drv_set_freq = sys_set_freq;
d6fdae5f
ML
381 drv_accrue_offset = sys_accrue_offset;
382 drv_get_offset_correction = sys_get_offset_correction;
02cbe5e1 383 drv_set_sync_status = sys_set_sync_status;
fc235a3f
ML
384
385 base_freq = (*drv_read_freq)();
386 slew_freq = 0.0;
387 offset_register = 0.0;
388
9cf78b97
ML
389 max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
390
d6fdae5f
ML
391 fastslew_min_offset = min_fastslew_offset;
392 fastslew_max_rate = max_fastslew_rate / 1.0e6;
393 fastslew_active = 0;
394
fc235a3f 395 lcl_RegisterSystemDrivers(read_frequency, set_frequency,
cf3c7b3b
ML
396 accrue_offset, sys_apply_step_offset ?
397 sys_apply_step_offset : apply_step_offset,
02cbe5e1 398 offset_convert, sys_set_leap, set_sync_status);
fc235a3f
ML
399
400 LCL_AddParameterChangeHandler(handle_step, NULL);
401}
402
403/* ================================================== */
404
405void
406SYS_Generic_Finalise(void)
407{
d0dfa1de 408 struct timespec now;
d6fdae5f 409
fc235a3f
ML
410 /* Must *NOT* leave a slew running - clock could drift way off
411 if the daemon is not restarted */
0076458e
ML
412
413 SCH_RemoveTimeout(slew_timeout_id);
414 slew_timeout_id = 0;
fc235a3f 415
ad942e35 416 (*drv_set_freq)(clamp_freq(base_freq));
d6fdae5f
ML
417
418 LCL_ReadRawTime(&now);
419 stop_fastslew(&now);
fc235a3f
ML
420}
421
422/* ================================================== */