summaryrefslogtreecommitdiffstats
path: root/contrib/ntp/libntp/systime.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/libntp/systime.c')
-rw-r--r--contrib/ntp/libntp/systime.c338
1 files changed, 211 insertions, 127 deletions
diff --git a/contrib/ntp/libntp/systime.c b/contrib/ntp/libntp/systime.c
index 29f1e86..8e70897 100644
--- a/contrib/ntp/libntp/systime.c
+++ b/contrib/ntp/libntp/systime.c
@@ -5,8 +5,10 @@
*
*/
#include <config.h>
+#include <math.h>
#include "ntp.h"
+#include "ntpd.h"
#include "ntp_syslog.h"
#include "ntp_stdlib.h"
#include "ntp_random.h"
@@ -14,6 +16,7 @@
#include "timevalops.h"
#include "timespecops.h"
#include "ntp_calendar.h"
+#include "lib_strbuf.h"
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
@@ -28,6 +31,9 @@
int allow_panic = FALSE; /* allow panic correction (-g) */
int enable_panic_check = TRUE; /* Can we check allow_panic's state? */
+u_long sys_lamport; /* Lamport violation */
+u_long sys_tsrounding; /* timestamp rounding errors */
+
#ifndef USE_COMPILETIME_PIVOT
# define USE_COMPILETIME_PIVOT 1
#endif
@@ -110,7 +116,10 @@ set_sys_fuzz(
sys_fuzz = fuzz_val;
INSIST(sys_fuzz >= 0);
INSIST(sys_fuzz <= 1.0);
- sys_fuzz_nsec = (long)(sys_fuzz * 1e9 + 0.5);
+ /* [Bug 3450] ensure nsec fuzz >= sys_fuzz to reduce chance of
+ * short-falling fuzz advance
+ */
+ sys_fuzz_nsec = (long)ceil(sys_fuzz * 1e9);
}
@@ -168,13 +177,10 @@ get_systime(
static struct timespec ts_last; /* last sampled os time */
static struct timespec ts_prev; /* prior os time */
static l_fp lfp_prev; /* prior result */
- static double dfuzz_prev; /* prior fuzz */
struct timespec ts; /* seconds and nanoseconds */
struct timespec ts_min; /* earliest permissible */
struct timespec ts_lam; /* lamport fictional increment */
- struct timespec ts_prev_log; /* for msyslog only */
double dfuzz;
- double ddelta;
l_fp result;
l_fp lfpfuzz;
l_fp lfpdelta;
@@ -191,8 +197,10 @@ get_systime(
* introduce small steps backward. It should not be an issue on
* systems where get_ostime() results in a true syscall.)
*/
- if (cmp_tspec(add_tspec_ns(ts, 50000000), ts_last) < 0)
+ if (cmp_tspec(add_tspec_ns(ts, 50000000), ts_last) < 0) {
lamport_violated = 1;
+ sys_lamport++;
+ }
ts_last = ts;
/*
@@ -216,21 +224,16 @@ get_systime(
if (!lamport_violated)
ts = ts_min;
}
- ts_prev_log = ts_prev;
ts_prev = ts;
- } else {
- /*
- * Quiet "ts_prev_log.tv_sec may be used uninitialized"
- * warning from x86 gcc 4.5.2.
- */
- ZERO(ts_prev_log);
}
/* convert from timespec to l_fp fixed-point */
result = tspec_stamp_to_lfp(ts);
/*
- * Add in the fuzz.
+ * Add in the fuzz. 'ntp_random()' returns [0..2**31-1] so we
+ * must scale up the result by 2.0 to cover the full fractional
+ * range.
*/
dfuzz = ntp_random() * 2. / FRAC * sys_fuzz;
DTOLFP(dfuzz, &lfpfuzz);
@@ -240,30 +243,34 @@ get_systime(
* Ensure result is strictly greater than prior result (ignoring
* sys_residual's effect for now) once sys_fuzz has been
* determined.
+ *
+ * [Bug 3450] Rounding errors and time slew can lead to a
+ * violation of the expected postcondition. This is bound to
+ * happen from time to time (depending on state of the random
+ * generator, the current slew and the closeness of system time
+ * stamps drawn) and does not warrant a syslog entry. Instead it
+ * makes much more sense to ensure the postcondition and hop
+ * along silently.
*/
if (!USING_SIGIO()) {
- if (!L_ISZERO(&lfp_prev) && !lamport_violated) {
- if (!L_ISGTU(&result, &lfp_prev) &&
- sys_fuzz > 0.) {
- msyslog(LOG_ERR, "ts_prev %s ts_min %s",
- tspectoa(ts_prev_log),
- tspectoa(ts_min));
- msyslog(LOG_ERR, "ts %s", tspectoa(ts));
- msyslog(LOG_ERR, "sys_fuzz %ld nsec, prior fuzz %.9f",
- sys_fuzz_nsec, dfuzz_prev);
- msyslog(LOG_ERR, "this fuzz %.9f",
- dfuzz);
- lfpdelta = lfp_prev;
- L_SUB(&lfpdelta, &result);
- LFPTOD(&lfpdelta, ddelta);
- msyslog(LOG_ERR,
- "prev get_systime 0x%x.%08x is %.9f later than 0x%x.%08x",
- lfp_prev.l_ui, lfp_prev.l_uf,
- ddelta, result.l_ui, result.l_uf);
+ if ( !L_ISZERO(&lfp_prev)
+ && !lamport_violated
+ && (sys_fuzz > 0.0)
+ ) {
+ lfpdelta = result;
+ L_SUB(&lfpdelta, &lfp_prev);
+ L_SUBUF(&lfpdelta, 1);
+ if (lfpdelta.l_i < 0)
+ {
+ L_NEG(&lfpdelta);
+ DPRINTF(1, ("get_systime: postcond failed by %s secs, fixed\n",
+ lfptoa(&lfpdelta, 9)));
+ result = lfp_prev;
+ L_ADDUF(&result, 1);
+ sys_tsrounding++;
}
}
lfp_prev = result;
- dfuzz_prev = dfuzz;
if (lamport_violated)
lamport_violated = FALSE;
}
@@ -362,105 +369,16 @@ adj_systime(
}
#endif
-
/*
- * step_systime - step the system clock.
+ * helper to keep utmp/wtmp up to date
*/
-
-int
-step_systime(
- double step
+static void
+update_uwtmp(
+ struct timeval timetv,
+ struct timeval tvlast
)
{
- time_t pivot; /* for ntp era unfolding */
- struct timeval timetv, tvlast, tvdiff;
- struct timespec timets;
- struct calendar jd;
- l_fp fp_ofs, fp_sys; /* offset and target system time in FP */
-
- /*
- * Get pivot time for NTP era unfolding. Since we don't step
- * very often, we can afford to do the whole calculation from
- * scratch. And we're not in the time-critical path yet.
- */
-#if SIZEOF_TIME_T > 4
- /*
- * This code makes sure the resulting time stamp for the new
- * system time is in the 2^32 seconds starting at 1970-01-01,
- * 00:00:00 UTC.
- */
- pivot = 0x80000000;
-#if USE_COMPILETIME_PIVOT
- /*
- * Add the compile time minus 10 years to get a possible target
- * area of (compile time - 10 years) to (compile time + 126
- * years). This should be sufficient for a given binary of
- * NTPD.
- */
- if (ntpcal_get_build_date(&jd)) {
- jd.year -= 10;
- pivot += ntpcal_date_to_time(&jd);
- } else {
- msyslog(LOG_ERR,
- "step-systime: assume 1970-01-01 as build date");
- }
-#else
- UNUSED_LOCAL(jd);
-#endif /* USE_COMPILETIME_PIVOT */
-#else
- UNUSED_LOCAL(jd);
- /* This makes sure the resulting time stamp is on or after
- * 1969-12-31/23:59:59 UTC and gives us additional two years,
- * from the change of NTP era in 2036 to the UNIX rollover in
- * 2038. (Minus one second, but that won't hurt.) We *really*
- * need a longer 'time_t' after that! Or a different baseline,
- * but that would cause other serious trouble, too.
- */
- pivot = 0x7FFFFFFF;
-#endif
-
- /* get the complete jump distance as l_fp */
- DTOLFP(sys_residual, &fp_sys);
- DTOLFP(step, &fp_ofs);
- L_ADD(&fp_ofs, &fp_sys);
-
- /* ---> time-critical path starts ---> */
-
- /* get the current time as l_fp (without fuzz) and as struct timeval */
- get_ostime(&timets);
- fp_sys = tspec_stamp_to_lfp(timets);
- tvlast.tv_sec = timets.tv_sec;
- tvlast.tv_usec = (timets.tv_nsec + 500) / 1000;
-
- /* get the target time as l_fp */
- L_ADD(&fp_sys, &fp_ofs);
-
- /* unfold the new system time */
- timetv = lfp_stamp_to_tval(fp_sys, &pivot);
-
- /* now set new system time */
- if (ntp_set_tod(&timetv, NULL) != 0) {
- msyslog(LOG_ERR, "step-systime: %m");
- if (enable_panic_check && allow_panic) {
- msyslog(LOG_ERR, "step_systime: allow_panic is TRUE!");
- }
- return FALSE;
- }
-
- /* <--- time-critical path ended with 'ntp_set_tod()' <--- */
-
- sys_residual = 0;
- lamport_violated = (step < 0);
- if (step_callback)
- (*step_callback)();
-
-#ifdef NEED_HPUX_ADJTIME
- /*
- * CHECKME: is this correct when called by ntpdate?????
- */
- _clear_adjtime();
-#endif
-
+ struct timeval tvdiff;
/*
* FreeBSD, for example, has:
* struct utmp {
@@ -589,6 +507,83 @@ step_systime(
#endif /* UPDATE_WTMPX */
}
+}
+
+/*
+ * step_systime - step the system clock.
+ */
+
+int
+step_systime(
+ double step
+ )
+{
+ time_t pivot; /* for ntp era unfolding */
+ struct timeval timetv, tvlast;
+ struct timespec timets;
+ l_fp fp_ofs, fp_sys; /* offset and target system time in FP */
+
+ /*
+ * Get pivot time for NTP era unfolding. Since we don't step
+ * very often, we can afford to do the whole calculation from
+ * scratch. And we're not in the time-critical path yet.
+ */
+#if SIZEOF_TIME_T > 4
+ pivot = basedate_get_eracenter();
+#else
+ /* This makes sure the resulting time stamp is on or after
+ * 1969-12-31/23:59:59 UTC and gives us additional two years,
+ * from the change of NTP era in 2036 to the UNIX rollover in
+ * 2038. (Minus one second, but that won't hurt.) We *really*
+ * need a longer 'time_t' after that! Or a different baseline,
+ * but that would cause other serious trouble, too.
+ */
+ pivot = 0x7FFFFFFF;
+#endif
+
+ /* get the complete jump distance as l_fp */
+ DTOLFP(sys_residual, &fp_sys);
+ DTOLFP(step, &fp_ofs);
+ L_ADD(&fp_ofs, &fp_sys);
+
+ /* ---> time-critical path starts ---> */
+
+ /* get the current time as l_fp (without fuzz) and as struct timeval */
+ get_ostime(&timets);
+ fp_sys = tspec_stamp_to_lfp(timets);
+ tvlast.tv_sec = timets.tv_sec;
+ tvlast.tv_usec = (timets.tv_nsec + 500) / 1000;
+
+ /* get the target time as l_fp */
+ L_ADD(&fp_sys, &fp_ofs);
+
+ /* unfold the new system time */
+ timetv = lfp_stamp_to_tval(fp_sys, &pivot);
+
+ /* now set new system time */
+ if (ntp_set_tod(&timetv, NULL) != 0) {
+ msyslog(LOG_ERR, "step-systime: %m");
+ if (enable_panic_check && allow_panic) {
+ msyslog(LOG_ERR, "step_systime: allow_panic is TRUE!");
+ }
+ return FALSE;
+ }
+
+ /* <--- time-critical path ended with 'ntp_set_tod()' <--- */
+
+ sys_residual = 0;
+ lamport_violated = (step < 0);
+ if (step_callback)
+ (*step_callback)();
+
+#ifdef NEED_HPUX_ADJTIME
+ /*
+ * CHECKME: is this correct when called by ntpdate?????
+ */
+ _clear_adjtime();
+#endif
+
+ update_uwtmp(timetv, tvlast);
if (enable_panic_check && allow_panic) {
msyslog(LOG_ERR, "step_systime: allow_panic is TRUE!");
INSIST(!allow_panic);
@@ -596,4 +591,93 @@ step_systime(
return TRUE;
}
+static const char *
+tv_fmt_libbuf(
+ const struct timeval * ptv
+ )
+{
+ char * retv;
+ vint64 secs;
+ ntpcal_split dds;
+ struct calendar jd;
+
+ secs = time_to_vint64(&ptv->tv_sec);
+ dds = ntpcal_daysplit(&secs);
+ ntpcal_daysplit_to_date(&jd, &dds, DAY_UNIX_STARTS);
+ LIB_GETBUF(retv);
+ snprintf(retv, LIB_BUFLENGTH,
+ "%04hu-%02hu-%02hu/%02hu:%02hu:%02hu.%06u",
+ jd.year, (u_short)jd.month, (u_short)jd.monthday,
+ (u_short)jd.hour, (u_short)jd.minute, (u_short)jd.second,
+ (u_int)ptv->tv_usec);
+ return retv;
+}
+
+
+int /*BOOL*/
+clamp_systime(void)
+{
+#if SIZEOF_TIME_T > 4
+
+ struct timeval timetv, tvlast;
+ struct timespec timets;
+ uint32_t tdiff;
+
+
+ timetv.tv_sec = basedate_get_erabase();
+
+ /* ---> time-critical path starts ---> */
+
+ /* get the current time as l_fp (without fuzz) and as struct timeval */
+ get_ostime(&timets);
+ tvlast.tv_sec = timets.tv_sec;
+ tvlast.tv_usec = (timets.tv_nsec + 500) / 1000;
+ if (tvlast.tv_usec >= 1000000) {
+ tvlast.tv_usec -= 1000000;
+ tvlast.tv_sec += 1;
+ }
+ timetv.tv_usec = tvlast.tv_usec;
+
+ tdiff = (uint32_t)(tvlast.tv_sec & UINT32_MAX) -
+ (uint32_t)(timetv.tv_sec & UINT32_MAX);
+ timetv.tv_sec += tdiff;
+ if (timetv.tv_sec != tvlast.tv_sec) {
+ /* now set new system time */
+ if (ntp_set_tod(&timetv, NULL) != 0) {
+ msyslog(LOG_ERR, "clamp-systime: %m");
+ return FALSE;
+ }
+ } else {
+ msyslog(LOG_INFO,
+ "clamp-systime: clock (%s) in allowed range",
+ tv_fmt_libbuf(&timetv));
+ return FALSE;
+ }
+
+ /* <--- time-critical path ended with 'ntp_set_tod()' <--- */
+
+ sys_residual = 0;
+ lamport_violated = (timetv.tv_sec < tvlast.tv_sec);
+ if (step_callback)
+ (*step_callback)();
+
+# ifdef NEED_HPUX_ADJTIME
+ /*
+ * CHECKME: is this correct when called by ntpdate?????
+ */
+ _clear_adjtime();
+# endif
+
+ update_uwtmp(timetv, tvlast);
+ msyslog(LOG_WARNING,
+ "clamp-systime: clock stepped from %s to %s!",
+ tv_fmt_libbuf(&tvlast), tv_fmt_libbuf(&timetv));
+ return TRUE;
+
+#else
+
+ return 0;
+#endif
+}
+
#endif /* !SIM */
OpenPOWER on IntegriCloud