* 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
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
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 *);
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;
{"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},
/* ================================================== */
+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)
{
/* ================================================== */
+double
+CNF_GetReselectDistance(void)
+{
+ return reselect_distance;
+}
+
+/* ================================================== */
+
int
CNF_GetManualEnabled(void)
{
*k1 = tempcomp_k1;
*k2 = tempcomp_k2;
}
+
/* Options used when selecting sources */
SRC_SelectOption sel_option;
+ /* Score against currently selected source */
+ double sel_score;
+
struct SelectInfo sel_info;
};
/* 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 */
n_sources = 0;
max_n_sources = 0;
selected_source_index = INVALID_SOURCE;
+ reselect_distance = CNF_GetReselectDistance();
initialised = 1;
LCL_AddParameterChangeHandler(slew_sources, NULL);
result->reachability = 0;
result->status = SRC_BAD_STATS;
result->type = type;
+ result->sel_score = 1.0;
result->sel_option = sel_option;
n_sources++;
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;
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;