Skip to content

Commit 070b4f6

Browse files
committed
ntp: add maxdelayquant option
Add a new test for maximum delay using a long-term estimate of a p-quantile of the peer delay. If enabled, it replaces the maxdelaydevratio test. It's main advantage is that it is not sensitive to outliers corrupting the minimum delay. As it can take a large number of samples for the estimate to reach the expected value and adapt to a new value after a network change, the option is recommended only for local networks with very short polling intervals.
1 parent 851c823 commit 070b4f6

File tree

12 files changed

+94
-10
lines changed

12 files changed

+94
-10
lines changed

Makefile.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ LDFLAGS = @LDFLAGS@
3535

3636
EXTRA_OBJS = @EXTRA_OBJS@
3737

38-
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o \
38+
OBJS = array.o cmdparse.o conf.o local.o logging.o main.o memory.o quantiles.o \
3939
reference.o regress.o rtc.o samplefilt.o sched.o socket.o sources.o sourcestats.o \
4040
stubs.o smooth.o sys.o sys_null.o tempcomp.o util.o $(EXTRA_OBJS)
4141

candm.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,8 @@ typedef struct {
296296
uint32_t flags;
297297
int32_t filter_length;
298298
uint32_t cert_set;
299-
uint32_t reserved[2];
299+
Float max_delay_quant;
300+
uint32_t reserved[1];
300301
int32_t EOR;
301302
} REQ_NTP_Source;
302303

client.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,8 @@ process_cmd_add_source(CMD_Request *msg, char *line)
952952
(data.params.sel_options & SRC_SELECT_REQUIRE ? REQ_ADDSRC_REQUIRE : 0));
953953
msg->data.ntp_source.filter_length = htonl(data.params.filter_length);
954954
msg->data.ntp_source.cert_set = htonl(data.params.cert_set);
955+
msg->data.ntp_source.max_delay_quant =
956+
UTI_FloatHostToNetwork(data.params.max_delay_quant);
955957
memset(msg->data.ntp_source.reserved, 0, sizeof (msg->data.ntp_source.reserved));
956958

957959
result = 1;

cmdmon.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,8 @@ handle_add_source(CMD_Request *rx_message, CMD_Reply *tx_message)
757757
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_ratio);
758758
params.max_delay_dev_ratio =
759759
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_dev_ratio);
760+
params.max_delay_quant =
761+
UTI_FloatNetworkToHost(rx_message->data.ntp_source.max_delay_quant);
760762
params.min_delay = UTI_FloatNetworkToHost(rx_message->data.ntp_source.min_delay);
761763
params.asymmetry = UTI_FloatNetworkToHost(rx_message->data.ntp_source.asymmetry);
762764
params.offset = UTI_FloatNetworkToHost(rx_message->data.ntp_source.offset);

cmdparse.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
7272
src->params.max_delay = SRC_DEFAULT_MAXDELAY;
7373
src->params.max_delay_ratio = SRC_DEFAULT_MAXDELAYRATIO;
7474
src->params.max_delay_dev_ratio = SRC_DEFAULT_MAXDELAYDEVRATIO;
75+
src->params.max_delay_quant = 0.0;
7576
src->params.min_delay = 0.0;
7677
src->params.asymmetry = SRC_DEFAULT_ASYMMETRY;
7778
src->params.offset = 0.0;
@@ -140,6 +141,9 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
140141
} else if (!strcasecmp(cmd, "maxdelaydevratio")) {
141142
if (sscanf(line, "%lf%n", &src->params.max_delay_dev_ratio, &n) != 1)
142143
return 0;
144+
} else if (!strcasecmp(cmd, "maxdelayquant")) {
145+
if (sscanf(line, "%lf%n", &src->params.max_delay_quant, &n) != 1)
146+
return 0;
143147
} else if (!strcasecmp(cmd, "maxpoll")) {
144148
if (sscanf(line, "%d%n", &src->params.maxpoll, &n) != 1)
145149
return 0;

configure

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,7 @@ if [ $feat_timestamping = "1" ] && [ $try_timestamping = "1" ] &&
737737
&val, sizeof (val));'
738738
then
739739
add_def HAVE_LINUX_TIMESTAMPING
740-
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o quantiles.o"
740+
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o ntp_io_linux.o"
741741

