From: Harlan Stenn Date: Wed, 6 Oct 2010 04:17:06 +0000 (-0400) Subject: Documentation and code cleanup from Dave Mills. No more NTP_MAXASSOC. X-Git-Tag: NTP_4_2_7P61~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f33f5beffc35cd0a0f501c7260b89f171dbb0db5;p=thirdparty%2Fntp.git Documentation and code cleanup from Dave Mills. No more NTP_MAXASSOC. bk: 4cabf842OHYqolr77TI6u6pWKWBh7g --- diff --git a/ChangeLog b/ChangeLog index d140898ee..9cefcdb87 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +* Documentation and code cleanup from Dave Mills. No more NTP_MAXASSOC. (4.2.7p60) 2010/10/04 Released by Harlan Stenn * Documentation updates from Dave Mills. (4.2.7p59) 2010/10/02 Released by Harlan Stenn diff --git a/html/cluster.html b/html/cluster.html index d96f44a67..123860442 100644 --- a/html/cluster.html +++ b/html/cluster.html @@ -4,13 +4,12 @@ Clock Cluster Algorithm - - +

Clock Cluster Algorithm

Last update: - 04-Oct-2010 2:42 + 05-Oct-2010 21:29 UTC


The clock cluster algorithm processes the truechimers produced by the clock select algorithm to produce the survivors used by the mitigation algorithms to discipline the system clock. It operates in a series of rounds, where at each round the truechimer furthest from the offset centroid is pruned from the population. The rounds are continued until a specified termination condition results. This page discusses the algorithm in detail.

@@ -19,12 +18,12 @@

di(j) = q(j) - q(i),

where q(i) is the peer offset of the ith entry and q(j) is the peer offset of the jth entry, both produced by the clock filter algorithm. Then, the select jitter jS(i) of the ith entry is the root distance of the ith entry times the RMS sum of di(j) as j ranges from 1 to n. For the purpose of notation in the example to follow, let jR(i) be the peer jitter computed by the clock filter algorithm for the ith entry. In general, the expected error statistic for the ith entry is the RMS sum of these two components, but that is not needed by the clock cluster algorithm.

-

The object at each round is to prune the entry with the largest select jitter until the termination condition is met. Note that the select jitter must be recomputed at each round, but the peer jitter does not change. At each round the remaining entries on the list represent the survivors of that round. The list is always pruned to the maxclock threshold with default 10, but can be set by the maxclock option of the tos command. This threshold is useful to limit the number of survivors when automatic server discovery schemes are in use.

-

The termination condition has two parts. First, if the number of candidates is not greater than the sane threshold set by the minsane option of the tos command, or not greater than the minclock threshold set by the minclock option of the tos command, the pruning process terminates. The minsane default is 1 and the minclock default is 3. These thresholds can be changed to fit special conditions, as described on the Mitigation Rules and the prefer Keyword page.

+

The object at each round is to prune the entry with the largest select jitter until the termination condition is met. Note that the select jitter must be recomputed at each round, but the peer jitter does not change. At each round the remaining entries on the list represent the survivors of that round. While the number of entries on the list is not limited, the list is always pruned to the maxclock threshold. It has default 10, but can be set by the maxclock option of the tos command. This threshold is useful to limit the number of survivors when automatic server discovery schemes are in use.

+

The termination condition has two parts. First, if the number of candidates is not greater than the minclock threshold set by the minclock option of the tos command, the pruning process terminates. The minclock default is 3, but can be changed to fit special conditions, as described on the Mitigation Rules and the prefer Keyword page.

gif

Figure 1. Cluster Algorithm

-

The second termination condition is more intricate. Figure 1 shows a round where a candidates of (a) is pruned to yield the candidates of (b). Let jmax be the maximum select jitter and jmin be the minimum peer jitter over all entries on the list. In (a), candidate 1 has the highest select jitter, so jmax = jS(1). Candidate 4 has the lowest peer jitter, so jmin = jR(4). Since jmax > jmin, the algorithm prunes candidate 1 and continues. In (b), jmax = jS(3) and jmin = jR(4). Since jmaxjmin, pruning additional candidates will not reduce select jitter. So, the algorithm terminates with candidates 2, 3 and 4 as survivors.

