diff --git a/chrony.texi b/chrony.texi index f52729a..d498e7c 100644 --- a/chrony.texi +++ b/chrony.texi @@ -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 +@end example @c }}} @c {{{ rtcdevice @node rtcdevice directive diff --git a/conf.c b/conf.c index b64c4bc..0715219 100644 --- 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 e2aac48..b1fb776 100644 --- 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); diff --git a/sources.c b/sources.c index 170710e..5b20a71 100644 --- 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; isel_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 - /* Does the current source have this stratum, doesn't have distance - much worse than the best source and is it still a survivor? */ + /* 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, "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, 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", selected_source_index); +#endif -#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); -#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;