[chrony-dev] [PATCH] MacOS X add drift removal with corrected rounding

[ Thread Index | Date Index | More chrony.tuxfamily.org/chrony-dev Archives ]


---
 sys_macosx.c | 110 +++++++++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 80 insertions(+), 30 deletions(-)

diff --git a/sys_macosx.c b/sys_macosx.c
index 30945a4..3952adb 100644
--- a/sys_macosx.c
+++ b/sys_macosx.c
@@ -44,6 +44,7 @@
 
 #include "sys_macosx.h"
 #include "localp.h"
+#include "sched.h"
 #include "logging.h"
 #include "util.h"
 
@@ -69,10 +70,9 @@ static double current_freq;
 
 static double adjustment_requested;
 
-/* Kernel parameters to calculate adjtime error. */
+/* skew rate for adjtime() - get from sysctl */
 
-static int kern_tickadj;
-static long kern_bigadj;
+static double skewrate;
 
 /* ================================================== */
 
@@ -114,8 +114,6 @@ start_adjust(void)
   struct timeval T1;
   double elapsed, accrued_error;
   double adjust_required;
-  struct timeval exact_newadj;
-  long delta, tickdelta;
   double rounding_error;
   double old_adjust_remaining;
 
@@ -129,24 +127,9 @@ start_adjust(void)
 
   adjust_required = - (accrued_error + offset_register);
 
-  UTI_DoubleToTimeval(adjust_required, &exact_newadj);
-
-  /* At this point, we need to round the required adjustment the
-     same way the kernel does. */
-
-  delta = exact_newadj.tv_sec * 1000000 + exact_newadj.tv_usec;
-  if (delta > kern_bigadj || delta < -kern_bigadj)
-    tickdelta = 10 * kern_tickadj;
-  else
-    tickdelta = kern_tickadj;
-  if (delta % tickdelta)
-	delta = delta / tickdelta * tickdelta;
-  newadj.tv_sec = 0;
-  newadj.tv_usec = (int)delta;
-  UTI_NormaliseTimeval(&newadj);
-
-  /* Add rounding error back onto offset register. */
-  UTI_DiffTimevalsToDouble(&rounding_error, &newadj, &exact_newadj);
+  UTI_DoubleToTimeval(adjust_required, &newadj);
+  UTI_TimevalToDouble(&newadj, &adjustment_requested);
+  rounding_error = adjust_required - adjustment_requested;
 
   if (adjtime(&newadj, &oldadj) < 0) {
     LOG_FATAL(LOGF_SysMacOSX, "adjtime() failed");
@@ -154,10 +137,9 @@ start_adjust(void)
 
   UTI_TimevalToDouble(&oldadj, &old_adjust_remaining);
 
-  offset_register = rounding_error - old_adjust_remaining;
+  offset_register = -(rounding_error + old_adjust_remaining);
 
   T0 = T1;
-  UTI_TimevalToDouble(&newadj, &adjustment_requested);
 }
 
 /* ================================================== */
@@ -168,7 +150,7 @@ stop_adjust(void)
   struct timeval T1;
   struct timeval zeroadj, remadj;
   double adjustment_remaining, adjustment_achieved;
-  double elapsed, elapsed_plus_adjust;
+  double elapsed, skew_time, drift_time;
 
   zeroadj.tv_sec = 0;
   zeroadj.tv_usec = 0;
@@ -184,11 +166,46 @@ stop_adjust(void)
   UTI_DiffTimevalsToDouble(&elapsed, &T1, &T0);
   UTI_TimevalToDouble(&remadj, &adjustment_remaining);
 
+  /* amount of adustment made by adjtime() since start_adjust() */
   adjustment_achieved = adjustment_requested - adjustment_remaining;
-  elapsed_plus_adjust = elapsed - adjustment_achieved;
 
