#define M_ISNEG(v_i, v_f) /* v < 0 */ \
(((v_i) & 0x80000000u) != 0)
+#define M_ISGTU(a_i, a_f, b_i, b_f) /* a > b unsigned */ \
+ (((u_int32)(a_i)) > ((u_int32)(b_i)) || \
+ ((a_i) == (b_i) && ((u_int32)(a_f)) > ((u_int32)(b_f))))
+
#define M_ISHIS(a_i, a_f, b_i, b_f) /* a >= b unsigned */ \
(((u_int32)(a_i)) > ((u_int32)(b_i)) || \
((a_i) == (b_i) && ((u_int32)(a_f)) >= ((u_int32)(b_f))))
#define M_ISGEQ(a_i, a_f, b_i, b_f) /* a >= b signed */ \
- (((u_int32)(a_i) - (u_int32)(b_i) + 0x80000000u > 0x80000000u) || \
+ (((u_int32)(a_i) - (u_int32)(b_i) + 0x80000000 > 0x80000000) || \
((a_i) == (b_i) && (u_int32)(a_f) >= (u_int32)(b_f)))
#define M_ISEQU(a_i, a_f, b_i, b_f) /* a == b unsigned */ \
#define L_ISNEG(v) M_ISNEG((v)->l_ui, (v)->l_uf)
#define L_ISZERO(v) (((v)->l_ui | (v)->l_uf) == 0)
+#define L_ISGTU(a, b) M_ISGTU((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
#define L_ISHIS(a, b) M_ISHIS((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
#define L_ISGEQ(a, b) M_ISGEQ((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
#define L_ISEQU(a, b) M_ISEQU((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
extern char * uglydate (l_fp *);
extern void mfp_mul (int32 *, u_int32 *, int32, u_int32, int32, u_int32);
+extern void set_sys_fuzz (double);
extern void get_systime (l_fp *);
extern int step_systime (double);
extern int adj_systime (double);
#endif /* HAVE_UTMPX_H */
-#define FUZZ 500e-6 /* fuzz pivot */
-
#ifndef USE_COMPILETIME_PIVOT
# define USE_COMPILETIME_PIVOT 1
#endif
-#if defined(HAVE_CLOCK_GETTIME)
-# define GET_SYSTIME_AS_TIMESPEC(tsp) clock_gettime(CLOCK_REALTIME, tsp)
-#elif defined(HAVE_GETCLOCK)
-# define GET_SYSTIME_AS_TIMESPEC(tsp) getclock(TIMEOFDAY, tsp)
-#elif !defined(GETTIMEOFDAY)
-# include "bletch: cannot get system time?"
-#endif
-
-
/*
* These routines (get_systime, step_systime, adj_systime) implement an
* interface between the system independent NTP clock and the Unix
* residues.
*
* In order to improve the apparent resolution, provide unbiased
- * rounding and insure that the readings cannot be predicted, the low-
- * order unused portion of the time below the resolution limit is filled
- * with an unbiased random fuzz.
+ * rounding and most importantly ensure that the readings cannot be
+ * predicted, the low-order unused portion of the time below the minimum
+ * time to read the clock is filled with an unbiased random fuzz.
+ *
+ * The sys_tick variable specifies the system clock tick interval in
+ * seconds, for stepping clocks, defined as those which return times
+ * less than MINSTEP greater than the previous reading. For systems that
+ * use a high-resolution counter such that each clock reading is always
+ * at least MINSTEP greater than the prior, sys_tick is the time to read
+ * the system clock.
+ *
+ * The sys_fuzz variable measures the minimum time to read the system
+ * clock, regardless of its precision. When reading the system clock
+ * using get_systime() after sys_tick and sys_fuzz have been determined,
+ * ntpd ensures each unprocessed clock reading is no less than sys_fuzz
+ * later than the prior unprocessed reading, and then fuzzes the bits
+ * below sys_fuzz in the timestamp returned, ensuring each of its
+ * resulting readings is strictly later than the previous.
*
- * The sys_tick variable secifies the system clock tick interval in
- * seconds. For systems that can interpolate between timer interrupts,
- * the resolution is presumed much less than the time to read the system
- * clock, which is the value of sys_tick after the precision has been
- * determined. For those systems that cannot interpolate between timer
- * interrupts, sys_tick will be much larger in the order of 10 ms, so the
- * fuzz should be that value. For Sunses the tick is not interpolated, but
- * the system clock is derived from a 2-MHz oscillator, so the resolution
- * is 500 ns and sys_tick is 500 ns.
+ * When slewing the system clock using adj_systime() (with the kernel
+ * loop discipline unavailable or disabled), adjtime() offsets are
+ * quantized to sys_tick, if sys_tick is greater than sys_fuzz, which
+ * is to say if the OS presents a stepping clock. Otherwise, offsets
+ * are quantized to the microsecond resolution of adjtime()'s timeval
+ * input. The remaining correction sys_residual is carried into the
+ * next adjtime() and meanwhile is also factored into get_systime()
+ * readings.
*/
-double sys_tick = 0; /* precision (time to read the clock) */
+double sys_tick = 0; /* tick size or time to read (s) */
+double sys_fuzz = 0; /* min. time to read the clock (s) */
+long sys_fuzz_nsec = 0; /* min. time to read the clock (ns) */
double sys_residual = 0; /* adjustment residue (s) */
time_stepped_callback step_callback;
+static int lamport_violated; /* clock was stepped back */
+
+void
+set_sys_fuzz(
+ double fuzz_val
+ )
+{
+ sys_fuzz = fuzz_val;
+ INSIST(sys_fuzz >= 0);
+ INSIST(sys_fuzz <= 1.0);
+ sys_fuzz_nsec = (long)(sys_fuzz * 1e9 + 0.5);
+}
+
+
#ifndef SIM /* ntpsim.c has get_systime() and friends for sim */
+static inline void
+get_ostime(
+ struct timespec * tsp
+ )
+{
+ int rc;
+
+#if defined(HAVE_CLOCK_GETTIME)
+ rc = clock_gettime(CLOCK_REALTIME, tsp);
+#elif defined(HAVE_GETCLOCK)
+ rc = getclock(TIMEOFDAY, tsp);
+#else
+ struct timeval tv;
+
+ rc = GETTIMEOFDAY(&tv, NULL);
+ tsp->tv_sec = tv.tv_sec;
+ tsp->tv_nsec = tv_tv_usec * 1000;
+#endif
+ if (rc < 0) {
+ msyslog(LOG_ERR, "read system clock failed: %m (%d)",
+ errno);
+ exit(1);
+ }
+}
+
+
/*
* get_systime - return system time in NTP timestamp format.
*/
l_fp *now /* system time */
)
{
- double dtemp;
-
-#if defined(GET_SYSTIME_AS_TIMESPEC)
-
+ static struct timespec ts_prev; /* prior os time */
+ static l_fp lfp_prev; /* prior pre-residual result */
+ static l_fp lfp_prev_w_resid;/* prior result including sys_residual */
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;
+
+ get_ostime(&ts);
/*
- * Convert Unix timespec from seconds and nanoseconds to NTP
- * seconds and fraction.
+ * After default_get_precision() has set a nonzero sys_fuzz,
+ * ensure every reading of the OS clock advances by at least
+ * sys_fuzz over the prior reading, thereby assuring each
+ * fuzzed result is strictly later than the prior. Limit the
+ * necessary fiction to 1 second.
*/
- GET_SYSTIME_AS_TIMESPEC(&ts);
- now->l_i = (int32)ts.tv_sec + JAN_1970;
- dtemp = 0;
- if (sys_tick > FUZZ)
- dtemp = ntp_random() * 2. / FRAC * sys_tick * 1e9;
- else if (sys_tick > 0)
- dtemp = ntp_random() * 2. / FRAC;
- dtemp = (ts.tv_nsec + dtemp) * 1e-9 + sys_residual;
- if (dtemp >= 1.) {
- dtemp -= 1.;
- now->l_i++;
- } else if (dtemp < 0) {
- dtemp += 1.;
- now->l_i--;
+ if (sys_fuzz_nsec > 0 && !lamport_violated) {
+ timespec_addns(&ts_min, &ts_prev, sys_fuzz_nsec);
+ if (timespec_cmp_fast(&ts, &ts_min) < 0) {
+ timespec_sub(&ts_lam, &ts_min, &ts);
+ ts = ts_min;
+ if (ts_lam.tv_sec > 0) {
+ msyslog(LOG_ERR,
+ "get_systime Lamport advance exceeds one second (%.9f)",
+ ts_lam.tv_sec + 1e-9 * ts_lam.tv_nsec);
+ exit(1);
+ }
+ }
}
- now->l_uf = (u_int32)(dtemp * FRAC);
+ ts_prev_log = ts_prev;
+ ts_prev = ts;
-#else /* have GETTIMEOFDAY */
+ /* convert from timespec to l_fp fixed-point */
+ timespec_abstolfp(&result, &ts);
- struct timeval tv; /* seconds and microseconds */
+ /*
+ * Add in the fuzz.
+ */
+ if (sys_fuzz > 0.) {
+ dfuzz = ntp_random() * 2. / FRAC * sys_fuzz;
+ DTOLFP(dfuzz, &lfpfuzz);
+ L_ADD(&result, &lfpfuzz);
+ } else {
+ dfuzz = 0;
+ }
/*
- * Convert Unix timeval from seconds and microseconds to NTP
- * seconds and fraction.
+ * Ensure result is strictly greater than prior result (ignoring
+ * sys_residual's effect for now) once sys_fuzz has been
+ * determined.
*/
- GETTIMEOFDAY(&tv, NULL);
- now->l_i = tv.tv_sec + JAN_1970;
- dtemp = 0;
- if (sys_tick > FUZZ)
- dtemp = ntp_random() * 2. / FRAC * sys_tick * 1e6;
- else if (sys_tick > 0)
- dtemp = ntp_random() * 2. / FRAC;
- dtemp = (tv.tv_usec + dtemp) * 1e-6 + sys_residual;
- if (dtemp >= 1.) {
- dtemp -= 1.;
- now->l_i++;
- } else if (dtemp < 0) {
- dtemp += 1.;
- now->l_i--;
+ if (sys_fuzz > 0.) {
+ if (!L_ISZERO(&lfp_prev) && !lamport_violated) {
+ if (!L_ISGTU(&result, &lfp_prev)) {
+ msyslog(LOG_ERR,
+ "%sts_min %s ts_prev %s ts %s",
+ (lamport_violated)
+ ? "LAMPORT "
+ : "",
+ timespec_tostr(&ts_min),
+ timespec_tostr(&ts_prev_log),
+ timespec_tostr(&ts));
+ msyslog(LOG_ERR, "sys_fuzz %ld nsec, this fuzz %.9f",
+ sys_fuzz_nsec, dfuzz);
+ lfpdelta = lfp_prev;
+ L_SUB(&lfpdelta, &result);
+ LFPTOD(&lfpdelta, ddelta);
+ msyslog(LOG_ERR,
+ "get_systime prev result 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);
+ }
+ }
+ lfp_prev = result;
}
- now->l_uf = (u_int32)(dtemp * FRAC);
+ if (lamport_violated)
+ lamport_violated = FALSE;
-#endif /* have GETTIMEOFDAY */
+ *now = result;
}
{
struct timeval adjtv; /* new adjustment */
struct timeval oadjtv; /* residual adjustment */
+ double quant; /* quantize to multiples of */
double dtemp;
long ticks;
int isneg = 0;
}
adjtv.tv_sec = (long)dtemp;
dtemp -= adjtv.tv_sec;
- ticks = (long)(dtemp / sys_tick + .5);
- adjtv.tv_usec = (long)(ticks * sys_tick * 1e6);
+ if (sys_tick > sys_fuzz)
+ quant = sys_tick;
+ else
+ quant = 1e-6;
+ ticks = (long)(dtemp / quant + .5);
+ adjtv.tv_usec = (long)(ticks * quant * 1e6);
dtemp -= adjtv.tv_usec / 1e6;
sys_residual = dtemp;
/* ---> time-critical path starts ---> */
/* get the current time as l_fp (without fuzz) and as struct timeval */
-#if defined(GET_SYSTIME_AS_TIMESPEC)
- GET_SYSTIME_AS_TIMESPEC(&timets);
+ get_ostime(&timets);
timespec_abstolfp(&fp_sys, &timets);
tvlast.tv_sec = timets.tv_sec;
tvlast.tv_usec = (timets.tv_nsec + 500) / 1000;
-#else /* have GETTIMEOFDAY */
- UNUSED_LOCAL(timets);
- GETTIMEOFDAY(&tvlast, NULL);
- timeval_abstolfp(&fp_sys, &tvlast);
-#endif
/* get the target time as l_fp */
L_ADD(&fp_sys, &fp_ofs);
/* <--- time-critical path ended with 'ntp_set_tod()' <--- */
sys_residual = 0;
+ lamport_violated = (step < 0);
if (step_callback)
(*step_callback)();
/*
* Find the precision of this particular machine
*/
-#define MINSTEP 100e-9 /* minimum clock increment (s) */
-#define MAXSTEP 20e-3 /* maximum clock increment (s) */
-#define MINLOOPS 5 /* minimum number of step samples */
+#define MINSTEP 100e-9 /* minimum clock increment (s) */
+#define MAXSTEP 1 /* maximum clock increment (s) */
+#define MINCHANGES 5 /* minimum number of step samples */
+#define MAXLOOPS ((int)(1. / MINSTEP)) /* avoid infinite loop */
/*
* This routine measures the system precision defined as the minimum of
* clock. However, if a difference is less than MINSTEP, the clock has
* been read more than once during a clock tick and the difference is
* ignored. We set MINSTEP greater than zero in case something happens
- * like a cache miss.
+ * like a cache miss, and to tolerate underlying system clocks which
+ * ensure each reading is strictly greater than prior readings while
+ * using an underlying stepping (not interpolated) clock.
+ *
+ * sys_tick and sys_precision represent the time to read the clock for
+ * systems with high-precision clocks, and the tick interval or step
+ * size for lower-precision stepping clocks.
+ *
+ * This routine also measures the time to read the clock on stepping
+ * system clocks by counting the number of readings between changes of
+ * the underlying clock. With either type of clock, the minimum time
+ * to read the clock is saved as sys_fuzz, and used to ensure the
+ * get_systime() readings always increase and are fuzzed below sys_fuzz.
*/
int
default_get_precision(void)
{
l_fp val; /* current seconds fraction */
l_fp last; /* last seconds fraction */
- l_fp diff; /* difference */
+ l_fp ldiff; /* difference */
double tick; /* computed tick value */
- double dtemp; /* scratch */
+ double diff; /* scratch */
int i; /* log2 precision */
+ int changes;
+ long repeats;
+ long max_repeats;
/*
- * Loop to find precision value in seconds.
+ * Loop to find precision value in seconds. With sys_fuzz set
+ * to zero, get_systime() disables its fuzzing of low bits.
*/
tick = MAXSTEP;
- i = 0;
+ set_sys_fuzz(0.);
get_systime(&last);
- while (1) {
+ max_repeats = 0;
+ repeats = 0;
+ changes = 0;
+ for (i = 0; i < MAXLOOPS && changes < MINCHANGES; i++) {
get_systime(&val);
- diff = val;
- L_SUB(&diff, &last);
+ ldiff = val;
+ L_SUB(&ldiff, &last);
last = val;
- LFPTOD(&diff, dtemp);
- if (dtemp < MINSTEP)
- continue;
-
- if (dtemp < tick)
- tick = dtemp;
- if (++i >= MINLOOPS)
- break;
+ LFPTOD(&ldiff, diff);
+ if (diff > MINSTEP) {
+ max_repeats = max(repeats, max_repeats);
+ repeats = 0;
+ changes++;
+ tick = min(diff, tick);
+ } else {
+ repeats++;
+ }
+ }
+ if (changes < MINCHANGES) {
+ msyslog(LOG_ERR, "Fatal error: precision could not be measured (MINSTEP too large?)");
+ exit(1);
}
sys_tick = tick;
+ if (0 == max_repeats) {
+ set_sys_fuzz(sys_tick);
+ } else {
+ set_sys_fuzz(sys_tick / max_repeats);
+ msyslog(LOG_NOTICE, "proto: fuzz beneath %.3f usec",
+ sys_fuzz * 1e6);
+ }
/*
* Find the nearest power of two.
*/
- msyslog(LOG_NOTICE, "proto: precision = %.3f usec", tick * 1e6);
- for (i = 0; tick <= 1; i++)
+ for (i = 0; tick <= 1; i--)
tick *= 2;
if (tick - 1 > 1 - tick / 2)
- i--;
- return (-i);
+ i++;
+ msyslog(LOG_NOTICE, "proto: precision = %.3f usec (%d)",
+ sys_tick * 1e6, i);
+
+ return i;
}