From 3f5f5653d7966da8ba96d64fbf74aa472e9f078c Mon Sep 17 00:00:00 2001 From: Vedang Patel Date: Thu, 28 Mar 2019 20:32:30 -0700 Subject: [PATCH] port: Add interval update mechanism. This commit adds functionality to increase the sync and pdelay request intervals once gptp synchronization has been achieved. This is useful while running Automotive Profile where the network is usually static. Here, we are assuming that the gPTP synchronization is achieved whenever the last 'n' offsets calculated using the Sync messages are below a certain threshold. Both, the number of offsets to consider and the offset threshold are configurable. In order to support the interval update, a new clock servo state CLOCK_SERVO_STABLE has been added. The clock will transition to this state whenever the above condition is satisfied. When this transition occurs, the slave will send an "interval request" signaling message asking the master to change the sync interval. It will also update the pdelay request interval locally. Four new config options have been added to provide this functionality: - servo_offset_threshold: All the offset values being considered should be below this value. - servo_num_offset_values: number of previously received offset values to consider. - operLogSyncInterval: slave will ask the master to switch to this interval for sending sync messages when the offset stabilizes. - operLogPdelayReqInterval: the slave will set this interval for pdelay request messages when the offset stabilizes. Signed-off-by: Vedang Patel --- clock.c | 1 + config.c | 4 ++++ configs/automotive-slave.cfg | 4 ++++ configs/default.cfg | 4 ++++ phc2sys.c | 1 + port.c | 44 +++++++++++++++++++++++++++++++---- port_private.h | 2 ++ ptp4l.8 | 30 ++++++++++++++++++++++++ servo.c | 45 +++++++++++++++++++++++++++++++++++- servo.h | 13 +++++++++++ servo_private.h | 3 +++ 11 files changed, 145 insertions(+), 6 deletions(-) diff --git a/clock.c b/clock.c index beca9d6..642f688 100644 --- a/clock.c +++ b/clock.c @@ -1690,6 +1690,7 @@ enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin) tsproc_reset(c->tsproc, 0); break; case SERVO_LOCKED: + case SERVO_LOCKED_STABLE: clockadj_set_freq(c->clkid, -adj); if (c->clkid == CLOCK_REALTIME) { sysclk_set_sync(); diff --git a/config.c b/config.c index 2321310..0da7ce4 100644 --- a/config.c +++ b/config.c @@ -263,6 +263,8 @@ struct config_item config_tab[] = { PORT_ITEM_ENU("network_transport", TRANS_UDP_IPV4, nw_trans_enu), GLOB_ITEM_INT("ntpshm_segment", 0, INT_MIN, INT_MAX), GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX), + PORT_ITEM_INT("operLogPdelayReqInterval", 0, INT8_MIN, INT8_MAX), + PORT_ITEM_INT("operLogSyncInterval", 0, INT8_MIN, INT8_MAX), PORT_ITEM_INT("path_trace_enabled", 0, 0, 1), GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX), GLOB_ITEM_DBL("pi_integral_exponent", 0.4, -DBL_MAX, DBL_MAX), @@ -279,6 +281,8 @@ struct config_item config_tab[] = { PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"), GLOB_ITEM_STR("revisionData", ";;"), GLOB_ITEM_INT("sanity_freq_limit", 200000000, 0, INT_MAX), + GLOB_ITEM_INT("servo_num_offset_values", 10, 0, INT_MAX), + GLOB_ITEM_INT("servo_offset_threshold", 0, 0, INT_MAX), GLOB_ITEM_INT("slaveOnly", 0, 0, 1), GLOB_ITEM_DBL("step_threshold", 0.0, 0.0, DBL_MAX), GLOB_ITEM_INT("summary_interval", 0, INT_MIN, INT_MAX), diff --git a/configs/automotive-slave.cfg b/configs/automotive-slave.cfg index 94d45b6..8cc7221 100644 --- a/configs/automotive-slave.cfg +++ b/configs/automotive-slave.cfg @@ -31,3 +31,7 @@ asCapable true ignore_source_id 1 # Required to quickly correct Time Jumps in master step_threshold 1 +operLogSyncInterval 0 +operLogPdelayReqInterval 2 +servo_offset_threshold 30 +servo_num_offset_values 10 diff --git a/configs/default.cfg b/configs/default.cfg index 3ee3a9b..2433553 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -23,8 +23,10 @@ maxStepsRemoved 255 # logAnnounceInterval 1 logSyncInterval 0 +operLogSyncInterval 0 logMinDelayReqInterval 0 logMinPdelayReqInterval 0 +operLogPdelayReqInterval 0 announceReceiptTimeout 3 syncReceiptTimeout 0 delayAsymmetry 0 @@ -73,6 +75,8 @@ max_frequency 900000000 clock_servo pi sanity_freq_limit 200000000 ntpshm_segment 0 +servo_num_offset_values 10 +servo_offset_threshold 0 # # Transport options # diff --git a/phc2sys.c b/phc2sys.c index a476e63..cc48f18 100644 --- a/phc2sys.c +++ b/phc2sys.c @@ -619,6 +619,7 @@ static void update_clock(struct node *node, struct clock *clock, clockcheck_step(clock->sanity_check, -offset); /* Fall through. */ case SERVO_LOCKED: + case SERVO_LOCKED_STABLE: clockadj_set_freq(clock->clkid, -ppb); if (clock->clkid == CLOCK_REALTIME) sysclk_set_sync(); diff --git a/port.c b/port.c index 982daf2..0aaf846 100644 --- a/port.c +++ b/port.c @@ -1110,10 +1110,12 @@ static void port_slave_priority_warning(struct port *p) static void port_synchronize(struct port *p, tmv_t ingress_ts, struct timestamp origin_ts, - Integer64 correction1, Integer64 correction2) + Integer64 correction1, Integer64 correction2, + Integer8 sync_interval) { - enum servo_state state; + enum servo_state state, last_state; tmv_t t1, t1c, t2, c1, c2; + struct servo *s; port_set_sync_rx_tmo(p); @@ -1123,10 +1125,20 @@ static void port_synchronize(struct port *p, c2 = correction_to_tmv(correction2); t1c = tmv_add(t1, tmv_add(c1, c2)); + s = clock_servo(p->clock); + last_state = clock_servo_state(p->clock); state = clock_synchronize(p->clock, t2, t1c); switch (state) { case SERVO_UNLOCKED: port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); + if (servo_offset_threshold(s) != 0 && + sync_interval != p->initialLogSyncInterval) { + p->logPdelayReqInterval = p->logMinPdelayReqInterval; + p->logSyncInterval = p->initialLogSyncInterval; + port_tx_interval_request(p, SIGNAL_NO_CHANGE, + SIGNAL_SET_INITIAL, + SIGNAL_NO_CHANGE); + } break; case SERVO_JUMP: port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); @@ -1139,6 +1151,23 @@ static void port_synchronize(struct port *p, case SERVO_LOCKED: port_dispatch(p, EV_MASTER_CLOCK_SELECTED, 0); break; + case SERVO_LOCKED_STABLE: + if (last_state == SERVO_LOCKED) { + p->logPdelayReqInterval = p->operLogPdelayReqInterval; + p->logSyncInterval = p->operLogSyncInterval; + port_tx_interval_request(p, SIGNAL_NO_CHANGE, + p->logSyncInterval, + SIGNAL_NO_CHANGE); + port_dispatch(p, EV_MASTER_CLOCK_SELECTED, 0); + } else if (sync_interval != p->operLogSyncInterval) { + /* + * The most likely reason for this to happen is the + * master daemon re-initialized due to some fault. + */ + servo_reset(s); + port_dispatch(p, EV_SYNCHRONIZATION_FAULT, 0); + } + break; } } @@ -1192,7 +1221,8 @@ static void port_syfufsm(struct port *p, enum syfu_event event, syn = p->last_syncfup; port_synchronize(p, syn->hwts.ts, m->ts.pdu, syn->header.correction, - m->header.correction); + m->header.correction, + m->header.logMessageInterval); msg_put(p->last_syncfup); p->syfu = SF_EMPTY; break; @@ -1211,7 +1241,8 @@ static void port_syfufsm(struct port *p, enum syfu_event event, fup = p->last_syncfup; port_synchronize(p, m->hwts.ts, fup->ts.pdu, m->header.correction, - fup->header.correction); + fup->header.correction, + m->header.logMessageInterval); msg_put(p->last_syncfup); p->syfu = SF_EMPTY; break; @@ -1613,8 +1644,10 @@ int port_initialize(struct port *p) p->localPriority = config_get_int(cfg, p->name, "G.8275.portDS.localPriority"); p->initialLogSyncInterval = config_get_int(cfg, p->name, "logSyncInterval"); p->logSyncInterval = p->initialLogSyncInterval; + p->operLogSyncInterval = config_get_int(cfg, p->name, "operLogSyncInterval"); p->logMinPdelayReqInterval = config_get_int(cfg, p->name, "logMinPdelayReqInterval"); p->logPdelayReqInterval = p->logMinPdelayReqInterval; + p->operLogPdelayReqInterval = config_get_int(cfg, p->name, "operLogPdelayReqInterval"); p->neighborPropDelayThresh = config_get_int(cfg, p->name, "neighborPropDelayThresh"); p->min_neighbor_prop_delay = config_get_int(cfg, p->name, "min_neighbor_prop_delay"); @@ -2224,7 +2257,8 @@ void process_sync(struct port *p, struct ptp_message *m) if (one_step(m)) { port_synchronize(p, m->hwts.ts, m->ts.pdu, - m->header.correction, 0); + m->header.correction, 0, + m->header.logMessageInterval); flush_last_sync(p); return; } diff --git a/port_private.h b/port_private.h index d28ab07..bb1d86e 100644 --- a/port_private.h +++ b/port_private.h @@ -115,9 +115,11 @@ struct port { UInteger8 transportSpecific; UInteger8 localPriority; Integer8 initialLogSyncInterval; + Integer8 operLogSyncInterval; Integer8 logSyncInterval; Enumeration8 delayMechanism; Integer8 logMinPdelayReqInterval; + Integer8 operLogPdelayReqInterval; Integer8 logPdelayReqInterval; UInteger32 neighborPropDelayThresh; int follow_up_info; diff --git a/ptp4l.8 b/ptp4l.8 index 46e6715..df7da82 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -161,6 +161,13 @@ The mean time interval between Sync messages. A shorter interval may improve accuracy of the local clock. It's specified as a power of two in seconds. The default is 0 (1 second). .TP +.B operLogSyncInterval +The mean time interval between Sync messages. This value is only used by the +slave device when interval_update_timer is enabled. Slave will send this +interval to the master to switch to. This is done via a signaling message after +interval_update_timer expires. It's specified as a power of two in seconds. The +default value is 0 (1 second). +.TP .B logMinDelayReqInterval The minimum permitted mean time interval between Delay_Req messages. A shorter interval makes ptp4l react faster to the changes in the path delay. It's @@ -172,6 +179,13 @@ The minimum permitted mean time interval between Pdelay_Req messages. It's specified as a power of two in seconds. The default is 0 (1 second). .TP +.B operLogPdelayReqInterval +The mean time interval between Pdelay Request messages. This value is only used +by the slave device when interval_update_timer is enabled. Slave will switch to +the interval specified by this config option after the interval_update_timer +expires. It's specified as a power of two in seconds. The default value is 0 (1 +second). +.TP .B announceReceiptTimeout The number of missed Announce messages before the last Announce messages expires. @@ -715,6 +729,22 @@ This will disable source port identity checking for Sync and Follow_Up messages. This is useful when the announce messages are disabled in the master and the slave does not have any way to know it's identity. The default is 0 (disabled). +.TP +.B servo_num_offset_values +The number of offset values calculated in previously received Sync messages to +consider when adjusting the Sync and Pdelay request intervals. More information +provided in the description of 'offset_threshold'. The default value is 10. +.TP +.B servo_offset_threshold +This value is used by the slave for adjusting the intervals for Sync and Pdelay +request messages. The slave will check the last 'num_offset_values' offsets and +if all those offsets are less than the offset_threshold, it will adjust both +the intervals. The Sync interval is adjusted via the signaling mechanism and +the pdelay request interval is just adjusted locally. The new values to use for +sync message intervals and pdelay request intervals can be indicated by +operLogSyncInterval and operLogPdelayReqInterval respectively. This mechanism +is currently only supported when BMCA == 'noop'. The default +value of offset_threshold is 0 (disabled). .SH UNICAST DISCOVERY OPTIONS diff --git a/servo.c b/servo.c index 8be4b92..46042aa 100644 --- a/servo.c +++ b/servo.c @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include +#include #include "config.h" #include "linreg.h" @@ -25,6 +26,8 @@ #include "pi.h" #include "servo_private.h" +#include "print.h" + #define NSEC_PER_SEC 1000000000 struct servo *servo_create(struct config *cfg, enum servo_type type, @@ -79,6 +82,9 @@ struct servo *servo_create(struct config *cfg, enum servo_type type, } servo->first_update = 1; + servo->offset_threshold = config_get_int(cfg, NULL, "servo_offset_threshold"); + servo->num_offset_values = config_get_int(cfg, NULL, "servo_num_offset_values"); + servo->curr_offset_values = servo->num_offset_values; return servo; } @@ -88,6 +94,18 @@ void servo_destroy(struct servo *servo) servo->destroy(servo); } +static int check_offset_threshold(struct servo *s, int64_t offset) +{ + long long int abs_offset = llabs(offset); + + if (s->offset_threshold) { + if (abs_offset < s->offset_threshold && s->curr_offset_values) + s->curr_offset_values--; + return s->curr_offset_values ? 0 : 1; + } + return 0; +} + double servo_sample(struct servo *servo, int64_t offset, uint64_t local_ts, @@ -98,8 +116,28 @@ double servo_sample(struct servo *servo, r = servo->sample(servo, offset, local_ts, weight, state); - if (*state != SERVO_UNLOCKED) + switch (*state) { + case SERVO_UNLOCKED: + servo->curr_offset_values = servo->num_offset_values; + break; + case SERVO_JUMP: + servo->curr_offset_values = servo->num_offset_values; servo->first_update = 0; + break; + case SERVO_LOCKED: + if (check_offset_threshold(servo, offset)) { + *state = SERVO_LOCKED_STABLE; + } + servo->first_update = 0; + break; + case SERVO_LOCKED_STABLE: + /* + * This case will never occur since the only place + * SERVO_LOCKED_STABLE is set is in this switch/case block + * (case SERVO_LOCKED). + */ + break; + } return r; } @@ -127,3 +165,8 @@ void servo_leap(struct servo *servo, int leap) if (servo->leap) servo->leap(servo, leap); } + +int servo_offset_threshold(struct servo *servo) +{ + return servo->offset_threshold; +} diff --git a/servo.h b/servo.h index f90befd..6c30d33 100644 --- a/servo.h +++ b/servo.h @@ -57,6 +57,12 @@ enum servo_state { * The servo is tracking the master clock. */ SERVO_LOCKED, + + /** + * The Servo has stabilized. The last 'servo_num_offset_values' values + * of the estimated threshold are less than servo_offset_threshold. + */ + SERVO_LOCKED_STABLE, }; /** @@ -123,4 +129,11 @@ double servo_rate_ratio(struct servo *servo); */ void servo_leap(struct servo *servo, int leap); +/** + * Get the offset threshold for triggering the interval change request. + * @param servo Pointer to a servo obtained via @ref servo_create(). + * @return The offset threshold set by the user. + */ +int servo_offset_threshold(struct servo *servo); + #endif diff --git a/servo_private.h b/servo_private.h index 1c5cfdf..4d74ca2 100644 --- a/servo_private.h +++ b/servo_private.h @@ -29,6 +29,9 @@ struct servo { double step_threshold; double first_step_threshold; int first_update; + int64_t offset_threshold; + int num_offset_values; + int curr_offset_values; void (*destroy)(struct servo *servo);