]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Select source with minimum distance using a scoring system
authorMiroslav Lichvar <mlichvar@redhat.com>
Mon, 24 Jan 2011 16:09:00 +0000 (17:09 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Tue, 25 Jan 2011 16:40:37 +0000 (17:40 +0100)
Each source has a score against currently selected source which is
updated (multiplied by ratio of their distances) when one of the two
sources has a new sample. When the score reaches a limit, the source
will be selected. This should allow to slowly select the source with
minimum distance without frequent reselecting.

To avoid switching between sources with very variable distances (e.g. on
LAN or when upstream server uses a longer polling interval), sources
that are currently not selected are penalized by a fixed distance. This
can be configured with new reselectdist directive (100 microseconds by
default).

chrony.texi
conf.c
conf.h
sources.c

index f52729a93cf394850fce19b98e631c8e5e787c07..d498e7c236110084c6f6204521754e831ea8834b 100644 (file)
@@ -1199,6 +1199,7 @@ directives can occur in any order in the file.
 * pidfile directive::           Specify the file where chronyd's pid is written
 * port directive::              Set port to use for NTP packets
 * refclock directive::          Specify a reference clock
+* reselectdist directive::      Set improvement in distance needed to reselect a source
 * rtcdevice directive::         Specify name of enhanced RTC device (if not /dev/rtc)
 * rtcfile directive::           Specify the file where real-time clock data is stored
 * rtconutc directive::          Specify that the real time clock keeps UTC not local time
@@ -2424,6 +2425,22 @@ Prefer this source over sources without prefer option.
 Never select this source.  This is particularly useful for monitoring.
 @end table
 
+@c }}}
+@c {{{ reselectdist
+@node reselectdist directive
+@subsection reselectdist
+When @code{chronyd} selects synchronisation source from available sources, it
+will prefer the one with minimum synchronisation distance.  However, to
+avoid frequent reselecting when there are sources with similar distance, a
+fixed distance is added to the distance for sources that are currently not
+selected.  This can be set with the @code{reselectdist} option.  By default, the
+distance is 100 microseconds.
+
+The syntax is
+
+@example
+reselectdist <dist-in-seconds>
+@end example
 @c }}}
 @c {{{ rtcdevice
 @node rtcdevice directive
diff --git a/conf.c b/conf.c
index b64c4bca8823772b17502950341747a589429651..0715219aa963c2aad2fda253feabe6f986a27827 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -78,6 +78,7 @@ static void parse_logbanner(const char *);
 static void parse_logdir(const char *);
 static void parse_maxupdateskew(const char *);
 static void parse_maxclockerror(const char *);
+static void parse_reselectdist(const char *);
 static void parse_peer(const char *);
 static void parse_acquisitionport(const char *);
 static void parse_port(const char *);
@@ -123,6 +124,8 @@ static unsigned long command_key_id;
 static double max_update_skew = 1000.0;
 static double max_clock_error = 10; /* in ppm */
 
+static double reselect_distance = 1e-4;
+
 static int cmd_port = -1;
 
 static int do_log_measurements = 0;
@@ -258,6 +261,7 @@ static const Command commands[] = {
   {"pidfile", 7, parse_pidfile},
   {"broadcast", 9, parse_broadcast},
   {"tempcomp", 8, parse_tempcomp},
+  {"reselectdist", 12, parse_reselectdist},
   {"linux_hz", 8, parse_linux_hz},
   {"linux_freq_scale", 16, parse_linux_freq_scale},
   {"sched_priority", 14, parse_sched_priority},
@@ -609,6 +613,16 @@ parse_maxclockerror(const char *line)
 
 /* ================================================== */
 
+static void
+parse_reselectdist(const char *line)
+{
+  if (sscanf(line, "%lf", &reselect_distance) != 1) {
+    LOG(LOGS_WARN, LOGF_Configure, "Could not read reselect distance at line %d in file", line_number);
+  }
+}
+
+/* ================================================== */
+
 static void
 parse_driftfile(const char *line)
 {
@@ -1431,6 +1445,14 @@ CNF_GetMaxClockError(void)
 
 /* ================================================== */
 
+double
+CNF_GetReselectDistance(void)
+{
+  return reselect_distance;
+}
+
+/* ================================================== */
+
 int
 CNF_GetManualEnabled(void)
 {
@@ -1637,3 +1659,4 @@ CNF_GetTempComp(char **file, double *interval, double *T0, double *k0, double *k
   *k1 = tempcomp_k1;
   *k2 = tempcomp_k2;
 }
+
diff --git a/conf.h b/conf.h
index e2aac480244b7fa4258a041bd3c9148ad2fc6105..b1fb77631891086d542e3e87519ced0aa830343b 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -78,6 +78,7 @@ extern void CNF_GetLinuxFreqScale(int *set, double *freq_scale);
 /* Value returned in ppm, as read from file */
 extern double CNF_GetMaxUpdateSkew(void);
 extern double CNF_GetMaxClockError(void);
+extern double CNF_GetReselectDistance(void);
 extern int CNF_AllowLocalReference(int *stratum);
 
 extern void CNF_SetupAccessRestrictions(void);
index 170710e34ed17ca4f8c7c0473a46a071e8caf291..5b20a713fcbe223e2eaaeeccf8f56c2d1bd2d637 100644 (file)
--- a/sources.c
+++ b/sources.c
@@ -105,6 +105,9 @@ struct SRC_Instance_Record {
   /* Options used when selecting sources */ 
   SRC_SelectOption sel_option;
 
+  /* Score against currently selected source */
+  double sel_score;
+
   struct SelectInfo sel_info;
 };
 
@@ -132,6 +135,11 @@ static int selected_source_index; /* Which source index is currently
 /* Keep reachability status for last 8 samples */
 #define REACH_BITS 8
 
+/* Score needed to replace the currently selected source */
+#define SCORE_LIMIT 10.0
+
+static double reselect_distance;
+
 /* ================================================== */
 /* Forward prototype */
 
@@ -151,6 +159,7 @@ void SRC_Initialise(void) {
   n_sources = 0;
   max_n_sources = 0;
   selected_source_index = INVALID_SOURCE;
+  reselect_distance = CNF_GetReselectDistance();
   initialised = 1;
 
   LCL_AddParameterChangeHandler(slew_sources, NULL);
@@ -205,6 +214,7 @@ SRC_Instance SRC_CreateNewInstance(unsigned long ref_id, SRC_Type type, SRC_Sele
   result->reachability = 0;
   result->status = SRC_BAD_STATS;
   result->type = type;
+  result->sel_score = 1.0;
   result->sel_option = sel_option;
 
   n_sources++;
@@ -420,11 +430,12 @@ SRC_SelectSource(unsigned long match_addr)
   int n_sel_sources;
   double distance, min_distance;
   int stratum, min_stratum;
-  int min_distance_index;
   struct SelectInfo *si;
   double total_root_dispersion;
   int n_badstats_sources;
   int max_sel_reach, max_badstat_reach;
+  int max_score_index;
+  double max_score;
 
   NTP_Leap leap_status = LEAP_Normal;
   old_selected_index = selected_source_index;
@@ -734,47 +745,89 @@ SRC_SelectSource(unsigned long match_addr)
           if (stratum < min_stratum) min_stratum = stratum;
         }
 
-        /* Find the best source with minimum stratum */
-        min_distance_index = INVALID_SOURCE;
-        for (i=0; i<n_sel_sources; i++) {
-          index = sel_sources[i];
-          if (sources[index]->sel_info.stratum == min_stratum) {
-            if ((min_distance_index == INVALID_SOURCE) ||
-                (sources[index]->sel_info.root_distance < min_distance)) {
-              min_distance = sources[index]->sel_info.root_distance;
-              min_distance_index = index;
+#if 0
+        LOG(LOGS_INFO, LOGF_Sources, "min_stratum=%d", min_stratum);
+#endif
+
+        /* Update scores and find source with maximum score */
+
+        max_score_index = INVALID_SOURCE;
+        max_score = 0.0;
+
+        for (i = 0; i < n_sources; i++) {
+
+          /* Reset score for non-selectable sources */
+          if (sources[i]->status != SRC_SELECTABLE) {
+            sources[i]->sel_score = 1.0;
+            continue;
+          }
+            
+          /* And for sources with stratum higher than the minimum */
+          if (sources[i]->sel_info.stratum > min_stratum) {
+            sources[i]->sel_score = 1.0;
+            continue;
+          }
+
+          distance = sources[i]->sel_info.root_distance + reselect_distance;
+
+          if (selected_source_index != INVALID_SOURCE) {
+
+            /* Update score, but only for source pairs where one source
+               has a new sample */
+            if (sources[i]->ref_id == match_addr ||
+                sources[selected_source_index]->ref_id == match_addr) {
+
+              sources[i]->sel_score *=
+                sources[selected_source_index]->sel_info.root_distance / distance;
+
+              if (sources[i]->sel_score < 1.0)
+                sources[i]->sel_score = 1.0;
             }
+
+          } else {
+
+            /* When there is no selected source yet, assign scores so the
+               source with minimum distance will have maximum score. The scores
+               will be immediately reset. */
+
+            sources[i]->sel_score = 1.0 / distance; 
           }
-        }
 
 #if 0
-        LOG(LOGS_INFO, LOGF_Sources, "min_stratum=%d", min_stratum);
+          LOG(LOGS_INFO, LOGF_Sources, "select score=%f refid=%lx match_refid=%lx status=%d dist=%f",
+              sources[i]->sel_score, sources[i]->ref_id, match_addr, sources[i]->status, distance);
 #endif
+        
+          if (max_score < sources[i]->sel_score) {
+            max_score = sources[i]->sel_score;
+            max_score_index = i;
+          }
+        }
+
+        assert(max_score_index != INVALID_SOURCE);
 
-        /* Does the current source have this stratum, doesn't have distance
-           much worse than the best source and is it still a survivor? */
+        /* Does the current source have this stratum, is it still a survivor
+           and no other source has reached the score limit? */
 
         if ((selected_source_index == INVALID_SOURCE) ||
             (sources[selected_source_index]->status != SRC_SELECTABLE) ||
             (sources[selected_source_index]->sel_info.stratum > min_stratum) ||
-            (sources[selected_source_index]->sel_info.root_distance > 10 * min_distance)) {
+            (max_score_index != selected_source_index && max_score > SCORE_LIMIT)) {
           
           /* We have to elect a new synchronisation source */
 
-          selected_source_index = min_distance_index;
+          selected_source_index = max_score_index;
           LOG(LOGS_INFO, LOGF_Sources, "Selected source %s",
                 source_to_string(sources[selected_source_index]));
                                  
-
-#if 0
-          LOG(LOGS_INFO, LOGF_Sources, "new_sel_index=%d", min_distance_index);
-#endif
-        } else {
-          /* We retain the existing sync source, see p40 of RFC1305b.ps */
 #if 0
-          LOG(LOGS_INFO, LOGF_Sources, "existing reference retained", min_distance_index);
+          LOG(LOGS_INFO, LOGF_Sources, "new_sel_index=%d", selected_source_index);
 #endif
-          
+
+          /* New source has been selected, reset all scores */
+          for (i=0; i < n_sources; i++) {
+            sources[i]->sel_score = 1.0;
+          }
         }
 
         sources[selected_source_index]->status = SRC_SYNC;