1e0163549SMatthew Dillon /*
2e0163549SMatthew Dillon * Copyright (c) 2005 The DragonFly Project. All rights reserved.
3e0163549SMatthew Dillon *
4e0163549SMatthew Dillon * This code is derived from software contributed to The DragonFly Project
5e0163549SMatthew Dillon * by Matthew Dillon <dillon@backplane.com>
6e0163549SMatthew Dillon *
7e0163549SMatthew Dillon * Redistribution and use in source and binary forms, with or without
8e0163549SMatthew Dillon * modification, are permitted provided that the following conditions
9e0163549SMatthew Dillon * are met:
10e0163549SMatthew Dillon *
11e0163549SMatthew Dillon * 1. Redistributions of source code must retain the above copyright
12e0163549SMatthew Dillon * notice, this list of conditions and the following disclaimer.
13e0163549SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
14e0163549SMatthew Dillon * notice, this list of conditions and the following disclaimer in
15e0163549SMatthew Dillon * the documentation and/or other materials provided with the
16e0163549SMatthew Dillon * distribution.
17e0163549SMatthew Dillon * 3. Neither the name of The DragonFly Project nor the names of its
18e0163549SMatthew Dillon * contributors may be used to endorse or promote products derived
19e0163549SMatthew Dillon * from this software without specific, prior written permission.
20e0163549SMatthew Dillon *
21e0163549SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22e0163549SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23e0163549SMatthew Dillon * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24e0163549SMatthew Dillon * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25e0163549SMatthew Dillon * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26e0163549SMatthew Dillon * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27e0163549SMatthew Dillon * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28e0163549SMatthew Dillon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29e0163549SMatthew Dillon * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30e0163549SMatthew Dillon * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31e0163549SMatthew Dillon * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32e0163549SMatthew Dillon * SUCH DAMAGE.
33e0163549SMatthew Dillon */
34e0163549SMatthew Dillon
35e0163549SMatthew Dillon #include "defs.h"
36e0163549SMatthew Dillon
372408d859SMatthew Dillon static int client_insane(struct server_info **, int, server_info_t);
382408d859SMatthew Dillon
39e0163549SMatthew Dillon void
client_init(void)40e0163549SMatthew Dillon client_init(void)
41e0163549SMatthew Dillon {
42e0163549SMatthew Dillon }
43e0163549SMatthew Dillon
44*1d9b37b0SSascha Wildner void
client_main(struct server_info ** info_ary,int count)45e0163549SMatthew Dillon client_main(struct server_info **info_ary, int count)
46e0163549SMatthew Dillon {
478e4e9813SMatthew Dillon struct server_info *best_off;
488e4e9813SMatthew Dillon struct server_info *best_freq;
495d177fa2SMatthew Dillon double last_freq;
50e0163549SMatthew Dillon double freq;
51e0163549SMatthew Dillon double offset;
52c32f1362SMatthew Dillon int calc_offset_correction;
532408d859SMatthew Dillon int didreconnect;
542408d859SMatthew Dillon int i;
552408d859SMatthew Dillon int insane;
56e0163549SMatthew Dillon
575d177fa2SMatthew Dillon last_freq = 0.0;
585d177fa2SMatthew Dillon
59e0163549SMatthew Dillon for (;;) {
60e0163549SMatthew Dillon /*
61fdd4337fSMatthew Dillon * Subtract the interval from poll_sleep and poll the client
62fdd4337fSMatthew Dillon * if it reaches 0.
63c32f1362SMatthew Dillon *
64c32f1362SMatthew Dillon * Because we do not compensate for offset corrections which are
65c32f1362SMatthew Dillon * in progress, we cannot accumulate data for an offset correction
66c32f1362SMatthew Dillon * while a prior correction is still being worked through by the
67c32f1362SMatthew Dillon * system.
68e0163549SMatthew Dillon */
69c32f1362SMatthew Dillon calc_offset_correction = !sysntp_offset_correction_is_running();
70e0163549SMatthew Dillon for (i = 0; i < count; ++i)
71c32f1362SMatthew Dillon client_poll(info_ary[i], min_sleep_opt, calc_offset_correction);
72e0163549SMatthew Dillon
73e0163549SMatthew Dillon /*
748e4e9813SMatthew Dillon * Find the best client (or synthesize one). A different client
758e4e9813SMatthew Dillon * can be chosen for frequency and offset. Note in particular
768e4e9813SMatthew Dillon * that offset counters and averaging code gets reset when an
778e4e9813SMatthew Dillon * offset correction is made (otherwise the averaging history will
788e4e9813SMatthew Dillon * cause later corrections to overshoot).
798e4e9813SMatthew Dillon *
808e4e9813SMatthew Dillon * The regression used to calculate the frequency is a much
818e4e9813SMatthew Dillon * longer-term entity and is NOT reset, so it is still possible
828e4e9813SMatthew Dillon * for the offset correction code to make minor adjustments to
838e4e9813SMatthew Dillon * the frequency if it so desires.
84e0163549SMatthew Dillon *
85e0163549SMatthew Dillon * client_check may replace the server_info pointer with a new
86e0163549SMatthew Dillon * one.
87e0163549SMatthew Dillon */
888e4e9813SMatthew Dillon best_off = NULL;
898e4e9813SMatthew Dillon best_freq = NULL;
90e0163549SMatthew Dillon for (i = 0; i < count; ++i)
918e4e9813SMatthew Dillon client_check(&info_ary[i], &best_off, &best_freq);
928e4e9813SMatthew Dillon
938e4e9813SMatthew Dillon /*
942408d859SMatthew Dillon * Check for server insanity. In large NNTP pools some servers
952408d859SMatthew Dillon * may just be dead wrong, but report that they are right.
962408d859SMatthew Dillon */
972408d859SMatthew Dillon if (best_off) {
982408d859SMatthew Dillon insane = client_insane(info_ary, count, best_off);
992408d859SMatthew Dillon if (insane > 0) {
1002408d859SMatthew Dillon /*
1012408d859SMatthew Dillon * best_off meets the quorum requirements and is good
1022408d859SMatthew Dillon * (keep best_off)
1032408d859SMatthew Dillon */
1045e13edd7SMatthew Dillon best_off->server_insane = 0;
1052408d859SMatthew Dillon } else if (insane == 0) {
1062408d859SMatthew Dillon /*
1072408d859SMatthew Dillon * best_off is probably good, but we do not have enough
1082408d859SMatthew Dillon * servers reporting yet to meet the quorum requirements.
1092408d859SMatthew Dillon */
1102408d859SMatthew Dillon best_off = NULL;
1112408d859SMatthew Dillon } else {
1122408d859SMatthew Dillon /*
1135e13edd7SMatthew Dillon * best_off is ugly, mark the server as being insane for
1145e13edd7SMatthew Dillon * 60 minutes.
1152408d859SMatthew Dillon */
1165e13edd7SMatthew Dillon best_off->server_insane = 60 * 60;
1175e13edd7SMatthew Dillon logdebuginfo(best_off, 1,
1185e13edd7SMatthew Dillon "excessive offset deviation, mapping out\n");
1195e13edd7SMatthew Dillon best_off = NULL;
1202408d859SMatthew Dillon }
1212408d859SMatthew Dillon }
1222408d859SMatthew Dillon
1232408d859SMatthew Dillon /*
1248e4e9813SMatthew Dillon * Offset correction.
1258e4e9813SMatthew Dillon */
1268e4e9813SMatthew Dillon if (best_off) {
1278e4e9813SMatthew Dillon offset = best_off->lin_sumoffset / best_off->lin_countoffset;
1288e4e9813SMatthew Dillon lin_resetalloffsets(info_ary, count);
1293d15852bSMatthew Dillon if (offset < -COURSE_OFFSET_CORRECTION_LIMIT ||
13075349a56SMatthew Dillon offset > COURSE_OFFSET_CORRECTION_LIMIT ||
13175349a56SMatthew Dillon quickset_opt
1323d15852bSMatthew Dillon ) {
1333d15852bSMatthew Dillon freq = sysntp_correct_course_offset(offset);
13475349a56SMatthew Dillon quickset_opt = 0;
1353d15852bSMatthew Dillon } else {
136e0163549SMatthew Dillon freq = sysntp_correct_offset(offset);
1373d15852bSMatthew Dillon }
1388e4e9813SMatthew Dillon } else {
1398e4e9813SMatthew Dillon freq = 0.0;
140e0163549SMatthew Dillon }
1418e4e9813SMatthew Dillon
1428e4e9813SMatthew Dillon /*
1438e4e9813SMatthew Dillon * Frequency correction (throw away minor freq adjusts from the
1445d177fa2SMatthew Dillon * offset code if we can't do a frequency correction here). Do
1455d177fa2SMatthew Dillon * not reissue if it hasn't changed from the last issued correction.
1468e4e9813SMatthew Dillon */
1478e4e9813SMatthew Dillon if (best_freq) {
1485d177fa2SMatthew Dillon freq += best_freq->lin_cache_freq;
1495d177fa2SMatthew Dillon if (last_freq != freq) {
1505d177fa2SMatthew Dillon sysntp_correct_freq(freq);
1515d177fa2SMatthew Dillon last_freq = freq;
1525d177fa2SMatthew Dillon }
1538e4e9813SMatthew Dillon }
1548e4e9813SMatthew Dillon
155fdd4337fSMatthew Dillon /*
156fdd4337fSMatthew Dillon * This function is responsible for managing the polling mode and
157fdd4337fSMatthew Dillon * figures out how long we should sleep.
158fdd4337fSMatthew Dillon */
1592408d859SMatthew Dillon didreconnect = 0;
160fdd4337fSMatthew Dillon for (i = 0; i < count; ++i)
1612408d859SMatthew Dillon client_manage_polling_mode(info_ary[i], &didreconnect);
1622408d859SMatthew Dillon if (didreconnect)
1632408d859SMatthew Dillon client_check_duplicate_ips(info_ary, count);
164fdd4337fSMatthew Dillon
165fdd4337fSMatthew Dillon /*
166fdd4337fSMatthew Dillon * Polling loop sleep.
167fdd4337fSMatthew Dillon */
168fdd4337fSMatthew Dillon usleep(min_sleep_opt * 1000000 + random() % 500000);
169e0163549SMatthew Dillon }
170e0163549SMatthew Dillon }
171e0163549SMatthew Dillon
172e0163549SMatthew Dillon void
client_poll(server_info_t info,int poll_interval,int calc_offset_correction)173c32f1362SMatthew Dillon client_poll(server_info_t info, int poll_interval, int calc_offset_correction)
174e0163549SMatthew Dillon {
175e0163549SMatthew Dillon struct timeval rtv;
176e0163549SMatthew Dillon struct timeval ltv;
177e0163549SMatthew Dillon struct timeval lbtv;
178e0163549SMatthew Dillon double offset;
179e0163549SMatthew Dillon
180fdd4337fSMatthew Dillon /*
1815e13edd7SMatthew Dillon * Adjust the insane-server countdown
1825e13edd7SMatthew Dillon */
1835e13edd7SMatthew Dillon if (info->server_insane > poll_interval)
1845e13edd7SMatthew Dillon info->server_insane -= poll_interval;
1855e13edd7SMatthew Dillon else
1865e13edd7SMatthew Dillon info->server_insane = 0;
1875e13edd7SMatthew Dillon
1885e13edd7SMatthew Dillon /*
189fdd4337fSMatthew Dillon * By default we always poll. If the polling interval comes under
190fdd4337fSMatthew Dillon * active management the poll_sleep will be non-zero.
191fdd4337fSMatthew Dillon */
192fdd4337fSMatthew Dillon if (info->poll_sleep > poll_interval) {
193fdd4337fSMatthew Dillon info->poll_sleep -= poll_interval;
194fdd4337fSMatthew Dillon return;
195fdd4337fSMatthew Dillon }
196fdd4337fSMatthew Dillon info->poll_sleep = 0;
197fdd4337fSMatthew Dillon
1982408d859SMatthew Dillon /*
1992408d859SMatthew Dillon * If the client isn't open don't mess with the poll_failed count
2002408d859SMatthew Dillon * or anything else. We are left in the init or startup phase.
2012408d859SMatthew Dillon */
2022408d859SMatthew Dillon if (info->fd < 0) {
2032408d859SMatthew Dillon if (info->poll_failed < 0x7FFFFFFF)
2042408d859SMatthew Dillon ++info->poll_failed;
2052408d859SMatthew Dillon return;
2062408d859SMatthew Dillon }
2072408d859SMatthew Dillon
2082408d859SMatthew Dillon logdebuginfo(info, 4, "poll, ");
209e0163549SMatthew Dillon if (udp_ntptimereq(info->fd, &rtv, <v, &lbtv) < 0) {
210fdd4337fSMatthew Dillon ++info->poll_failed;
2112408d859SMatthew Dillon logdebug(4, "no response (%d failures in a row)\n", info->poll_failed);
212fdd4337fSMatthew Dillon if (info->poll_failed == POLL_FAIL_RESET) {
2133d15852bSMatthew Dillon if (info->lin_count != 0) {
2142408d859SMatthew Dillon logdebuginfo(info, 4, "resetting regression due to failures\n");
215fdd4337fSMatthew Dillon }
216fdd4337fSMatthew Dillon lin_reset(info);
217fdd4337fSMatthew Dillon }
218e0163549SMatthew Dillon return;
219e0163549SMatthew Dillon }
220e0163549SMatthew Dillon
221e0163549SMatthew Dillon /*
222fdd4337fSMatthew Dillon * Successful query. Update polling info for the polling mode manager.
223fdd4337fSMatthew Dillon */
224fdd4337fSMatthew Dillon ++info->poll_count;
225fdd4337fSMatthew Dillon info->poll_failed = 0;
226fdd4337fSMatthew Dillon
227fdd4337fSMatthew Dillon /*
228e0163549SMatthew Dillon * Figure out the offset (the difference between the reported
229e0163549SMatthew Dillon * time and our current time) for linear regression purposes.
230e0163549SMatthew Dillon */
2318e4e9813SMatthew Dillon offset = tv_delta_double(&rtv, <v);
232e0163549SMatthew Dillon
233e0163549SMatthew Dillon while (info) {
234e0163549SMatthew Dillon /*
235e0163549SMatthew Dillon * Linear regression
236e0163549SMatthew Dillon */
2373d15852bSMatthew Dillon if (debug_level >= 4) {
238e0163549SMatthew Dillon struct tm *tp;
239e0163549SMatthew Dillon char buf[64];
240e0163549SMatthew Dillon time_t t;
241e0163549SMatthew Dillon
242e0163549SMatthew Dillon t = rtv.tv_sec;
243e0163549SMatthew Dillon tp = localtime(&t);
244e0163549SMatthew Dillon strftime(buf, sizeof(buf), "%d-%b-%Y %H:%M:%S", tp);
2453d15852bSMatthew Dillon logdebug(4, "%s.%03ld ", buf, rtv.tv_usec / 1000);
246e0163549SMatthew Dillon }
247c32f1362SMatthew Dillon lin_regress(info, <v, &lbtv, offset, calc_offset_correction);
248e0163549SMatthew Dillon info = info->altinfo;
2493d15852bSMatthew Dillon if (info && debug_level >= 4) {
2503d15852bSMatthew Dillon logdebug(4, "%*.*s: poll, ",
2519b724d8cSMatthew Dillon (int)strlen(info->target),
2523d15852bSMatthew Dillon (int)strlen(info->target), "(alt)");
2539b724d8cSMatthew Dillon }
254e0163549SMatthew Dillon }
255e0163549SMatthew Dillon }
256e0163549SMatthew Dillon
257e0163549SMatthew Dillon /*
2588e4e9813SMatthew Dillon * Find the best client (or synthesize a fake info structure to return).
2598e4e9813SMatthew Dillon * We can find separate best clients for offset and frequency.
260e0163549SMatthew Dillon */
2618e4e9813SMatthew Dillon void
client_check(struct server_info ** checkp,struct server_info ** best_off,struct server_info ** best_freq)2628e4e9813SMatthew Dillon client_check(struct server_info **checkp,
2638e4e9813SMatthew Dillon struct server_info **best_off,
2648e4e9813SMatthew Dillon struct server_info **best_freq)
265e0163549SMatthew Dillon {
266e0163549SMatthew Dillon struct server_info *check = *checkp;
267e0163549SMatthew Dillon struct server_info *info;
2684c77cdebSMatthew Dillon int min_samples;
269e0163549SMatthew Dillon
270e0163549SMatthew Dillon /*
271e0163549SMatthew Dillon * Start an alternate linear regression once our current one
272e0163549SMatthew Dillon * has passed a certain point.
273e0163549SMatthew Dillon */
274e0163549SMatthew Dillon if (check->lin_count >= LIN_RESTART / 2 && check->altinfo == NULL) {
275e0163549SMatthew Dillon info = malloc(sizeof(*info));
276e0163549SMatthew Dillon assert(info != NULL);
277fdd4337fSMatthew Dillon /* note: check->altinfo is NULL as of the bcopy */
278e0163549SMatthew Dillon bcopy(check, info, sizeof(*info));
279e0163549SMatthew Dillon check->altinfo = info;
280e0163549SMatthew Dillon lin_reset(info);
281e0163549SMatthew Dillon }
282e0163549SMatthew Dillon
283e0163549SMatthew Dillon /*
284e0163549SMatthew Dillon * Replace our current linear regression with the alternate once
285e0163549SMatthew Dillon * the current one has hit its limit (beyond a certain point the
286e0163549SMatthew Dillon * linear regression starts to work against us, preventing us from
287e0163549SMatthew Dillon * reacting to changing conditions).
288e0163549SMatthew Dillon *
289e0163549SMatthew Dillon * Report any significant change in the offset or ppm.
290e0163549SMatthew Dillon */
291e0163549SMatthew Dillon if (check->lin_count >= LIN_RESTART) {
292e0163549SMatthew Dillon if ((info = check->altinfo) && info->lin_count >= LIN_RESTART / 2) {
293e0163549SMatthew Dillon double freq_diff;
294e0163549SMatthew Dillon
295e0163549SMatthew Dillon freq_diff = info->lin_cache_freq - check->lin_cache_freq;
2968d67cbb3SSascha Wildner logdebuginfo(info, 4, "Switching to alternate, Frequency "
2973d15852bSMatthew Dillon "difference is %6.3f ppm\n",
2982408d859SMatthew Dillon freq_diff * 1.0E+6);
299e0163549SMatthew Dillon *checkp = info;
300e0163549SMatthew Dillon free(check);
301e0163549SMatthew Dillon check = info;
302e0163549SMatthew Dillon }
303e0163549SMatthew Dillon }
304e0163549SMatthew Dillon
305e0163549SMatthew Dillon /*
3068e4e9813SMatthew Dillon * BEST CLIENT FOR FREQUENCY CORRECTION:
3072e8f7384SMatthew Dillon *
3084c77cdebSMatthew Dillon * Frequency corrections get better the longer the time separation
3094c77cdebSMatthew Dillon * between samples.
3104c77cdebSMatthew Dillon *
3113f5e28f4SSascha Wildner * 8 samples and a correlation > 0.99, or
3123f5e28f4SSascha Wildner * 16 samples and a correlation > 0.96
313e0163549SMatthew Dillon */
3148e4e9813SMatthew Dillon info = *best_freq;
3152e8f7384SMatthew Dillon if ((check->lin_count >= 8 && fabs(check->lin_cache_corr) >= 0.99) ||
3162e8f7384SMatthew Dillon (check->lin_count >= 16 && fabs(check->lin_cache_corr) >= 0.96)
3172e8f7384SMatthew Dillon ) {
3188e4e9813SMatthew Dillon if (info == NULL ||
3198e4e9813SMatthew Dillon fabs(check->lin_cache_corr) > fabs(info->lin_cache_corr)
3208e4e9813SMatthew Dillon ) {
3218e4e9813SMatthew Dillon info = check;
3228e4e9813SMatthew Dillon *best_freq = info;
323e0163549SMatthew Dillon }
324e0163549SMatthew Dillon
325e0163549SMatthew Dillon }
3268e4e9813SMatthew Dillon
3278e4e9813SMatthew Dillon /*
3288e4e9813SMatthew Dillon * BEST CLIENT FOR OFFSET CORRECTION:
3298e4e9813SMatthew Dillon *
3308e4e9813SMatthew Dillon * Use the standard-deviation and require at least 4 samples. An
3318e4e9813SMatthew Dillon * offset correction is valid if the standard deviation is less then
3328e4e9813SMatthew Dillon * the average offset divided by 4.
3335e13edd7SMatthew Dillon *
3344c77cdebSMatthew Dillon * If we are in maintainance mode, require 8 samples instead of 4.
3354c77cdebSMatthew Dillon * Offset corrections get better with more samples. This reduces
3364c77cdebSMatthew Dillon * ping-pong effects that can occur with a small number of samples.
3374c77cdebSMatthew Dillon *
3385e13edd7SMatthew Dillon * Servers marked as being insane are not allowed
3398e4e9813SMatthew Dillon */
3408e4e9813SMatthew Dillon info = *best_off;
3414c77cdebSMatthew Dillon if (info && info->poll_mode == POLL_MAINTAIN)
3424c77cdebSMatthew Dillon min_samples = 8;
3434c77cdebSMatthew Dillon else
3444c77cdebSMatthew Dillon min_samples = 4;
3454c77cdebSMatthew Dillon if (check->lin_countoffset >= min_samples &&
3465e13edd7SMatthew Dillon (check->lin_cache_stddev <
3475e13edd7SMatthew Dillon fabs(check->lin_sumoffset / check->lin_countoffset / 4)) &&
3485e13edd7SMatthew Dillon check->server_insane == 0
3495e13edd7SMatthew Dillon ) {
3508e4e9813SMatthew Dillon if (info == NULL ||
3518e4e9813SMatthew Dillon fabs(check->lin_cache_stddev) < fabs(info->lin_cache_stddev)
3528e4e9813SMatthew Dillon ) {
3538e4e9813SMatthew Dillon info = check;
3548e4e9813SMatthew Dillon *best_off = info;
3558e4e9813SMatthew Dillon }
3568e4e9813SMatthew Dillon }
357e0163549SMatthew Dillon }
358e0163549SMatthew Dillon
359e0163549SMatthew Dillon /*
360fdd4337fSMatthew Dillon * Actively manage the polling interval. Note that the poll_* fields are
361fdd4337fSMatthew Dillon * always transfered to the alternate regression when the check code replaces
362fdd4337fSMatthew Dillon * the current regression with a new one.
363fdd4337fSMatthew Dillon *
364fdd4337fSMatthew Dillon * This routine is called from the main loop for each base info structure.
365fdd4337fSMatthew Dillon * The polling mode applies to all alternates so we do not have to iterate
366fdd4337fSMatthew Dillon * through the alt's.
367fdd4337fSMatthew Dillon */
368fdd4337fSMatthew Dillon void
client_manage_polling_mode(struct server_info * info,int * didreconnect)3692408d859SMatthew Dillon client_manage_polling_mode(struct server_info *info, int *didreconnect)
370fdd4337fSMatthew Dillon {
371fdd4337fSMatthew Dillon /*
3722408d859SMatthew Dillon * Permanently failed servers are ignored.
373fdd4337fSMatthew Dillon */
3742408d859SMatthew Dillon if (info->server_state == -2)
3752408d859SMatthew Dillon return;
3762408d859SMatthew Dillon
3772408d859SMatthew Dillon /*
3782408d859SMatthew Dillon * Our polling interval has not yet passed.
3792408d859SMatthew Dillon */
3802408d859SMatthew Dillon if (info->poll_sleep)
3812408d859SMatthew Dillon return;
382fdd4337fSMatthew Dillon
383fdd4337fSMatthew Dillon /*
384fdd4337fSMatthew Dillon * Standard polling mode progression
385fdd4337fSMatthew Dillon */
386fdd4337fSMatthew Dillon switch(info->poll_mode) {
387fdd4337fSMatthew Dillon case POLL_FIXED:
3882408d859SMatthew Dillon /*
3892408d859SMatthew Dillon * Initial state after connect or when a reconnect is required.
3902408d859SMatthew Dillon */
3912408d859SMatthew Dillon if (info->fd < 0) {
3922408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode INIT, relookup & reconnect\n");
3932408d859SMatthew Dillon reconnect_server(info);
3942408d859SMatthew Dillon *didreconnect = 1;
3952408d859SMatthew Dillon if (info->fd < 0) {
3962408d859SMatthew Dillon if (info->poll_failed >= POLL_RECOVERY_RESTART * 5)
3972408d859SMatthew Dillon info->poll_sleep = max_sleep_opt;
3982408d859SMatthew Dillon else if (info->poll_failed >= POLL_RECOVERY_RESTART)
3992408d859SMatthew Dillon info->poll_sleep = nom_sleep_opt;
4002408d859SMatthew Dillon else
401fdd4337fSMatthew Dillon info->poll_sleep = min_sleep_opt;
402fdd4337fSMatthew Dillon break;
403fdd4337fSMatthew Dillon }
4042408d859SMatthew Dillon
4052408d859SMatthew Dillon /*
4062408d859SMatthew Dillon * Transition the server to the DNS lookup successful state.
4072408d859SMatthew Dillon * Note that the server state does not transition out of
4082408d859SMatthew Dillon * lookup successful if we relookup after a packet failure
4092408d859SMatthew Dillon * so the message is printed only once, usually.
4102408d859SMatthew Dillon */
4112408d859SMatthew Dillon client_setserverstate(info, 0, "DNS lookup success");
4122408d859SMatthew Dillon
4132408d859SMatthew Dillon /*
4142408d859SMatthew Dillon * If we've failed many times switch to the startup state but
4152408d859SMatthew Dillon * do not fall through into it. break the switch and a single
4162408d859SMatthew Dillon * poll will be made after the nominal polling interval.
4172408d859SMatthew Dillon */
4182408d859SMatthew Dillon if (info->poll_failed >= POLL_RECOVERY_RESTART * 5) {
4192408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode INIT->STARTUP (very slow)\n");
4202408d859SMatthew Dillon info->poll_mode = POLL_STARTUP;
4212408d859SMatthew Dillon info->poll_sleep = max_sleep_opt;
4222408d859SMatthew Dillon info->poll_count = 0;
4232408d859SMatthew Dillon break;
4242408d859SMatthew Dillon } else if (info->poll_failed >= POLL_RECOVERY_RESTART) {
4252408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode INIT->STARTUP (slow)\n");
4262408d859SMatthew Dillon info->poll_mode = POLL_STARTUP;
4272408d859SMatthew Dillon info->poll_count = 0;
4282408d859SMatthew Dillon break;
4292408d859SMatthew Dillon }
4302408d859SMatthew Dillon }
4312408d859SMatthew Dillon
4322408d859SMatthew Dillon /*
4332408d859SMatthew Dillon * Fall through to the startup state.
4342408d859SMatthew Dillon */
4352408d859SMatthew Dillon info->poll_mode = POLL_STARTUP;
4362408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode INIT->STARTUP (normal)\n");
4372408d859SMatthew Dillon /* fall through */
4382408d859SMatthew Dillon case POLL_STARTUP:
4392408d859SMatthew Dillon /*
4402408d859SMatthew Dillon * Transition to a FAILED state if too many poll failures occured.
4412408d859SMatthew Dillon */
4422408d859SMatthew Dillon if (info->poll_failed >= POLL_FAIL_RESET) {
4432408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode STARTUP->FAILED\n");
4442408d859SMatthew Dillon info->poll_mode = POLL_FAILED;
4452408d859SMatthew Dillon info->poll_count = 0;
4462408d859SMatthew Dillon break;
4472408d859SMatthew Dillon }
4482408d859SMatthew Dillon
4492408d859SMatthew Dillon /*
4502408d859SMatthew Dillon * Transition the server to operational. Do a number of minimum
4512408d859SMatthew Dillon * interval polls to try to get a good offset calculation quickly.
4522408d859SMatthew Dillon */
4532408d859SMatthew Dillon if (info->poll_count)
4542408d859SMatthew Dillon client_setserverstate(info, 1, "connected ok");
4552408d859SMatthew Dillon if (info->poll_count < POLL_STARTUP_MAX) {
4562408d859SMatthew Dillon info->poll_sleep = min_sleep_opt;
4572408d859SMatthew Dillon break;
4582408d859SMatthew Dillon }
4592408d859SMatthew Dillon
4602408d859SMatthew Dillon /*
4612408d859SMatthew Dillon * Once we've got our polls fall through to aquisition mode to
4622408d859SMatthew Dillon * do aquisition processing.
4632408d859SMatthew Dillon */
464fdd4337fSMatthew Dillon info->poll_mode = POLL_ACQUIRE;
465fdd4337fSMatthew Dillon info->poll_count = 0;
4662408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode STARTUP->ACQUIRE\n");
467fdd4337fSMatthew Dillon /* fall through */
468fdd4337fSMatthew Dillon case POLL_ACQUIRE:
469fdd4337fSMatthew Dillon /*
4702408d859SMatthew Dillon * Transition to a FAILED state if too many poll failures occured.
4712408d859SMatthew Dillon */
4722408d859SMatthew Dillon if (info->poll_failed >= POLL_FAIL_RESET) {
4732408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode STARTUP->FAILED\n");
4742408d859SMatthew Dillon info->poll_mode = POLL_FAILED;
4752408d859SMatthew Dillon info->poll_count = 0;
4762408d859SMatthew Dillon break;
4772408d859SMatthew Dillon }
4782408d859SMatthew Dillon
4792408d859SMatthew Dillon /*
480fdd4337fSMatthew Dillon * Acquisition mode using the nominal timeout. We do not shift
4813f5e28f4SSascha Wildner * to maintainance mode unless the correlation is at least 0.90
482fdd4337fSMatthew Dillon */
483fdd4337fSMatthew Dillon if (info->poll_count < POLL_ACQUIRE_MAX ||
484fdd4337fSMatthew Dillon info->lin_count < 8 ||
485fdd4337fSMatthew Dillon fabs(info->lin_cache_corr) < 0.85
486fdd4337fSMatthew Dillon ) {
4873d15852bSMatthew Dillon if (info->poll_count >= POLL_ACQUIRE_MAX &&
488fdd4337fSMatthew Dillon info->lin_count == LIN_RESTART - 2
489fdd4337fSMatthew Dillon ) {
4902408d859SMatthew Dillon logdebuginfo(info, 2,
4912408d859SMatthew Dillon "WARNING: Unable to shift this source to "
4924e8ef5c1SSascha Wildner "maintenance mode. Target correlation is awful\n");
493fdd4337fSMatthew Dillon }
494fdd4337fSMatthew Dillon break;
495fdd4337fSMatthew Dillon }
496fdd4337fSMatthew Dillon info->poll_mode = POLL_MAINTAIN;
497fdd4337fSMatthew Dillon info->poll_count = 0;
4982408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode ACQUIRE->MAINTAIN\n");
499fdd4337fSMatthew Dillon /* fall through */
500fdd4337fSMatthew Dillon case POLL_MAINTAIN:
5012408d859SMatthew Dillon /*
5022408d859SMatthew Dillon * Transition to a FAILED state if too many poll failures occured.
5032408d859SMatthew Dillon */
5042408d859SMatthew Dillon if (info->poll_failed >= POLL_FAIL_RESET) {
5052408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode STARTUP->FAILED\n");
5062408d859SMatthew Dillon info->poll_mode = POLL_FAILED;
5072408d859SMatthew Dillon info->poll_count = 0;
5082408d859SMatthew Dillon break;
5092408d859SMatthew Dillon }
5102408d859SMatthew Dillon
5112408d859SMatthew Dillon /*
5122408d859SMatthew Dillon * Maintaince mode, max polling interval.
5132408d859SMatthew Dillon *
5142408d859SMatthew Dillon * Transition back to acquisition mode if we are unable to maintain
5152408d859SMatthew Dillon * this mode due to the correlation going bad.
5162408d859SMatthew Dillon */
517fdd4337fSMatthew Dillon if (info->lin_count >= LIN_RESTART / 2 &&
518fdd4337fSMatthew Dillon fabs(info->lin_cache_corr) < 0.70
519fdd4337fSMatthew Dillon ) {
5202408d859SMatthew Dillon logdebuginfo(info, 2,
5212408d859SMatthew Dillon "polling mode MAINTAIN->ACQUIRE. Unable to maintain\n"
5223f5e28f4SSascha Wildner "the maintenance mode because the correlation went"
5232408d859SMatthew Dillon " bad!\n");
524fdd4337fSMatthew Dillon info->poll_mode = POLL_ACQUIRE;
525fdd4337fSMatthew Dillon info->poll_count = 0;
526fdd4337fSMatthew Dillon break;
527fdd4337fSMatthew Dillon }
528fdd4337fSMatthew Dillon info->poll_sleep = max_sleep_opt;
529fdd4337fSMatthew Dillon break;
5302408d859SMatthew Dillon case POLL_FAILED:
531fdd4337fSMatthew Dillon /*
5322408d859SMatthew Dillon * We have a communications failure. A late recovery is possible
5332408d859SMatthew Dillon * if we enter this state with a good poll.
534fdd4337fSMatthew Dillon */
535fdd4337fSMatthew Dillon if (info->poll_count != 0) {
5362408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode FAILED->ACQUIRE\n");
5372408d859SMatthew Dillon if (info->poll_failed >= POLL_FAIL_RESET)
5382408d859SMatthew Dillon info->poll_mode = POLL_STARTUP;
5392408d859SMatthew Dillon else
540fdd4337fSMatthew Dillon info->poll_mode = POLL_ACQUIRE;
541fdd4337fSMatthew Dillon /* do not reset poll_count */
542fdd4337fSMatthew Dillon break;
543fdd4337fSMatthew Dillon }
5442408d859SMatthew Dillon
545fdd4337fSMatthew Dillon /*
5462408d859SMatthew Dillon * If we have been failed too long, disconnect from the server
5472408d859SMatthew Dillon * and start us all over again. Note that the failed count is not
5482408d859SMatthew Dillon * reset to 0.
549fdd4337fSMatthew Dillon */
5502408d859SMatthew Dillon if (info->poll_failed >= POLL_RECOVERY_RESTART) {
5512408d859SMatthew Dillon logdebuginfo(info, 2, "polling mode FAILED->INIT\n");
5522408d859SMatthew Dillon client_setserverstate(info, 0, "FAILED");
5532408d859SMatthew Dillon disconnect_server(info);
5542408d859SMatthew Dillon info->poll_mode = POLL_FIXED;
555fdd4337fSMatthew Dillon break;
556fdd4337fSMatthew Dillon }
5572408d859SMatthew Dillon break;
5582408d859SMatthew Dillon }
5592408d859SMatthew Dillon
5602408d859SMatthew Dillon /*
5612408d859SMatthew Dillon * If the above state machine has not set a polling interval, set a
5622408d859SMatthew Dillon * nominal polling interval.
5632408d859SMatthew Dillon */
564fdd4337fSMatthew Dillon if (info->poll_sleep == 0)
565fdd4337fSMatthew Dillon info->poll_sleep = nom_sleep_opt;
5662408d859SMatthew Dillon }
5672408d859SMatthew Dillon
5682408d859SMatthew Dillon /*
5692408d859SMatthew Dillon * Look for duplicate IP addresses. This is done very inoften, so we do
5702408d859SMatthew Dillon * not use a particularly efficient algorithm.
5712408d859SMatthew Dillon *
5722408d859SMatthew Dillon * Only reconnect a client which has not done its initial poll.
5732408d859SMatthew Dillon */
5742408d859SMatthew Dillon void
client_check_duplicate_ips(struct server_info ** info_ary,int count)5752408d859SMatthew Dillon client_check_duplicate_ips(struct server_info **info_ary, int count)
5762408d859SMatthew Dillon {
5772408d859SMatthew Dillon server_info_t info1;
5782408d859SMatthew Dillon server_info_t info2;
5792408d859SMatthew Dillon int tries;
5802408d859SMatthew Dillon int i;
5812408d859SMatthew Dillon int j;
5822408d859SMatthew Dillon
5832408d859SMatthew Dillon for (i = 0; i < count; ++i) {
5842408d859SMatthew Dillon info1 = info_ary[i];
5852408d859SMatthew Dillon if (info1->fd < 0 || info1->server_state != 0)
5862408d859SMatthew Dillon continue;
5872408d859SMatthew Dillon for (tries = 0; tries < 10; ++tries) {
5882408d859SMatthew Dillon for (j = 0; j < count; ++j) {
5892408d859SMatthew Dillon info2 = info_ary[j];
5902408d859SMatthew Dillon if (i == j || info2->fd < 0)
5912408d859SMatthew Dillon continue;
5927af666f2SSepherosa Ziehau if (info1->fd < 0 || /* info1 was lost in previous reconnect */
5937af666f2SSepherosa Ziehau strcmp(info1->ipstr, info2->ipstr) == 0) {
5942408d859SMatthew Dillon reconnect_server(info1);
595fdd4337fSMatthew Dillon break;
596fdd4337fSMatthew Dillon }
597fdd4337fSMatthew Dillon }
5982408d859SMatthew Dillon if (j == count)
5992408d859SMatthew Dillon break;
6002408d859SMatthew Dillon }
6012408d859SMatthew Dillon if (tries == 10) {
6022408d859SMatthew Dillon disconnect_server(info1);
6032408d859SMatthew Dillon client_setserverstate(info1, -2,
6042408d859SMatthew Dillon "permanently disabling duplicate server");
6052408d859SMatthew Dillon }
6062408d859SMatthew Dillon }
6072408d859SMatthew Dillon }
6082408d859SMatthew Dillon
6092408d859SMatthew Dillon /*
6102408d859SMatthew Dillon * Calculate whether the server pointed to by *bestp is insane or not.
6112408d859SMatthew Dillon * For some reason some servers in e.g. the ntp pool are sometimes an hour
6122408d859SMatthew Dillon * off. If we have at least three servers in the pool require that a
6132408d859SMatthew Dillon * quorum agree that the current best server's offset is reasonable.
6142408d859SMatthew Dillon *
6155e13edd7SMatthew Dillon * Allow +/- 0.5 seconds of error for now (settable with option).
6162408d859SMatthew Dillon *
6172408d859SMatthew Dillon * Returns -1 if insane, 0 if not enough samples, and 1 if ok
6182408d859SMatthew Dillon */
6192408d859SMatthew Dillon static
6202408d859SMatthew Dillon int
client_insane(struct server_info ** info_ary,int count,server_info_t best)6212408d859SMatthew Dillon client_insane(struct server_info **info_ary, int count, server_info_t best)
6222408d859SMatthew Dillon {
6232408d859SMatthew Dillon server_info_t info;
6242408d859SMatthew Dillon double best_offset;
6252408d859SMatthew Dillon double info_offset;
6262408d859SMatthew Dillon int good;
6272408d859SMatthew Dillon int bad;
6282408d859SMatthew Dillon int skip;
6292408d859SMatthew Dillon int quorum;
6302408d859SMatthew Dillon int i;
6312408d859SMatthew Dillon
6322408d859SMatthew Dillon /*
6332408d859SMatthew Dillon * If only one ntp server we cannot check to see if it is insane
6342408d859SMatthew Dillon */
6352408d859SMatthew Dillon if (count < 2)
6362408d859SMatthew Dillon return(1);
6372408d859SMatthew Dillon best_offset = best->lin_sumoffset / best->lin_countoffset;
6382408d859SMatthew Dillon
6392408d859SMatthew Dillon /*
6402408d859SMatthew Dillon * Calculated the quorum. Do not count permanently failed servers
6412408d859SMatthew Dillon * in the calculation.
6422408d859SMatthew Dillon *
6432408d859SMatthew Dillon * adjusted count quorum
6442408d859SMatthew Dillon * 2 2
6452408d859SMatthew Dillon * 3 2
6462408d859SMatthew Dillon * 4 3
6472408d859SMatthew Dillon * 5 3
6482408d859SMatthew Dillon */
6492408d859SMatthew Dillon quorum = count;
6502408d859SMatthew Dillon for (i = 0; i < count; ++i) {
6512408d859SMatthew Dillon info = info_ary[i];
6522408d859SMatthew Dillon if (info->server_state == -2)
6532408d859SMatthew Dillon --quorum;
6542408d859SMatthew Dillon }
6552408d859SMatthew Dillon
6562408d859SMatthew Dillon quorum = quorum / 2 + 1;
6572408d859SMatthew Dillon good = 0;
6582408d859SMatthew Dillon bad = 0;
6592408d859SMatthew Dillon skip = 0;
6602408d859SMatthew Dillon
6612408d859SMatthew Dillon /*
6625e13edd7SMatthew Dillon * Find the good, the bad, and the ugly. We need at least four samples
6635e13edd7SMatthew Dillon * and a stddev within the deviation being checked to count a server
6645e13edd7SMatthew Dillon * in the calculation.
6652408d859SMatthew Dillon */
6662408d859SMatthew Dillon for (i = 0; i < count; ++i) {
6672408d859SMatthew Dillon info = info_ary[i];
6685e13edd7SMatthew Dillon if (info->lin_countoffset < 4 ||
6695e13edd7SMatthew Dillon info->lin_cache_stddev > insane_deviation
6705e13edd7SMatthew Dillon ) {
6712408d859SMatthew Dillon ++skip;
6722408d859SMatthew Dillon continue;
6732408d859SMatthew Dillon }
6745e13edd7SMatthew Dillon
6752408d859SMatthew Dillon info_offset = info->lin_sumoffset / info->lin_countoffset;
6762408d859SMatthew Dillon info_offset -= best_offset;
6775e13edd7SMatthew Dillon if (info_offset < -insane_deviation || info_offset > insane_deviation)
6782408d859SMatthew Dillon ++bad;
6792408d859SMatthew Dillon else
6802408d859SMatthew Dillon ++good;
6812408d859SMatthew Dillon }
6822408d859SMatthew Dillon
6832408d859SMatthew Dillon /*
6842408d859SMatthew Dillon * Did we meet our quorum?
6852408d859SMatthew Dillon */
6865e13edd7SMatthew Dillon logdebuginfo(best, 5, "insanecheck good=%d bad=%d skip=%d "
6875e13edd7SMatthew Dillon "quorum=%d (allowed=%-+8.6f)\n",
6885e13edd7SMatthew Dillon good, bad, skip, quorum, insane_deviation);
6892408d859SMatthew Dillon if (good >= quorum)
6902408d859SMatthew Dillon return(1);
6912408d859SMatthew Dillon if (good + skip >= quorum)
6922408d859SMatthew Dillon return(0);
6932408d859SMatthew Dillon return(-1);
6942408d859SMatthew Dillon }
695fdd4337fSMatthew Dillon
696fdd4337fSMatthew Dillon /*
697e0163549SMatthew Dillon * Linear regression.
698e0163549SMatthew Dillon *
699e0163549SMatthew Dillon * ltv local time as of when the offset error was calculated between
700e0163549SMatthew Dillon * local time and remote time.
701e0163549SMatthew Dillon *
702e0163549SMatthew Dillon * lbtv base time as of when local time was obtained. Used to
703e0163549SMatthew Dillon * calculate the cumulative corrections made to the system's
704e0163549SMatthew Dillon * real time clock so we can de-correct the offset for the
705e0163549SMatthew Dillon * linear regression.
706e0163549SMatthew Dillon *
707e0163549SMatthew Dillon * X is the time axis, in seconds.
708e0163549SMatthew Dillon * Y is the uncorrected offset, in seconds.
709e0163549SMatthew Dillon */
710e0163549SMatthew Dillon void
lin_regress(server_info_t info,struct timeval * ltv,struct timeval * lbtv,double offset,int calc_offset_correction)711e0163549SMatthew Dillon lin_regress(server_info_t info, struct timeval *ltv, struct timeval *lbtv,
712c32f1362SMatthew Dillon double offset, int calc_offset_correction)
713e0163549SMatthew Dillon {
714e0163549SMatthew Dillon double time_axis;
715e0163549SMatthew Dillon double uncorrected_offset;
716e0163549SMatthew Dillon
7178e4e9813SMatthew Dillon /*
7188e4e9813SMatthew Dillon * De-correcting the offset:
7198e4e9813SMatthew Dillon *
7208e4e9813SMatthew Dillon * The passed offset is (our_real_time - remote_real_time). To remove
7218e4e9813SMatthew Dillon * corrections from our_real_time we take the difference in the basetime
7228e4e9813SMatthew Dillon * (new_base_time - old_base_time) and subtract that from the offset.
7238e4e9813SMatthew Dillon * That is, if the basetime goesup, the uncorrected offset goes down.
7248e4e9813SMatthew Dillon */
725e0163549SMatthew Dillon if (info->lin_count == 0) {
726e0163549SMatthew Dillon info->lin_tv = *ltv;
727e0163549SMatthew Dillon info->lin_btv = *lbtv;
728e0163549SMatthew Dillon time_axis = 0;
729e0163549SMatthew Dillon uncorrected_offset = offset;
730e0163549SMatthew Dillon } else {
7318e4e9813SMatthew Dillon time_axis = tv_delta_double(&info->lin_tv, ltv);
7328e4e9813SMatthew Dillon uncorrected_offset = offset - tv_delta_double(&info->lin_btv, lbtv);
733e0163549SMatthew Dillon }
7348e4e9813SMatthew Dillon
7358e4e9813SMatthew Dillon /*
7368e4e9813SMatthew Dillon * We have to use the uncorrected offset for frequency calculations.
7378e4e9813SMatthew Dillon */
738e0163549SMatthew Dillon ++info->lin_count;
739e0163549SMatthew Dillon info->lin_sumx += time_axis;
740e0163549SMatthew Dillon info->lin_sumx2 += time_axis * time_axis;
741e0163549SMatthew Dillon info->lin_sumy += uncorrected_offset;
742e0163549SMatthew Dillon info->lin_sumy2 += uncorrected_offset * uncorrected_offset;
743e0163549SMatthew Dillon info->lin_sumxy += time_axis * uncorrected_offset;
744e0163549SMatthew Dillon
745e0163549SMatthew Dillon /*
7468e4e9813SMatthew Dillon * We have to use the corrected offset for offset calculations.
747e0163549SMatthew Dillon */
748c32f1362SMatthew Dillon if (calc_offset_correction) {
7498e4e9813SMatthew Dillon ++info->lin_countoffset;
7508e4e9813SMatthew Dillon info->lin_sumoffset += offset;
7518e4e9813SMatthew Dillon info->lin_sumoffset2 += offset * offset;
752c32f1362SMatthew Dillon }
753e0163549SMatthew Dillon
7548e4e9813SMatthew Dillon /*
7558e4e9813SMatthew Dillon * Calculate various derived values. This gets us slope, y-intercept,
7563f5e28f4SSascha Wildner * and correlation from the linear regression.
7578e4e9813SMatthew Dillon */
7589b724d8cSMatthew Dillon if (info->lin_count > 1) {
759e0163549SMatthew Dillon info->lin_cache_slope =
760e0163549SMatthew Dillon (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
761e0163549SMatthew Dillon (info->lin_count * info->lin_sumx2 - info->lin_sumx * info->lin_sumx);
762e0163549SMatthew Dillon
763e0163549SMatthew Dillon info->lin_cache_yint =
764e0163549SMatthew Dillon (info->lin_sumy - info->lin_cache_slope * info->lin_sumx) /
765e0163549SMatthew Dillon (info->lin_count);
766e0163549SMatthew Dillon
767e0163549SMatthew Dillon info->lin_cache_corr =
768e0163549SMatthew Dillon (info->lin_count * info->lin_sumxy - info->lin_sumx * info->lin_sumy) /
769e0163549SMatthew Dillon sqrt((info->lin_count * info->lin_sumx2 -
770e0163549SMatthew Dillon info->lin_sumx * info->lin_sumx) *
771e0163549SMatthew Dillon (info->lin_count * info->lin_sumy2 -
772e0163549SMatthew Dillon info->lin_sumy * info->lin_sumy)
773e0163549SMatthew Dillon );
7749b724d8cSMatthew Dillon }
775e0163549SMatthew Dillon
776e0163549SMatthew Dillon /*
7778e4e9813SMatthew Dillon * Calculate more derived values. This gets us the standard-deviation
7788e4e9813SMatthew Dillon * of offsets. The standard deviation approximately means that 68%
7798e4e9813SMatthew Dillon * of the samples fall within the calculated stddev of the mean.
7808e4e9813SMatthew Dillon */
7818e4e9813SMatthew Dillon if (info->lin_countoffset > 1) {
7828e4e9813SMatthew Dillon info->lin_cache_stddev =
7838e4e9813SMatthew Dillon sqrt((info->lin_sumoffset2 -
7848e4e9813SMatthew Dillon ((info->lin_sumoffset * info->lin_sumoffset /
7858e4e9813SMatthew Dillon info->lin_countoffset))) /
7868e4e9813SMatthew Dillon (info->lin_countoffset - 1.0));
7878e4e9813SMatthew Dillon }
7888e4e9813SMatthew Dillon
7898e4e9813SMatthew Dillon /*
7908e4e9813SMatthew Dillon * Save the most recent offset, we might use it in the future.
7918e4e9813SMatthew Dillon * Save the frequency correction (we might scale the slope later so
7928e4e9813SMatthew Dillon * we have a separate field for the actual frequency correction in
7938e4e9813SMatthew Dillon * seconds per second).
794e0163549SMatthew Dillon */
795e0163549SMatthew Dillon info->lin_cache_offset = offset;
796e0163549SMatthew Dillon info->lin_cache_freq = info->lin_cache_slope;
797e0163549SMatthew Dillon
7983d15852bSMatthew Dillon if (debug_level >= 4) {
7994709be8cSMatthew Dillon logdebuginfo(info, 4, "iter=%2d time=%7.3f off=%+.6f uoff=%+.6f",
8009b724d8cSMatthew Dillon (int)info->lin_count,
8019b724d8cSMatthew Dillon time_axis, offset, uncorrected_offset);
8029b724d8cSMatthew Dillon if (info->lin_count > 1) {
8034709be8cSMatthew Dillon logdebug(4, " slope %+7.6f"
8044709be8cSMatthew Dillon " yint %+3.2f corr %+7.6f freq_ppm %+4.2f",
805e0163549SMatthew Dillon info->lin_cache_slope,
806e0163549SMatthew Dillon info->lin_cache_yint,
807e0163549SMatthew Dillon info->lin_cache_corr,
808e0163549SMatthew Dillon info->lin_cache_freq * 1000000.0);
809e0163549SMatthew Dillon }
8108e4e9813SMatthew Dillon if (info->lin_countoffset > 1) {
8113d15852bSMatthew Dillon logdebug(4, " stddev %7.6f", info->lin_cache_stddev);
812c32f1362SMatthew Dillon } else if (calc_offset_correction == 0) {
813c32f1362SMatthew Dillon /* cannot calculate offset correction due to prior correction */
814c32f1362SMatthew Dillon logdebug(4, " offset_ignored");
8158e4e9813SMatthew Dillon }
8163d15852bSMatthew Dillon logdebug(4, "\n");
8179b724d8cSMatthew Dillon }
818e0163549SMatthew Dillon }
819e0163549SMatthew Dillon
8208e4e9813SMatthew Dillon /*
8218e4e9813SMatthew Dillon * Reset the linear regression data. The info structure will not again be
8228e4e9813SMatthew Dillon * a candidate for frequency or offset correction until sufficient data
8238e4e9813SMatthew Dillon * has been accumulated to make a decision.
8248e4e9813SMatthew Dillon */
825e0163549SMatthew Dillon void
lin_reset(server_info_t info)826e0163549SMatthew Dillon lin_reset(server_info_t info)
827e0163549SMatthew Dillon {
828fdd4337fSMatthew Dillon server_info_t scan;
829fdd4337fSMatthew Dillon
830e0163549SMatthew Dillon info->lin_count = 0;
831e0163549SMatthew Dillon info->lin_sumx = 0;
832e0163549SMatthew Dillon info->lin_sumy = 0;
833e0163549SMatthew Dillon info->lin_sumxy = 0;
834e0163549SMatthew Dillon info->lin_sumx2 = 0;
835e0163549SMatthew Dillon info->lin_sumy2 = 0;
8368e4e9813SMatthew Dillon
8378e4e9813SMatthew Dillon info->lin_countoffset = 0;
838e0163549SMatthew Dillon info->lin_sumoffset = 0;
8398e4e9813SMatthew Dillon info->lin_sumoffset2 = 0;
840e0163549SMatthew Dillon
841e0163549SMatthew Dillon info->lin_cache_slope = 0;
842e0163549SMatthew Dillon info->lin_cache_yint = 0;
843e0163549SMatthew Dillon info->lin_cache_corr = 0;
844e0163549SMatthew Dillon info->lin_cache_offset = 0;
845e0163549SMatthew Dillon info->lin_cache_freq = 0;
846fdd4337fSMatthew Dillon
847fdd4337fSMatthew Dillon /*
848fdd4337fSMatthew Dillon * Destroy any additional alternative regressions.
849fdd4337fSMatthew Dillon */
850fdd4337fSMatthew Dillon while ((scan = info->altinfo) != NULL) {
851fdd4337fSMatthew Dillon info->altinfo = scan->altinfo;
852fdd4337fSMatthew Dillon free(scan);
853fdd4337fSMatthew Dillon }
854e0163549SMatthew Dillon }
855e0163549SMatthew Dillon
8568e4e9813SMatthew Dillon /*
8578e4e9813SMatthew Dillon * Sometimes we want to clean out the offset calculations without
8588e4e9813SMatthew Dillon * destroying the linear regression used to figure out the frequency
8598e4e9813SMatthew Dillon * correction. This usually occurs whenever we issue an offset
8608e4e9813SMatthew Dillon * adjustment to the system, which invalidates any offset data accumulated
8618e4e9813SMatthew Dillon * up to that point.
8628e4e9813SMatthew Dillon */
8638e4e9813SMatthew Dillon void
lin_resetalloffsets(struct server_info ** info_ary,int count)8648e4e9813SMatthew Dillon lin_resetalloffsets(struct server_info **info_ary, int count)
8658e4e9813SMatthew Dillon {
8668e4e9813SMatthew Dillon server_info_t info;
8678e4e9813SMatthew Dillon int i;
8688e4e9813SMatthew Dillon
8698e4e9813SMatthew Dillon for (i = 0; i < count; ++i) {
8708e4e9813SMatthew Dillon for (info = info_ary[i]; info; info = info->altinfo)
8718e4e9813SMatthew Dillon lin_resetoffsets(info);
8728e4e9813SMatthew Dillon }
8738e4e9813SMatthew Dillon }
8748e4e9813SMatthew Dillon
8758e4e9813SMatthew Dillon void
lin_resetoffsets(server_info_t info)8768e4e9813SMatthew Dillon lin_resetoffsets(server_info_t info)
8778e4e9813SMatthew Dillon {
8788e4e9813SMatthew Dillon info->lin_countoffset = 0;
8798e4e9813SMatthew Dillon info->lin_sumoffset = 0;
8808e4e9813SMatthew Dillon info->lin_sumoffset2 = 0;
8818e4e9813SMatthew Dillon }
8828e4e9813SMatthew Dillon
8832408d859SMatthew Dillon void
client_setserverstate(server_info_t info,int state,const char * str)8842408d859SMatthew Dillon client_setserverstate(server_info_t info, int state, const char *str)
8852408d859SMatthew Dillon {
8862408d859SMatthew Dillon if (info->server_state != state) {
8872408d859SMatthew Dillon info->server_state = state;
8882408d859SMatthew Dillon logdebuginfo(info, 1, "%s\n", str);
8892408d859SMatthew Dillon }
8902408d859SMatthew Dillon }
8912408d859SMatthew Dillon
892