X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Ftimedate%2Ftimedate-sntp.c;h=a85b37775290146f5afefab2cd5caa00b4ed9ef8;hb=2f1a3d086860b7cff659e425ea16d8bb34dc62ac;hp=98a21721982eaf1fdf2b33ae871dbfaa3705206b;hpb=7b41586761d6173cf6596b84a55d6b036662e068;p=elogind.git diff --git a/src/timedate/timedate-sntp.c b/src/timedate/timedate-sntp.c index 98a217219..a85b37775 100644 --- a/src/timedate/timedate-sntp.c +++ b/src/timedate/timedate-sntp.c @@ -59,24 +59,24 @@ #define ADJ_SETOFFSET 0x0100 /* add 'time' to current time */ #endif -/* Maximum delta in seconds which the system clock is gradually adjusted - * to approach the network time. Deltas larger that this are set by letting - * the system time jump. The maximum for adjtime is 500ms. - */ -#define NTP_MAX_ADJUST 0.2 +/* expected accuracy of time synchronization; used to adjust the poll interval */ +#define NTP_ACCURACY_SEC 0.2 /* - * "Define the required accuracy of the system clock, then calculate the - * maximum timeout. Use the longest maximum timeout possible given the system - * constraints to minimize time server aggregate load." - * * "A client MUST NOT under any conditions use a poll interval less * than 15 seconds." */ -#define NTP_POLL_INTERVAL_MIN_SEC 16 +#define NTP_POLL_INTERVAL_MIN_SEC 32 #define NTP_POLL_INTERVAL_MAX_SEC 2048 -#define NTP_ACCURACY_SEC 0.1 +/* + * Maximum delta in seconds which the system clock is gradually adjusted + * (slew) to approach the network time. Deltas larger that this are set by + * letting the system time jump. The kernel's limit for adjtime is 0.5s. + */ +#define NTP_MAX_ADJUST 0.4 + +/* NTP protocol, packet header */ #define NTP_LEAP_PLUSSEC 1 #define NTP_LEAP_MINUSSEC 2 #define NTP_LEAP_NOTINSYNC 3 @@ -118,6 +118,8 @@ struct ntp_msg { } _packed_; struct SNTPContext { + void (*report)(usec_t poll, double offset, double delay, double jitter, bool spike); + /* peer */ sd_event_source *event_receive; char *server; @@ -133,10 +135,10 @@ struct SNTPContext { /* poll timer */ sd_event_source *event_timer; - usec_t poll_interval; + usec_t poll_interval_usec; bool poll_resync; - /* statistics data */ + /* history data */ struct { double offset; double delay; @@ -203,7 +205,7 @@ static int sntp_send_request(SNTPContext *sntp) { * matching answer to our request. * * The actual value does not matter, We do not care about the correct - * NTP UINT_MAX fraction, we just pass the plain nanosecond value. + * NTP UINT_MAX fraction; we just pass the plain nanosecond value. */ clock_gettime(CLOCK_MONOTONIC, &sntp->trans_time_mon); clock_gettime(CLOCK_REALTIME, &sntp->trans_time); @@ -218,7 +220,7 @@ static int sntp_send_request(SNTPContext *sntp) { sntp->pending = true; log_debug("Sent NTP request to: %s", sntp->server); } else - log_info("Sending NTP request to %s failed: %m", sntp->server); + log_debug("Sending NTP request to %s failed: %m", sntp->server); /* re-arm timer with incresing timeout, in case the packets never arrive back */ if (sntp->retry_interval > 0) { @@ -263,7 +265,12 @@ static int sntp_arm_timer(SNTPContext *sntp, usec_t next) { } e = sd_event_source_get_event(sntp->event_receive); - r = sd_event_add_monotonic(e, &sntp->event_timer, now(CLOCK_MONOTONIC) + next, 0, sntp_timer, sntp); + r = sd_event_add_time( + e, + &sntp->event_timer, + CLOCK_MONOTONIC, + now(CLOCK_MONOTONIC) + next, 0, + sntp_timer, sntp); if (r < 0) return r; @@ -286,7 +293,7 @@ static int sntp_clock_watch(sd_event_source *source, int fd, uint32_t revents, v } /* resync */ - log_info("System time changed, resyncing."); + log_info("System time changed. Resyncing."); sntp->poll_resync = true; sntp_send_request(sntp); @@ -345,14 +352,12 @@ static int sntp_adjust_clock(SNTPContext *sntp, double offset, int leap_sec) { * syncs the system time periodically to the hardware clock. */ if (offset < NTP_MAX_ADJUST && offset > -NTP_MAX_ADJUST) { - int constant; - - constant = log2i(sntp->poll_interval / USEC_PER_SEC) - 5; - - tmx.modes |= ADJ_STATUS | ADJ_OFFSET | ADJ_TIMECONST; + tmx.modes |= ADJ_STATUS | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; tmx.status = STA_PLL; tmx.offset = offset * 1000 * 1000; - tmx.constant = constant; + tmx.constant = log2i(sntp->poll_interval_usec / USEC_PER_SEC) - 6; + tmx.maxerror = 0; + tmx.esterror = 0; log_debug(" adjust (slew): %+f sec\n", (double)tmx.offset / USEC_PER_SEC); } else { tmx.modes = ADJ_SETOFFSET; @@ -392,7 +397,13 @@ static int sntp_adjust_clock(SNTPContext *sntp, double offset, int leap_sec) { static bool sntp_sample_spike_detection(SNTPContext *sntp, double offset, double delay) { unsigned int i, idx_cur, idx_new, idx_min; double jitter; - bool spike; + double j; + + sntp->packet_count++; + + /* ignore initial sample */ + if (sntp->packet_count == 1) + return false; /* store the current data in our samples array */ idx_cur = sntp->samples_idx; @@ -401,62 +412,61 @@ static bool sntp_sample_spike_detection(SNTPContext *sntp, double offset, double sntp->samples[idx_new].offset = offset; sntp->samples[idx_new].delay = delay; - sntp->packet_count++; - - /* - * Spike detection; compare the difference between the current offset to - * the previous offset and jitter. - */ - spike = !sntp->poll_resync && - sntp->packet_count > 2 && - fabs(offset - sntp->samples[idx_cur].offset) > sntp->samples_jitter * 3; - /* calculate new jitter value from the RMS differences relative to the lowest delay sample */ + jitter = sntp->samples_jitter; for (idx_min = idx_cur, i = 0; i < ELEMENTSOF(sntp->samples); i++) if (sntp->samples[i].delay > 0 && sntp->samples[i].delay < sntp->samples[idx_min].delay) idx_min = i; - for (jitter = 0, i = 0; i < ELEMENTSOF(sntp->samples); i++) - jitter += square(sntp->samples[i].offset - sntp->samples[idx_min].offset); - sntp->samples_jitter = sqrt(jitter / (ELEMENTSOF(sntp->samples) - 1)); + j = 0; + for (i = 0; i < ELEMENTSOF(sntp->samples); i++) + j += square(sntp->samples[i].offset - sntp->samples[idx_min].offset); + sntp->samples_jitter = sqrt(j / (ELEMENTSOF(sntp->samples) - 1)); + + /* ignore samples when resyncing */ + if (sntp->poll_resync) + return false; + + /* always accept offset if we are farther off than the round-trip delay */ + if (fabs(offset) > delay) + return false; - return spike; + /* we need a few samples before looking at them */ + if (sntp->packet_count < 4) + return false; + + /* do not accept anything worse than the maximum possible error of the best sample */ + if (fabs(offset) > sntp->samples[idx_min].delay) + return true; + + /* compare the difference between the current offset to the previous offset and jitter */ + return fabs(offset - sntp->samples[idx_cur].offset) > 3 * jitter; } static void sntp_adjust_poll(SNTPContext *sntp, double offset, bool spike) { - double delta; - if (sntp->poll_resync) { - sntp->poll_interval = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; + sntp->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; sntp->poll_resync = false; return; } - if (spike) { - if (sntp->poll_interval > NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC) - sntp->poll_interval /= 2; - return; - } - - delta = fabs(offset); - /* set to minimal poll interval */ - if (delta > NTP_ACCURACY_SEC) { - sntp->poll_interval = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; + if (!spike && fabs(offset) > NTP_ACCURACY_SEC) { + sntp->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; return; } /* increase polling interval */ - if (delta < NTP_ACCURACY_SEC * 0.25) { - if (sntp->poll_interval < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) - sntp->poll_interval *= 2; + if (fabs(offset) < NTP_ACCURACY_SEC * 0.25) { + if (sntp->poll_interval_usec < NTP_POLL_INTERVAL_MAX_SEC * USEC_PER_SEC) + sntp->poll_interval_usec *= 2; return; } /* decrease polling interval */ - if (delta > NTP_ACCURACY_SEC * 0.75) { - if (sntp->poll_interval > NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC) - sntp->poll_interval /= 2; + if (spike || fabs(offset) > NTP_ACCURACY_SEC * 0.75) { + if (sntp->poll_interval_usec > NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC) + sntp->poll_interval_usec /= 2; return; } } @@ -482,7 +492,7 @@ static int sntp_receive_response(sd_event_source *source, int fd, uint32_t reven .msg_namelen = sizeof(server_addr), }; struct cmsghdr *cmsg; - struct timespec now; + struct timespec now_ts; struct timeval *recv_time; ssize_t len; struct ntp_msg *ntpmsg; @@ -493,24 +503,24 @@ static int sntp_receive_response(sd_event_source *source, int fd, uint32_t reven int r; if (revents & (EPOLLHUP|EPOLLERR)) { - log_debug("Server connection returned error, closing."); + log_debug("Server connection returned error. Closing."); sntp_server_disconnect(sntp); return -ENOTCONN; } len = recvmsg(fd, &msghdr, MSG_DONTWAIT); if (len < 0) { - log_debug("Error receiving message, disconnecting"); + log_debug("Error receiving message. Disconnecting."); return -EINVAL; } if (iov.iov_len < sizeof(struct ntp_msg)) { - log_debug("Invalid response from server, disconnecting"); + log_debug("Invalid response from server. Disconnecting."); return -EINVAL; } if (sntp->server_addr.sin_addr.s_addr != server_addr.sin_addr.s_addr) { - log_debug("Response from unknown server, disconnecting"); + log_debug("Response from unknown server. Disconnecting."); return -EINVAL; } @@ -526,35 +536,35 @@ static int sntp_receive_response(sd_event_source *source, int fd, uint32_t reven } } if (!recv_time) { - log_debug("Invalid packet timestamp, disconnecting"); + log_debug("Invalid packet timestamp. Disconnecting."); return -EINVAL; } ntpmsg = iov.iov_base; if (!sntp->pending) { - log_debug("Unexpected reply, ignoring"); + log_debug("Unexpected reply. Ignoring."); return 0; } /* check our "time cookie" (we just stored nanoseconds in the fraction field) */ - if (be32toh(ntpmsg->origin_time.sec) != sntp->trans_time.tv_sec + OFFSET_1900_1970|| + if (be32toh(ntpmsg->origin_time.sec) != sntp->trans_time.tv_sec + OFFSET_1900_1970 || be32toh(ntpmsg->origin_time.frac) != sntp->trans_time.tv_nsec) { - log_debug("Invalid reply, not our transmit time, ignoring"); + log_debug("Invalid reply; not our transmit time. Ignoring."); return 0; } if (NTP_FIELD_LEAP(ntpmsg->field) == NTP_LEAP_NOTINSYNC) { - log_debug("Server is not synchronized, disconnecting"); + log_debug("Server is not synchronized. Disconnecting."); return -EINVAL; } if (NTP_FIELD_VERSION(ntpmsg->field) != 4) { - log_debug("Response NTPv%d, disconnecting", NTP_FIELD_VERSION(ntpmsg->field)); + log_debug("Response NTPv%d. Disconnecting.", NTP_FIELD_VERSION(ntpmsg->field)); return -EINVAL; } if (NTP_FIELD_MODE(ntpmsg->field) != NTP_MODE_SERVER) { - log_debug("Unsupported mode %d, disconnecting", NTP_FIELD_MODE(ntpmsg->field)); + log_debug("Unsupported mode %d. Disconnecting.", NTP_FIELD_MODE(ntpmsg->field)); return -EINVAL; } @@ -578,11 +588,11 @@ static int sntp_receive_response(sd_event_source *source, int fd, uint32_t reven * Transmit Timestamp T3 time reply sent by server * Destination Timestamp T4 time reply received by client * - * The roundtrip delay d and system clock offset t are defined as: + * The round-trip delay, d, and system clock offset, t, are defined as: * d = (T4 - T1) - (T3 - T2) t = ((T2 - T1) + (T3 - T4)) / 2" */ - clock_gettime(CLOCK_MONOTONIC, &now); - origin = tv_to_d(recv_time) - (ts_to_d(&now) - ts_to_d(&sntp->trans_time_mon)) + OFFSET_1900_1970; + clock_gettime(CLOCK_MONOTONIC, &now_ts); + origin = tv_to_d(recv_time) - (ts_to_d(&now_ts) - ts_to_d(&sntp->trans_time_mon)) + OFFSET_1900_1970; receive = ntp_ts_to_d(&ntpmsg->recv_time); trans = ntp_ts_to_d(&ntpmsg->trans_time); dest = tv_to_d(recv_time) + OFFSET_1900_1970; @@ -608,7 +618,7 @@ static int sntp_receive_response(sd_event_source *source, int fd, uint32_t reven " offset : %+f sec\n" " delay : %+f sec\n" " packet count : %"PRIu64"\n" - " jitter/spike : %f (%s)\n" + " jitter : %f%s\n" " poll interval: %llu\n", NTP_FIELD_LEAP(ntpmsg->field), NTP_FIELD_VERSION(ntpmsg->field), @@ -622,11 +632,11 @@ static int sntp_receive_response(sd_event_source *source, int fd, uint32_t reven dest - OFFSET_1900_1970, offset, delay, sntp->packet_count, - sntp->samples_jitter, spike ? "yes" : "no", - sntp->poll_interval / USEC_PER_SEC); + sntp->samples_jitter, spike ? " spike" : "", + sntp->poll_interval_usec / USEC_PER_SEC); - log_info("%4llu %+10f %10f %10f %s", - sntp->poll_interval / USEC_PER_SEC, offset, delay, sntp->samples_jitter, spike ? "spike" : ""); + if (sntp->report) + sntp->report(sntp->poll_interval_usec, offset, delay, sntp->samples_jitter, spike); if (!spike) { r = sntp_adjust_clock(sntp, offset, leap_sec); @@ -634,7 +644,7 @@ static int sntp_receive_response(sd_event_source *source, int fd, uint32_t reven log_error("Failed to call clock_adjtime(): %m"); } - r = sntp_arm_timer(sntp, sntp->poll_interval); + r = sntp_arm_timer(sntp, sntp->poll_interval_usec); if (r < 0) return r; @@ -660,7 +670,7 @@ int sntp_server_connect(SNTPContext *sntp, const char *server) { sntp->server_addr.sin_family = AF_INET; sntp->server_addr.sin_addr.s_addr = inet_addr(server); - sntp->poll_interval = 2 * NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; + sntp->poll_interval_usec = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; return sntp_send_request(sntp); } @@ -721,6 +731,10 @@ static int sntp_listen_setup(SNTPContext *sntp, sd_event *e) { return 0; } +void sntp_report_register(SNTPContext *sntp, void (*report)(usec_t poll_usec, double offset, double delay, double jitter, bool spike)) { + sntp->report = report; +} + int sntp_new(SNTPContext **sntp, sd_event *e) { _cleanup_free_ SNTPContext *c; int r;