From: Mike Brady <4265913+mikebrady@users.noreply.github.com> Date: Thu, 24 Jun 2021 09:23:20 +0000 (+0100) Subject: Add a new state clock_is_becoming_master and don't promote to master until at least... X-Git-Tag: 1.2~113^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b8997bc0856184ce9482ee4330665fd29fb13b69;p=thirdparty%2Fnqptp.git Add a new state clock_is_becoming_master and don't promote to master until at least one samples has been received after the mode clock_is_becoming_master is entered. --- diff --git a/nqptp-clock-sources.c b/nqptp-clock-sources.c index 6d47920..b173515 100644 --- a/nqptp-clock-sources.c +++ b/nqptp-clock-sources.c @@ -155,6 +155,48 @@ void update_clock_self_identifications(clock_source_private_data *clocks_private freeifaddrs(ifap); } +void debug_log_nqptp_status(int level) { + int records_in_use = 0; + int i; + for (i = 0; i < MAX_CLOCKS; i++) + if (clocks_private[i].in_use != 0) + records_in_use++; + if (records_in_use > 0) { + debug(level, ""); + debug(level, "Current NQPTP Status:"); + uint32_t peer_mask = (1 << clock_is_a_timing_peer); + uint32_t peer_clock_mask = peer_mask | (1 << clock_is_valid); + uint32_t peer_master_mask = peer_clock_mask | (1 << clock_is_master); + uint32_t peer_becoming_master_mask = peer_clock_mask | (1 << clock_is_becoming_master); + uint32_t non_peer_clock_mask = (1 << clock_is_valid); + uint32_t non_peer_master_mask = non_peer_clock_mask | (1 << clock_is_master); + for (i = 0; i < MAX_CLOCKS; i++) { + if (clocks_private[i].in_use != 0) { + if ((clocks_private[i].flags & peer_master_mask) == peer_master_mask) { + debug(level, " Peer Master: %" PRIx64 " %s.", clocks_private[i].clock_id, + clocks_private[i].ip); + } else if ((clocks_private[i].flags & peer_becoming_master_mask) == peer_becoming_master_mask) { + debug(level, " Peer Becoming Master: %" PRIx64 " %s.", clocks_private[i].clock_id, + clocks_private[i].ip); + } else if ((clocks_private[i].flags & peer_clock_mask) == peer_clock_mask) { + debug(level, " Peer Clock: %" PRIx64 " %s.", clocks_private[i].clock_id, + clocks_private[i].ip); + } else if ((clocks_private[i].flags & peer_mask) == peer_mask) { + debug(level, " Peer: %s.", clocks_private[i].ip); + } else if ((clocks_private[i].flags & non_peer_master_mask) == non_peer_master_mask) { + debug(level, " Non Peer Master: %" PRIx64 " %s.", clocks_private[i].clock_id, + clocks_private[i].ip); + } else if ((clocks_private[i].flags & non_peer_clock_mask) == non_peer_clock_mask) { + debug(level, " Non Peer Clock: %16" PRIx64 " %s.", clocks_private[i].clock_id, + clocks_private[i].ip); + } else { + debug(level, " Non Peer Record: %s.", clocks_private[i].ip); + } + } + } + } +} + void update_master() { // note -- this is definitely incomplete -- it doesn't do the full // data set comparison specified by the IEEE 588 standard @@ -166,6 +208,7 @@ void update_master() { if (old_master == -1) old_master = i; // find old master clocks_private[i].flags &= ~(1 << clock_is_master); // turn them all off + clocks_private[i].flags &= ~(1 << clock_is_becoming_master); // turn them all off } int best_so_far = -1; @@ -218,146 +261,16 @@ void update_master() { debug(1, "No master clock not found!"); } else { // we found a master clock - clocks_private[best_so_far].flags |= (1 << clock_is_master); - - if (old_master != best_so_far) { - uint64_t oldest_acceptable_master_clock_time = - clocks_private[best_so_far].source_time + 1150000000; // ns. - - // we will try to improve on this present, definitive, local_to_source_time_offset we have - int changes_made = 0; - - uint64_t best_offset_so_far = clocks_private[best_so_far].local_to_source_time_offset; - uint64_t age_of_oldest_legitimate_sample = clocks_private[best_so_far].local_time; - - int number_of_samples = MAX_TIMING_SAMPLES - clocks_private[best_so_far].vacant_samples; - int samples_checked = 0; - if (number_of_samples > 0) { - debug(3, "Number of samples: %d.", number_of_samples); - uint64_t time_now = get_time_now(); - - // Now we use the last few samples to calculate the best offset for the - // new master clock. - - // The time of the oldest sample we use will become the time of the start of the - // mastership - - // We will accept samples that would make the local-to-clock offset greatest, - // provided they are not too old and that they don't push the current clock time - // more than, say, 1000 ms plus one sample interval (i.e about 1.125 seconds) in the future. - - // This present sample is the only time estimate we have when the clock is definitely a - // master, so we use it to eliminate any previous time estimates, made when the clock wasn't - // designated a master, that would put it more than, say, a 1.15 seconds further into the - // future. - // Allow the samples to give a valid master clock time up to this much later than the - // present, definitive, sample: - uint64_t oldest_acceptable_time = time_now - 10000000000; // only go back this far (ns) - - int64_t cko = age_of_oldest_legitimate_sample - oldest_acceptable_time; - if (cko < 0) - debug(1,"starting sample is too old: %" PRId64 " ns.", cko); - - int i; - for (i = 0; i < number_of_samples; i++) { - - int64_t age = time_now - clocks_private[best_so_far].samples[i].local_time; - - int64_t age_relative_to_oldest_acceptable_time = - clocks_private[best_so_far].samples[i].local_time - oldest_acceptable_time; - if (age_relative_to_oldest_acceptable_time > 0) { - debug(3, "sample accepted at %f seconds old.", 0.000000001 * age); - if (clocks_private[best_so_far].samples[i].local_time < - age_of_oldest_legitimate_sample) { - age_of_oldest_legitimate_sample = clocks_private[best_so_far].samples[i].local_time; - } - uint64_t possible_offset = clocks_private[best_so_far].samples[i].clock_time - - clocks_private[best_so_far].samples[i].local_time; - uint64_t possible_master_clock_time = - clocks_private[best_so_far].local_time + possible_offset; - int64_t age_relative_to_oldest_acceptable_master_clock_time = - possible_master_clock_time - oldest_acceptable_master_clock_time; - if (age_relative_to_oldest_acceptable_master_clock_time <= 0) { - samples_checked++; - // so, the sample was not obtained too far in the past - // and it would not push the estimated master clock_time too far into the future - // so, if it is greater than the best_offset_so_far, then make it the new one - if (possible_offset > best_offset_so_far) { - debug(3, "new best offset"); - best_offset_so_far = possible_offset; - changes_made++; - } - } else { - debug(3, "sample too far into the future"); - } - } else { - debug(3, "sample too old at %f seconds old.", 0.000000001 * age); - } - } - } - - // it is possible that the clock has been designated master without any valid recent samples - // in which case the number of valid samples will be zero. - if (samples_checked == 0) { - debug(2,"clock %" PRIx64 " has become master without any recent samples...", clocks_private[best_so_far].clock_id); - // having no samples is a flag for the first FOLLOW_UP to set the bus mastership start time - // to its own reception time. - clocks_private[best_so_far].vacant_samples = MAX_TIMING_SAMPLES; // discard all samples - } else { - clocks_private[best_so_far].mastership_start_time = age_of_oldest_legitimate_sample; - int64_t offset_difference = - best_offset_so_far - clocks_private[best_so_far].local_to_source_time_offset; - - debug(2, "Lookback difference: %f ms with %d samples checked of %d samples total.", - 0.000001 * offset_difference, samples_checked, number_of_samples); - clocks_private[best_so_far].local_to_source_time_offset = best_offset_so_far; - - debug(2, "Master sampling started %f ms before becoming master.", - 0.000001 * (clocks_private[best_so_far].local_time - age_of_oldest_legitimate_sample)); - update_master_clock_info(clocks_private[best_so_far].clock_id, - (const char *)&clocks_private[best_so_far].ip, - clocks_private[best_so_far].local_time, - clocks_private[best_so_far].local_to_source_time_offset, - clocks_private[best_so_far].mastership_start_time); - } + if (old_master != best_so_far) { + // if the naster is a new one clocks_private[best_so_far].previous_offset_time = 0; // resync + clocks_private[best_so_far].flags |= (1 << clock_is_becoming_master); + } else { + // if its the same one as before + clocks_private[best_so_far].flags |= (1 << clock_is_master); } } - - int records_in_use = 0; - for (i = 0; i < MAX_CLOCKS; i++) - if (clocks_private[i].in_use != 0) - records_in_use++; - if (records_in_use > 0) { - debug(1, ""); - debug(1, "Current NQPTP Status:"); - uint32_t peer_mask = (1 << clock_is_a_timing_peer); - uint32_t peer_clock_mask = peer_mask | (1 << clock_is_valid); - uint32_t peer_master_mask = peer_clock_mask | (1 << clock_is_master); - uint32_t non_peer_clock_mask = (1 << clock_is_valid); - uint32_t non_peer_master_mask = non_peer_clock_mask | (1 << clock_is_master); - for (i = 0; i < MAX_CLOCKS; i++) { - if (clocks_private[i].in_use != 0) { - if ((clocks_private[i].flags & peer_master_mask) == peer_master_mask) { - debug(1, " Peer Master: %" PRIx64 " %s.", clocks_private[i].clock_id, - clocks_private[i].ip); - } else if ((clocks_private[i].flags & peer_clock_mask) == peer_clock_mask) { - debug(1, " Peer Clock: %" PRIx64 " %s.", clocks_private[i].clock_id, - clocks_private[i].ip); - } else if ((clocks_private[i].flags & peer_mask) == peer_mask) { - debug(1, " Peer: %s.", clocks_private[i].ip); - } else if ((clocks_private[i].flags & non_peer_master_mask) == non_peer_master_mask) { - debug(1, " Non Peer Master: %" PRIx64 " %s.", clocks_private[i].clock_id, - clocks_private[i].ip); - } else if ((clocks_private[i].flags & non_peer_clock_mask) == non_peer_clock_mask) { - debug(1, " Non Peer Clock: %" PRIx64 " %s.", clocks_private[i].clock_id, - clocks_private[i].ip); - } else { - debug(1, " Non Peer Record: %s.", clocks_private[i].ip); - } - } - } - } + debug_log_nqptp_status(1); } diff --git a/nqptp-clock-sources.h b/nqptp-clock-sources.h index 68de9e2..c0674d6 100644 --- a/nqptp-clock-sources.h +++ b/nqptp-clock-sources.h @@ -29,6 +29,7 @@ typedef enum { clock_is_valid, clock_is_a_timing_peer, clock_is_qualified, + clock_is_becoming_master, clock_is_master } clock_flags; @@ -89,4 +90,6 @@ extern clock_source_private_data clocks_private[MAX_CLOCKS]; void update_master(); +void debug_log_nqptp_status(int level); + #endif diff --git a/nqptp-message-handlers.c b/nqptp-message-handlers.c index da3114a..46e91da 100644 --- a/nqptp-message-handlers.c +++ b/nqptp-message-handlers.c @@ -244,13 +244,100 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len, clock_private_info->next_sample_goes_here = 0; debug(2, "FOLLOWUP from %" PRIx64 ", %s.", clock_private_info->clock_id, &clock_private_info->ip); - uint64_t offset = preciseOriginTimestamp - reception_time; - int64_t jitter = 0; - // if there has never been a previous follow_up or if it was long ago (more than 15 seconds), - // don't use it - if (clock_private_info->previous_offset_time != 0) { + + if ((clock_private_info->flags & (1 << clock_is_becoming_master)) != 0) { + // we definitely have at least one sample since the request was made to + // designate it a master, so we assume it is legitimate. That is, we assume + // that the clock originator knows that it a clock master by now. + uint64_t oldest_acceptable_master_clock_time = + clock_private_info->source_time + 1150000000; // ns. + + // we will try to improve on this present, definitive, local_to_source_time_offset we have + int changes_made = 0; + + uint64_t best_offset_so_far = clock_private_info->local_to_source_time_offset; + uint64_t age_of_oldest_legitimate_sample = clock_private_info->local_time; + + int number_of_samples = MAX_TIMING_SAMPLES - clock_private_info->vacant_samples; + int samples_checked = 0; + if (number_of_samples > 0) { + debug(3, "Number of samples: %d.", number_of_samples); + + // Now we use the last few samples to calculate the best offset for the + // new master clock. + + // The time of the oldest sample we use will become the time of the start of the + // mastership. + + // We will accept samples that would make the local-to-clock offset greatest, + // provided they are not too old and that they don't push the current clock time + // more than, say, 1000 ms plus one sample interval (i.e about 1.125 seconds) in the future. + + // This present sample is the only time estimate we have when the clock is definitely a + // master, so we use it to eliminate any previous time estimates, made when the clock wasn't + // designated a master, that would put it more than, say, a 1.15 seconds further into the + // future. + + // Allow the samples to give a valid master clock time up to this much later than the + // present, definitive, sample: + + uint64_t oldest_acceptable_time = reception_time - 10000000000; // only go back this far (ns) + + int64_t cko = age_of_oldest_legitimate_sample - oldest_acceptable_time; + if (cko < 0) + debug(1,"starting sample is too old: %" PRId64 " ns.", cko); + + int i; + for (i = 0; i < number_of_samples; i++) { + int64_t age = reception_time - clock_private_info->samples[i].local_time; + int64_t age_relative_to_oldest_acceptable_time = + clock_private_info->samples[i].local_time - oldest_acceptable_time; + if (age_relative_to_oldest_acceptable_time > 0) { + debug(3, "sample accepted at %f seconds old.", 0.000000001 * age); + if (clock_private_info->samples[i].local_time < + age_of_oldest_legitimate_sample) { + age_of_oldest_legitimate_sample = clock_private_info->samples[i].local_time; + } + uint64_t possible_offset = clock_private_info->samples[i].clock_time - + clock_private_info->samples[i].local_time; + uint64_t possible_master_clock_time = + clock_private_info->local_time + possible_offset; + int64_t age_relative_to_oldest_acceptable_master_clock_time = + possible_master_clock_time - oldest_acceptable_master_clock_time; + if (age_relative_to_oldest_acceptable_master_clock_time <= 0) { + samples_checked++; + // so, the sample was not obtained too far in the past + // and it would not push the estimated master clock_time too far into the future + // so, if it is greater than the best_offset_so_far, then make it the new one + if (possible_offset > best_offset_so_far) { + debug(3, "new best offset"); + best_offset_so_far = possible_offset; + changes_made++; + } + } else { + debug(3, "sample too far into the future"); + } + } else { + debug(3, "sample too old at %f seconds old.", 0.000000001 * age); + } + } + } + clock_private_info->mastership_start_time = age_of_oldest_legitimate_sample; + int64_t offset_difference = + best_offset_so_far - clock_private_info->local_to_source_time_offset; + + debug(2, "Lookback difference: %f ms with %d samples checked of %d samples total.", + 0.000001 * offset_difference, samples_checked, number_of_samples); + clock_private_info->local_to_source_time_offset = best_offset_so_far; + + debug(2, "Master sampling started %f ms before becoming master.", + 0.000001 * (reception_time - age_of_oldest_legitimate_sample)); + clock_private_info->flags &= ~(1 << clock_is_becoming_master); + clock_private_info->flags |= 1 << clock_is_master; + } else if (clock_private_info->previous_offset_time != 0) { + // i.e. if it's not becoming a master and there has been a previous follow_up int64_t time_since_last_sync = reception_time - clock_private_info->last_sync_time; int64_t sync_timeout = 60000000000; // nanoseconds debug(2, "Sync interval: %f seconds.", 0.000000001 * time_since_last_sync); @@ -295,23 +382,11 @@ void handle_follow_up(char *buf, __attribute__((unused)) ssize_t recv_len, clock_private_info->last_sync_time = reception_time; } - uint32_t old_flags = clock_private_info->flags; - clock_private_info->local_time = reception_time; clock_private_info->source_time = preciseOriginTimestamp; clock_private_info->local_to_source_time_offset = offset; - if (old_flags != clock_private_info->flags) { - update_master(); - } else if ((clock_private_info->flags & (1 << clock_is_master)) != 0) { - // if there were no prior samples, then this must be the first sample with this clock - // as master. Therefore set its mastership_start_time to the reception time. - if (clock_private_info->vacant_samples == (MAX_TIMING_SAMPLES - 1)) { - debug(2,"Clock %" PRIx64 " became a bus master with no valid prior samples -- this is its first one.", clock_private_info->clock_id); - clock_private_info->mastership_start_time = reception_time; - } - - + if ((clock_private_info->flags & (1 << clock_is_master)) != 0) { update_master_clock_info(clock_private_info->clock_id, (const char *)&clock_private_info->ip, reception_time, offset, clock_private_info->mastership_start_time); debug(3, "clock: %" PRIx64 ", time: %" PRIu64 ", offset: %" PRId64 ", jitter: %+f ms.", diff --git a/nqptp.c b/nqptp.c index 603564a..f90c364 100644 --- a/nqptp.c +++ b/nqptp.c @@ -66,7 +66,7 @@ int epoll_fd; void update_master_clock_info(uint64_t master_clock_id, const char *ip, uint64_t local_time, uint64_t local_to_master_offset, uint64_t mastership_start_time) { if (shared_memory->master_clock_id != master_clock_id) - debug(1, "Master clock is: %" PRIx64 ".", master_clock_id); + debug_log_nqptp_status(1); int rc = pthread_mutex_lock(&shared_memory->shm_mutex); if (rc != 0) warn("Can't acquire mutex to update master clock!");