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