+

The second termination condition is more intricate. Figure 1 shows a round where a candidate of (a) is pruned to yield the candidates of (b). Let jmax be the maximum select jitter and jmin be the minimum peer jitter over all entries on the list. In (a), candidate 1 has the highest select jitter, so jmax = jS(1). Candidate 4 has the lowest peer jitter, so jmin = jR(4). Since jmax > jmin, select jitter dominates total jitter, so the algorithm prunes candidate 1. In (b), jmax = jS(3) and jmin = jR(4). Since jmax < jmin, pruning additional candidates will not significantly reduce total jitter. So, the algorithm terminates with candidates 2, 3 and 4 as survivors.


diff --git a/include/ntp.h b/include/ntp.h index 9ecee5d4b..d37b56057 100644 --- a/include/ntp.h +++ b/include/ntp.h @@ -126,7 +126,6 @@ typedef char s_char; */ #define NTP_MINCLOCK 3 /* min survivors */ #define NTP_MAXCLOCK 10 /* max candidates */ -#define NTP_MAXASSOC 50 /* max associations */ #define MINDISPERSE .001 /* min distance */ #define MAXDISTANCE 1.5 /* max root distance (select threshold) */ #define CLOCK_SGATE 3. /* popcorn spike gate */ diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c index b261c8b34..97b4109f5 100644 --- a/ntpd/ntp_proto.c +++ b/ntpd/ntp_proto.c @@ -2270,7 +2270,6 @@ clock_select(void) double d, e, f, g; double high, low; double seljitter; - double synch[NTP_MAXASSOC], error[NTP_MAXASSOC]; double orphdist = 1e10; struct peer *osys_peer = NULL; struct peer *sys_prefer = NULL; /* prefer peer */ @@ -2281,14 +2280,16 @@ clock_select(void) struct peer *typelocal = NULL; struct peer *typepps = NULL; #endif /* REFCLOCK */ - - static int list_alloc = 0; static struct endpoint *endpoint = NULL; static int *indx = NULL; static struct peer **peers = NULL; + static double *synch = NULL; + static double *error = NULL; static u_int endpoint_size = 0; static u_int indx_size = 0; static u_int peers_size = 0; + static u_int synch_size = 0; + static u_int error_size = 0; size_t octets; /* @@ -2303,19 +2304,23 @@ clock_select(void) sys_stratum = STRATUM_UNSPEC; memcpy(&sys_refid, "DOWN", 4); #endif /* LOCKCLOCK */ - nlist = peer_count; - if (nlist > list_alloc) { - while (list_alloc < nlist) { - list_alloc += 5; - endpoint_size += 5 * 3 * sizeof(*endpoint); - indx_size += 5 * 3 * sizeof(*indx); - peers_size += 5 * sizeof(*peers); - } - octets = endpoint_size + indx_size + peers_size; - endpoint = erealloc(endpoint, octets); - indx = (int *)((char *)endpoint + endpoint_size); - peers = (struct peer **)((char *)indx + indx_size); - } + + /* + * Allocate dynamic space depending on the number of + * associations. + */ + endpoint_size = peer_count * 3 * sizeof(struct endpoint); + indx_size = peer_count * 3 * sizeof(int); + peers_size = peer_count * sizeof(struct peer *); + synch_size = peer_count * sizeof(double); + error_size = peer_count * sizeof(double); + octets = endpoint_size + indx_size + peers_size + synch_size + + error_size; + endpoint = erealloc(endpoint, octets); + indx = (int *)((char *)endpoint + endpoint_size); + peers = (struct peer **)((char *)indx + indx_size); + synch = (double *)((char *)peers + peers_size); + error = (double *)((char *)synch + synch_size); /* * Initially, we populate the island with all the rifraff peers @@ -2384,33 +2389,23 @@ clock_select(void) /* * Insert each interval endpoint on the sorted - * list. + * list. This code relies on the endpointd being at + * increasing offsets. */ - e = peer->offset; /* Upper end */ + e = peer->offset; /* upper end */ f = root_distance(peer); e = e + f; for (i = nl3 - 1; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; - indx[i + 3] = indx[i]; - } - indx[i + 3] = nl3; - endpoint[nl3].type = 1; - endpoint[nl3++].val = e; - - e = e - f; /* Center point */ - for (; i >= 0; i--) { - if (e >= endpoint[indx[i]].val) - break; - indx[i + 2] = indx[i]; } indx[i + 2] = nl3; - endpoint[nl3].type = 0; + endpoint[nl3].type = 1; endpoint[nl3++].val = e; - e = e - f; /* Lower end */ + e = e - f - f; /* lower end */ for (; i >= 0; i--) { if (e >= endpoint[indx[i]].val) break; @@ -2428,6 +2423,7 @@ clock_select(void) endpoint[indx[i]].type, endpoint[indx[i]].val); #endif + /* * This is the actual algorithm that cleaves the truechimers * from the falsetickers. The original algorithm was described @@ -2453,21 +2449,17 @@ clock_select(void) low = 1e9; high = -1e9; for (allow = 0; 2 * allow < nlist; allow++) { - int found; /* - * Bound the interval (low, high) as the largest - * interval containing points from presumed truechimers. + * Bound the interval (low, high) as the smallest + * interval containing points from the most sources. */ - found = 0; n = 0; for (i = 0; i < nl3; i++) { low = endpoint[indx[i]].val; n -= endpoint[indx[i]].type; if (n >= nlist - allow) break; - if (endpoint[indx[i]].type == 0) - found++; } n = 0; for (j = nl3 - 1; j >= 0; j--) { @@ -2475,22 +2467,8 @@ clock_select(void) n += endpoint[indx[j]].type; if (n >= nlist - allow) break; - if (endpoint[indx[j]].type == 0) - found++; } -#if 0 - /* - * If the number of candidates found outside the - * interval is greater than the number of falsetickers, - * then at least one truechimer is outside the interval, - * so go around again. This is what makes this algorithm - * different than Marzullo's. - */ - if (found > allow) - continue; -#endif - /* * If an interval containing truechimers is found, stop. * If not, increase the number of falsetickers and go @@ -2502,11 +2480,16 @@ clock_select(void) /* * Clustering algorithm. Construct candidate list in order first - * by stratum then by root distance, but keep only the best - * NTP_MAXASSOC of them. Scan the list to find falsetickers, who - * leave the island immediately. The TRUE peer is always a - * truechimer. We must leave at least one peer to collect the - * million bucks. + * by stratum then by root distance. Scan the list to find + * falsetickers, who leave the island immediately. The TRUE peer + * is always a truechimer. We must leave at least one peer + * to collect the million bucks. + * + * We assert the correc time is contained in the interval, but + * the best offset estimate for the interval might not be + * contained in the interval. For this purpose, a truechimer is + * defined as the midpoint of an interval that overlaps the + * intersection interval. */ j = 0; for (i = 0; i < nlist; i++) { @@ -2537,12 +2520,6 @@ clock_select(void) */ d = root_distance(peer) + clock_phi * (peer->nextdate - current_time); - if (j >= NTP_MAXASSOC) { - if (d >= synch[j - 1]) - continue; - else - j--; - } for (k = j; k > 0; k--) { if (d >= synch[k - 1]) break; @@ -2625,7 +2602,7 @@ clock_select(void) } } f = max(f, LOGTOD(sys_precision)); - if (nlist <= sys_minsane || nlist <= sys_minclock) { + if (nlist <= sys_minclock) { break; } else if (f <= d || peers[k]->flags &