]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
sys_generic: allow fast slewing with system driver
authorMiroslav Lichvar <mlichvar@redhat.com>
Tue, 22 Sep 2015 15:12:15 +0000 (17:12 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Wed, 23 Sep 2015 09:19:09 +0000 (11:19 +0200)
The system drivers may implement their own slewing which the generic
driver can use to slew faster than the maximum frequency the driver is
allowed to set directly.

sys_generic.c
sys_generic.h
sys_linux.c
sys_timex.c
sys_timex.h

index db343e55d3facc962206ed41afd841ed5b3e315d..49f5df234b4268903df010d205cce21ffc3f6802 100644 (file)
@@ -43,6 +43,8 @@
 static lcl_ReadFrequencyDriver drv_read_freq;
 static lcl_SetFrequencyDriver drv_set_freq;
 static lcl_SetSyncStatusDriver drv_set_sync_status;
+static lcl_AccrueOffsetDriver drv_accrue_offset;
+static lcl_OffsetCorrectionDriver drv_get_offset_correction;
 
 /* Current frequency as requested by the local module (in ppm) */
 static double base_freq;
@@ -85,6 +87,16 @@ static double correction_rate;
    real frequency of the clock */
 static double slew_error;
 
+/* Minimum offset that the system driver can slew faster than the maximum
+   frequency offset that it allows to be set directly */
+static double fastslew_min_offset;
+
+/* Maximum slew rate of the system driver */
+static double fastslew_max_rate;
+
+/* Flag indicating that the system driver is currently slewing */
+static int fastslew_active;
+
 /* ================================================== */
 
 static void handle_end_of_slew(void *anything);
@@ -109,6 +121,38 @@ handle_step(struct timeval *raw, struct timeval *cooked, double dfreq,
 
 /* ================================================== */
 
+static void
+start_fastslew(void)
+{
+  if (!drv_accrue_offset)
+    return;
+
+  drv_accrue_offset(offset_register, 0.0);
+
+  DEBUG_LOG(LOGF_SysGeneric, "fastslew offset=%e", offset_register);
+
+  offset_register = 0.0;
+  fastslew_active = 1;
+}
+
+/* ================================================== */
+
+static void
+stop_fastslew(struct timeval *now)
+{
+  double corr;
+
+  if (!drv_get_offset_correction || !fastslew_active)
+    return;
+
+  /* Cancel the remaining offset */
+  drv_get_offset_correction(now, &corr, NULL);
+  drv_accrue_offset(corr, 0.0);
+  offset_register -= corr;
+}
+
+/* ================================================== */
+
 static double
 clamp_freq(double freq)
 {
@@ -138,6 +182,8 @@ update_slew(void)
   UTI_DiffTimevalsToDouble(&duration, &now, &slew_start);
   offset_register -= slew_freq * duration;
 
+  stop_fastslew(&now);
+
   /* Estimate how long should the next slew take */
   if (fabs(offset_register) < MIN_OFFSET_CORRECTION) {
     duration = MAX_SLEW_TIMEOUT;
@@ -155,6 +201,14 @@ update_slew(void)
   else if (corr_freq > max_corr_freq)
     corr_freq = max_corr_freq;
 
+  /* Let the system driver perform the slew if the requested frequency
+     offset is too large for the frequency driver */
+  if (drv_accrue_offset && fabs(corr_freq) >= fastslew_max_rate &&
+      fabs(offset_register) > fastslew_min_offset) {
+    start_fastslew();
+    corr_freq = 0.0;
+  }
+
   /* Get the new real frequency and clamp it */
   total_freq = clamp_freq(base_freq + corr_freq * (1.0e6 - base_freq));
 
@@ -175,8 +229,8 @@ update_slew(void)
 
   /* Compute the duration of the slew and clamp it.  If the slewing frequency
      is zero or has wrong sign (e.g. due to rounding in the frequency driver or
-     when base_freq is larger than max_freq), use maximum timeout and try again
-     on the next update. */
+     when base_freq is larger than max_freq, or fast slew is active), use the
+     maximum timeout and try again on the next update. */
   if (fabs(offset_register) < MIN_OFFSET_CORRECTION ||
       offset_register * slew_freq <= 0.0) {
     duration = MAX_SLEW_TIMEOUT;
@@ -246,13 +300,25 @@ static void
 offset_convert(struct timeval *raw,
                double *corr, double *err)
 {
-  double duration;
+  double duration, fastslew_corr, fastslew_err;
 
   UTI_DiffTimevalsToDouble(&duration, raw, &slew_start);
 
-  *corr = slew_freq * duration - offset_register;
-  if (err)
-    *err = fabs(duration) <= max_freq_change_delay ? slew_error : 0.0;
+  if (drv_get_offset_correction && fastslew_active) {
+    drv_get_offset_correction(raw, &fastslew_corr, &fastslew_err);
+    if (fastslew_corr == 0.0 && fastslew_err == 0.0)
+      fastslew_active = 0;
+  } else {
+    fastslew_corr = fastslew_err = 0.0;
+  }
+
+  *corr = slew_freq * duration + fastslew_corr - offset_register;
+
+  if (err) {
+    *err = fastslew_err;
+    if (fabs(duration) <= max_freq_change_delay)
+      *err += slew_error;
+  }
 }
 
 /* ================================================== */
@@ -303,6 +369,9 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
                                lcl_ReadFrequencyDriver sys_read_freq,
                                lcl_SetFrequencyDriver sys_set_freq,
                                lcl_ApplyStepOffsetDriver sys_apply_step_offset,
+                               double min_fastslew_offset, double max_fastslew_rate,
+                               lcl_AccrueOffsetDriver sys_accrue_offset,
+                               lcl_OffsetCorrectionDriver sys_get_offset_correction,
                                lcl_SetLeapDriver sys_set_leap,
                                lcl_SetSyncStatusDriver sys_set_sync_status)
 {
@@ -310,6 +379,8 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
   max_freq_change_delay = max_set_freq_delay * (1.0 + max_freq / 1.0e6);
   drv_read_freq = sys_read_freq;
   drv_set_freq = sys_set_freq;
+  drv_accrue_offset = sys_accrue_offset;
+  drv_get_offset_correction = sys_get_offset_correction;
   drv_set_sync_status = sys_set_sync_status;
 
   base_freq = (*drv_read_freq)();
@@ -318,6 +389,10 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
 
   max_corr_freq = CNF_GetMaxSlewRate() / 1.0e6;
 
+  fastslew_min_offset = min_fastslew_offset;
+  fastslew_max_rate = max_fastslew_rate / 1.0e6;
+  fastslew_active = 0;
+
   lcl_RegisterSystemDrivers(read_frequency, set_frequency,
                             accrue_offset, sys_apply_step_offset ?
                               sys_apply_step_offset : apply_step_offset,
@@ -331,6 +406,8 @@ SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_set_freq_dela
 void
 SYS_Generic_Finalise(void)
 {
+  struct timeval now;
+
   /* Must *NOT* leave a slew running - clock could drift way off
      if the daemon is not restarted */
   if (slew_timer_running) {
@@ -339,6 +416,9 @@ SYS_Generic_Finalise(void)
   }
 
   (*drv_set_freq)(clamp_freq(base_freq));
+
+  LCL_ReadRawTime(&now);
+  stop_fastslew(&now);
 }
 
 /* ================================================== */
index 453208381e013121976d4b2bb861112bb9a4b895..d9b252e585dbacb624c2f0692c4353b592c8121c 100644 (file)
@@ -35,6 +35,9 @@ extern void SYS_Generic_CompleteFreqDriver(double max_set_freq_ppm, double max_s
                                            lcl_ReadFrequencyDriver sys_read_freq,
                                            lcl_SetFrequencyDriver sys_set_freq,
                                            lcl_ApplyStepOffsetDriver sys_apply_step_offset,
+                                           double min_fastslew_offset, double max_fastslew_rate,
+                                           lcl_AccrueOffsetDriver sys_accrue_offset,
+                                           lcl_OffsetCorrectionDriver sys_get_offset_correction,
                                            lcl_SetLeapDriver sys_set_leap,
                                            lcl_SetSyncStatusDriver sys_set_sync_status);
 
index f5543319ad847950492c64d15515de2bc95cacc2..24fcbbc46ad404c3703f6e06930d0e6d0064f2d8 100644 (file)
@@ -384,7 +384,8 @@ SYS_Linux_Initialise(void)
   SYS_Timex_InitialiseWithFunctions(1.0e6 * max_tick_bias / nominal_tick,
                                     1.0 / tick_update_hz,
                                     read_frequency, set_frequency,
-                                    have_setoffset ? apply_step_offset : NULL);
+                                    have_setoffset ? apply_step_offset : NULL,
+                                    0.0, 0.0, NULL, NULL);
 }
 
 /* ================================================== */
index b2b53994a921a96b1a74ce5db0098f71659d4595..60018f150edfd7aeb2af7c0ade3f270300315169 100644 (file)
@@ -181,7 +181,8 @@ initialise_timex(void)
 void
 SYS_Timex_Initialise(void)
 {
-  SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE, NULL, NULL, NULL);
+  SYS_Timex_InitialiseWithFunctions(MAX_FREQ, 1.0 / MIN_TICK_RATE, NULL, NULL, NULL,
+                                    0.0, 0.0, NULL, NULL);
 }
 
 /* ================================================== */
@@ -190,14 +191,20 @@ void
 SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay,
                                   lcl_ReadFrequencyDriver sys_read_freq,
                                   lcl_SetFrequencyDriver sys_set_freq,
-                                  lcl_ApplyStepOffsetDriver sys_apply_step_offset)
+                                  lcl_ApplyStepOffsetDriver sys_apply_step_offset,
+                                  double min_fastslew_offset, double max_fastslew_rate,
+                                  lcl_AccrueOffsetDriver sys_accrue_offset,
+                                  lcl_OffsetCorrectionDriver sys_get_offset_correction)
 {
   initialise_timex();
 
   SYS_Generic_CompleteFreqDriver(max_set_freq_ppm, max_set_freq_delay,
                                  sys_read_freq ? sys_read_freq : read_frequency,
                                  sys_set_freq ? sys_set_freq : set_frequency,
-                                 sys_apply_step_offset, set_leap, set_sync_status);
+                                 sys_apply_step_offset,
+                                 min_fastslew_offset, max_fastslew_rate,
+                                 sys_accrue_offset, sys_get_offset_correction,
+                                 set_leap, set_sync_status);
 }
 
 /* ================================================== */
index 9f8544b91d4be0f8a3d3e42538b2b69d00cc80d5..c15b16c6c58a97dbeb61ba2a3d86e3af813c74d8 100644 (file)
@@ -37,7 +37,10 @@ extern void SYS_Timex_Initialise(void);
 extern void SYS_Timex_InitialiseWithFunctions(double max_set_freq_ppm, double max_set_freq_delay,
                                               lcl_ReadFrequencyDriver sys_read_freq,
                                               lcl_SetFrequencyDriver sys_set_freq,
-                                              lcl_ApplyStepOffsetDriver sys_apply_step_offset);
+                                              lcl_ApplyStepOffsetDriver sys_apply_step_offset,
+                                              double min_fastslew_offset, double max_fastslew_rate,
+                                              lcl_AccrueOffsetDriver sys_accrue_offset,
+                                              lcl_OffsetCorrectionDriver sys_get_offset_correction);
 
 extern void SYS_Timex_Finalise(void);