742742
if test_code 'other timestamping options' \
743743
'sys/types.h sys/socket.h linux/net_tstamp.h' '' '' '
@@ -830,7 +830,7 @@ if [ $feat_refclock = "1" ] && [ $feat_phc = "1" ] && [ $try_phc = "1" ] && \
830830
'ioctl(1, PTP_CLOCK_GETCAPS + PTP_SYS_OFFSET, 0);'
831831
then
832832
grep 'HAVE_LINUX_TIMESTAMPING' config.h > /dev/null ||
833-
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o quantiles.o"
833+
EXTRA_OBJECTS="$EXTRA_OBJECTS hwclock.o"
834834
add_def FEAT_PHC
835835
fi
836836

doc/chrony.conf.adoc

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,20 @@ If a measurement has a ratio of the increase in the round-trip delay from the
151151
minimum delay amongst the previous measurements to the standard deviation of
152152
the previous measurements that is greater than the specified ratio, it will be
153153
rejected. The default is 10.0.
154+
*maxdelayquant* _p_:::
155+
This option disables the *maxdelaydevratio* test and specifies the maximum
156+
acceptable delay as a quantile of the round-trip delay instead of a function of
157+
the minimum delay amongst the buffered measurements. If a measurement has a
158+
round-trip delay that is greater than a long-term estimate of the _p_-quantile,
159+
it will be rejected.
160+
+
161+
The specified _p_ value should be between 0.05 and 0.95. For example,
162+
*maxdelayquant 0.2* would indicate that only measurements with the lowest 20
163+
percent of round-trip delays should be accepted. Note that it can take many
164+
measurements for the estimated quantile to reach the expected value. This
165+
option is intended for synchronisation in mostly static local networks with
166+
very short polling intervals and possibly combined with the *filter* option.
167+
By default, this test is disabled in favour of the *maxdelaydevratio* test.
154168
*mindelay* _delay_:::
155169
This option specifies a fixed minimum round-trip delay to be used instead of
156170
the minimum amongst the previous measurements. This can be useful in networks
@@ -2070,8 +2084,8 @@ from the example line above):
20702084
. Stratum of remote computer. [2]
20712085
. RFC 5905 tests 1 through 3 (1=pass, 0=fail) [111]
20722086
. RFC 5905 tests 5 through 7 (1=pass, 0=fail) [111]
2073-
. Tests for maximum delay, maximum delay ratio and maximum delay dev ratio,
2074-
against defined parameters, and a test for synchronisation loop (1=pass,
2087+
. Results of the *maxdelay*, *maxdelayratio*, and *maxdelaydevratio* (or
2088+
*maxdelayquant*) tests, and a test for synchronisation loop (1=pass,
20752089
0=fail). The first test from these four also checks the server precision,
20762090
response time, and whether an interleaved response is acceptable for
20772091
synchronisation. [1111]

doc/chronyc.adoc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,8 @@ packets sent to the source is more variable than the delay of packets sent
699699
from the source back.
700700
*NTP tests*:::
701701
Results of RFC 5905 tests 1 through 3, 5 through 7, and tests for maximum
702-
delay, delay ratio, delay dev ratio, and synchronisation loop.
702+
delay, delay ratio, delay dev ratio (or delay quantile), and synchronisation
703+
loop.
703704
*Interleaved*:::
704705
This shows if the response was in the interleaved mode.
705706
*Authenticated*:::

ntp_core.c

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "ntp_ext.h"
3636
#include "ntp_io.h"
3737
#include "memory.h"
38+
#include "quantiles.h"
3839
#include "sched.h"
3940
#include "reference.h"
4041
#include "local.h"
@@ -196,6 +197,9 @@ struct NCR_Instance_Record {
196197

197198
SRC_Instance source;
198199

200+
/* Optional long-term quantile estimate of peer delay */
201+
QNT_Instance delay_quant;
202+
199203
/* Optional median filter for NTP measurements */
200204
SPF_Instance filter;
201205
int filter_count;
@@ -266,6 +270,10 @@ static ARR_Instance broadcasts;
266270
#define MAX_MAXDELAYRATIO 1.0e6
267271
#define MAX_MAXDELAYDEVRATIO 1.0e6
268272

273+
/* Parameters for the peer delay quantile */
274+
#define DELAY_QUANT_Q 100
275+
#define DELAY_QUANT_REPEAT 7
276+
269277
/* Minimum and maximum allowed poll interval */
270278
#define MIN_POLL -7
271279
#define MAX_POLL 24
@@ -642,6 +650,14 @@ NCR_CreateInstance(NTP_Remote_Address *remote_addr, NTP_Source_Type type,
642650
params->min_samples, params->max_samples,
643651
params->min_delay, params->asymmetry);
644652

653+
if (params->max_delay_quant > 0.0) {
654+
int k = round(CLAMP(0.05, params->max_delay_quant, 0.95) * DELAY_QUANT_Q);
655+
result->delay_quant = QNT_CreateInstance(k, k, DELAY_QUANT_Q, DELAY_QUANT_REPEAT,
656+
LCL_GetSysPrecisionAsQuantum() / 2.0);
657+
} else {
658+
result->delay_quant = NULL;
659+
}
660+
645661
if (params->filter_length >= 1)
646662
result->filter = SPF_CreateInstance(1, params->filter_length, NTP_MAX_DISPERSION, 0.0);
647663
else
@@ -677,6 +693,8 @@ NCR_DestroyInstance(NCR_Instance instance)
677693
if (instance->mode == MODE_ACTIVE)
678694
NIO_CloseServerSocket(instance->local_addr.sock_fd);
679695

696+
if (instance->delay_quant)
697+
QNT_DestroyInstance(instance->delay_quant);
680698
if (instance->filter)
681699
SPF_DestroyInstance(instance->filter);
682700

@@ -733,6 +751,8 @@ NCR_ResetInstance(NCR_Instance instance)
733751
UTI_ZeroNtp64(&instance->init_remote_ntp_tx);
734752
zero_local_timestamp(&instance->init_local_rx);
735753

754+
if (instance->delay_quant)
755+
QNT_Reset(instance->delay_quant);
736756
if (instance->filter)
737757
SPF_DropSamples(instance->filter);
738758
instance->filter_count = 0;
@@ -1550,6 +1570,22 @@ check_delay_ratio(NCR_Instance inst, SST_Stats stats,
15501570

15511571
/* ================================================== */
15521572

1573+
static int
1574+
check_delay_quant(NCR_Instance inst, double delay)
1575+
{
1576+
double quant;
1577+
1578+
quant = QNT_GetQuantile(inst->delay_quant, QNT_GetMinK(inst->delay_quant));
1579+
1580+
if (delay <= quant)
1581+
return 1;
1582+
1583+
DEBUG_LOG("maxdelayquant: delay=%e quant=%e", delay, quant);
1584+
return 0;
1585+
}
1586+
1587+
/* ================================================== */
1588+
15531589
static int
15541590
check_delay_dev_ratio(NCR_Instance inst, SST_Stats stats,
15551591
struct timespec *sample_time, double offset, double delay)
@@ -1935,12 +1971,18 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
19351971
administrator-defined value */
19361972
testB = check_delay_ratio(inst, stats, &sample.time, sample.peer_delay);
19371973

1938-
/* Test C requires that the ratio of the increase in delay from the minimum
1974+
/* Test C either requires that the delay is less than an estimate of an
1975+
administrator-defined quantile, or (if the quantile is not specified)
1976+
it requires that the ratio of the increase in delay from the minimum
19391977
one in the stats data register to the standard deviation of the offsets
19401978
in the register is less than an administrator-defined value or the
19411979
difference between measured offset and predicted offset is larger than
19421980
the increase in delay */
1943-
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset, sample.peer_delay);
1981+
if (inst->delay_quant)
1982+
testC = check_delay_quant(inst, sample.peer_delay);
1983+
else
1984+
testC = check_delay_dev_ratio(inst, stats, &sample.time, sample.offset,
1985+
sample.peer_delay);
19441986

19451987
/* Test D requires that the source is not synchronised to us and is not us
19461988
to prevent a synchronisation loop */
@@ -2073,6 +2115,9 @@ process_response(NCR_Instance inst, NTP_Local_Address *local_addr,
20732115
}
20742116

20752117
SRC_UpdateStatus(inst->source, MAX(inst->remote_stratum, inst->min_stratum), pkt_leap);
2118+
2119+
if (inst->delay_quant)
2120+
QNT_Accumulate(inst->delay_quant, sample.peer_delay);
20762121
}
20772122

20782123
if (good_packet) {

srcparams.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ typedef struct {
6161
double max_delay;
6262
double max_delay_ratio;
6363
double max_delay_dev_ratio;
64+
double max_delay_quant;
6465
double min_delay;
6566
double asymmetry;
6667
double offset;

0 commit comments

Comments
 (0)