-  offset_register += current_freq * elapsed_plus_adjust - adjustment_remaining;
+  /*
+
+    Adjustment started at T0
+    Adjustment completed at T  (skew_time)
+    Clock drifts during T to T1
+
+     ^    |\                |
+     |    | \               |
+     o    |  \            / |
+     f    |   \         /   |
+     f    |    \      /     |
+     s    |     \   /       |
+     e    |------\/---------|--
+     t    T0      T         T1
+     |    |<---- elapsed -->|
+     v    |<-skew><-drift-->|
 
+     From T0 to T  the clock is skewed at a rate of skewrate
+     From T  to T1 the clock drifts as measured by current_frequency
+
+     Assume T and T1 are always greater than T0. Either or both could be less
+     if the clock has been manually adjusted, but thats a one off aberration
+     and will self correct in the next adjustment cycle.
+
+     Interval calculation is independent of the sign of the various offsets
+     although signs must be taken into account when calculating the updated offset
+
+  */
+
+  skew_time = fabs(adjustment_achieved / skewrate); /* time during which adjtime() is active */
+  drift_time = 0;
+  if(skew_time < fabs(elapsed)) {
+    /* previous adjtime() has completed - remaining time is free running */
+    drift_time = fabs(elapsed) - skew_time;
+  }
+
+  offset_register += current_freq * drift_time - adjustment_remaining;
   adjustment_requested = 0.0;
   T0 = T1;
 }
@@ -272,6 +289,32 @@ get_offset_correction(struct timeval *raw,
 
 /* ================================================== */
 
+/* Interval in seconds between adjustments to cancel systematic drift */
+#define DRIFT_REMOVAL_INTERVAL (1.0)
+
+static int drift_removal_running = 0;
+static SCH_TimeoutID drift_removal_id;
+
+/* ================================================== */
+/* This is the timer callback routine which is called periodically to
+ invoke a time adjustment to take out the machine's drift.
+ Otherwise, times reported through this software (e.g. by running
+ ntpdate from another machine) show the machine being correct (since
+ they correct for drift build-up), but any program on this machine
+ that reads the system time will be given an erroneous value, the
+ degree of error depending on how long it is since
+ get_offset_correction was last called. */
+
+static void
+drift_removal_timeout(SCH_ArbitraryArgument not_used)
+{
+  stop_adjust();
+  start_adjust();
+  drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
+}
+
+/* ================================================== */
+
 void
 SYS_MacOSX_Initialise(void)
 {
@@ -289,8 +332,7 @@ SYS_MacOSX_Initialise(void)
   if(result < 0) {
     LOG_FATAL(LOGF_SysMacOSX, "Cannot read clockinfo");
   }
-  kern_tickadj = clockinfo.tickadj;
-  kern_bigadj = clockinfo.tick;
+  skewrate = (double)clockinfo.tickadj / (double)clockinfo.tick;
 
   clock_initialise();
 
@@ -299,6 +341,10 @@ SYS_MacOSX_Initialise(void)
                             get_offset_correction,
                             NULL /* set_leap */,
                             NULL /* set_sync_status */);
+
+
+  drift_removal_id = SCH_AddTimeoutByDelay(DRIFT_REMOVAL_INTERVAL, drift_removal_timeout, NULL);
+  drift_removal_running = 1;
 }
 
 /* ================================================== */
@@ -306,6 +352,10 @@ SYS_MacOSX_Initialise(void)
 void
 SYS_MacOSX_Finalise(void)
 {
+  if (drift_removal_running) {
+    SCH_RemoveTimeout(drift_removal_id);
+  }
+
   clock_finalise();
 }
 
-- 
2.3.2 (Apple Git-55)


-- 
To unsubscribe email chrony-dev-request@xxxxxxxxxxxxxxxxxxxx with "unsubscribe" in the subject.
For help email chrony-dev-request@xxxxxxxxxxxxxxxxxxxx with "help" in the subject.
Trouble?  Email listmaster@xxxxxxxxxxxxxxxxxxxx.


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/