Thanks for the feedback Erez,
My goal of this initial patch was to give an API to extract the statistics that
are already calculated/existing in the clock module and already printed to the
logs, i.e. the statistics that generate the numbers in this log message:
/* Path delay stats are updated separately, they may be empty. */
if (c->delay_stats_valid) {
pr_info("rms %4.0f max %4.0f "
"freq %+6.0f +/- %3.0f "
"delay %5.0f +/- %3.0f",
c->offset_stats.rms, c->offset_stats.max_abs,
c->freq_stats.mean, c->freq_stats.stddev,
c->delay_stats.mean, c->delay_stats.stddev);
} else {
pr_info("rms %4.0f max %4.0f "
"freq %+6.0f +/- %3.0f",
c->offset_stats.rms, c->offset_stats.max_abs,
c->freq_stats.mean, c->freq_stats.stddev);
The log message looks like this:
ptp4l[5024110.731]: rms 9 max 17 freq -17594 +/- 14 delay 220 +/- 1
One method of course would be to scrape the logs for these 6 numbers, but when
I looked into the implementation I found the other statistics so I decided to
include all of them in the CLOCK_STATS_NP TLV for thoroughness:
struct stats_result {
double min;
double max;
double max_abs;
double mean;
double rms;
double stddev;
};
As far as I can tell, the PARENT_DATA_SET TLV isn’t actually implemented?
/* Initialize the parentDS. */
clock_update_grandmaster(c);
c->dad.pds.parentStats = 0;
c->dad.pds.observedParentOffsetScaledLogVariance = 0xffff;
c->dad.pds.observedParentClockPhaseChangeRate = 0x7fffffff;
Doing a pmc get confirms this, at least for my setup:
sending: GET PARENT_DATA_SET
0c42a1.fffe.d1d1bd-0 seq 0 RESPONSE MANAGEMENT PARENT_DATA_SET
…
parentStats 0
observedParentOffsetScaledLogVariance 0xffff
observedParentClockPhaseChangeRate 0x7fffffff
…
After reading the relevant 1588-2019 paragraphs, I’m not 100% sure the existing
calculation of freq_stats and offset_stats are transformable to
“observedParentClockPhaseChangeRate” and
“observedParentOffsetScaledLogVariance”, respectively. Modification of these
existing stats could be future work after a more careful review of 1588-2019
(i.e. section 7.6.3, etc).
Thanks,
Tim
From: Geva, Erez <[email protected]>
Date: Wednesday, May 26, 2021 at 4:09 AM
To: Tim Martin <[email protected]>
Cc: [email protected] <[email protected]>
Subject: RE: [Linuxptp-devel] [PATCH] Add new CLOCK_STATS_NP TLV GET to pmc and
clock
External email: Use caution opening links or attachments
In IEEE 1588-2019
I see PARENT_DATA_SET with
- observedParentOffsetScaledLogVariance
- observedParentClockPhaseChangeRate
You use a natural statistics syntax.
Perhaps you may use a more coherent phrasing that match the standard.
May be something that match the performanceMonitoringDS, like
- averageMasterSlaveDelay
- minMasterSlaveDelay
...
Erez
-----Original Message-----
From: Tim Martin <[email protected]>
Sent: Tuesday, 25 May 2021 22:29
To: [email protected]
Subject: [Linuxptp-devel] [PATCH] Add new CLOCK_STATS_NP TLV GET to pmc and
clock
Currently there is no way to programmatically access statistics about the clock
frequency offset, time delay, or time offset (collectively, the "clock_stats"
metrics), except for parsing the ptp4l logs. One option for time offset would
be to poll TLV_TIME_STATUS_NP in regular intervals from a custom client, but
that has the disadvantage of either very high poll rates or missed updates
which would affect the statistics.
To address these issues, introduce a management mesage, CLOCK_STATS_NP, which
reports the statistics that would be displayed in the ptp4l log file. This
fixes the above problem by allowing a custom client to poll CLOCK_STATS_NP at
longer intervals without missing any updates.
Follow-up patches could then introduc knobs to configure the time over which
CLOCK_STATS_NP integrates statistics, separate from any other tracking loop
updates, which would allow even longer polling intervals.
Signed-off-by: Tim Martin <[email protected]>
---
clock.c | 50 ++++++++++++++++++++++++++++++++++----------------
pmc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
pmc_common.c | 1 +
tlv.h | 10 ++++++++++
4 files changed, 93 insertions(+), 16 deletions(-)
diff --git a/clock.c b/clock.c
index e545a9b..550d222 100644
--- a/clock.c
+++ b/clock.c
@@ -129,6 +129,9 @@ struct clock {
double nrr;
struct clock_description desc;
struct clock_stats stats;
+ struct stats_result offset_stats, freq_stats, delay_stats;
+ int offset_freq_stats_valid;
+ int delay_stats_valid;
int stats_interval;
struct clockcheck *sanity_check;
struct interface *uds_rw_if;
@@ -144,7 +147,7 @@ struct clock the_clock; static void
handle_state_decision_event(struct clock *c); static int
clock_resize_pollfd(struct clock *c, int new_nports); static void
clock_remove_port(struct clock *c, struct port *p); -static void
clock_stats_display(struct clock_stats *s);
+static void clock_stats_display(struct clock *c);
static void remove_subscriber(struct clock_subscriber *s) { @@ -351,6 +354,7
@@ static int clock_management_fill_response(struct clock *c, struct port *p,
struct tlv_extra *extra;
struct PTPText *text;
uint16_t duration;
+ struct clock_stats_np_tlv *stats;
int datalen = 0;
extra = tlv_extra_alloc();
@@ -462,6 +466,15 @@ static int clock_management_fill_response(struct clock *c,
struct port *p,
mtd->val = c->local_sync_uncertain;
datalen = sizeof(*mtd);
break;
+ case TLV_CLOCK_STATS_NP:
+ stats = (struct clock_stats_np_tlv *) tlv->data;
+ stats->offset_stats = c->offset_stats;
+ stats->freq_stats = c->freq_stats;
+ stats->delay_stats = c->delay_stats;
+ stats->offset_freq_stats_valid = c->offset_freq_stats_valid;
+ stats->delay_stats_valid = c->delay_stats_valid;
+ datalen = sizeof(*stats);
+ break;
default:
/* The caller should *not* respond to this message. */
tlv_extra_recycle(extra);
@@ -544,7 +557,7 @@ static int clock_management_set(struct clock *c, struct
port *p,
/* Display stats on change of local_sync_uncertain */
if (c->local_sync_uncertain != mtd->val
&& stats_get_num_values(c->stats.offset))
- clock_stats_display(&c->stats);
+ clock_stats_display(c);
c->local_sync_uncertain = mtd->val;
respond = 1;
break;
@@ -556,38 +569,41 @@ static int clock_management_set(struct clock *c, struct
port *p,
return respond ? 1 : 0;
}
-static void clock_stats_update(struct clock_stats *s,
+static void clock_stats_update(struct clock *c,
double offset, double freq)
{
+ struct clock_stats *s = &c->stats;
stats_add_value(s->offset, offset);
stats_add_value(s->freq, freq);
if (stats_get_num_values(s->offset) < s->max_count)
return;
- clock_stats_display(s);
+ clock_stats_display(c);
}
-static void clock_stats_display(struct clock_stats *s)
+static void clock_stats_display(struct clock *c)
{
- struct stats_result offset_stats, freq_stats, delay_stats;
+ struct clock_stats *s = &c->stats;
- stats_get_result(s->offset, &offset_stats);
- stats_get_result(s->freq, &freq_stats);
+ stats_get_result(s->offset, &c->offset_stats);
+ stats_get_result(s->freq, &c->freq_stats);
+ c->offset_freq_stats_valid = 1;
+ c->delay_stats_valid = !stats_get_result(s->delay, &c->delay_stats);
/* Path delay stats are updated separately, they may be empty. */
- if (!stats_get_result(s->delay, &delay_stats)) {
+ if (c->delay_stats_valid) {
pr_info("rms %4.0f max %4.0f "
"freq %+6.0f +/- %3.0f "
"delay %5.0f +/- %3.0f",
- offset_stats.rms, offset_stats.max_abs,
- freq_stats.mean, freq_stats.stddev,
- delay_stats.mean, delay_stats.stddev);
+ c->offset_stats.rms, c->offset_stats.max_abs,
+ c->freq_stats.mean, c->freq_stats.stddev,
+ c->delay_stats.mean, c->delay_stats.stddev);
} else {
pr_info("rms %4.0f max %4.0f "
"freq %+6.0f +/- %3.0f",
- offset_stats.rms, offset_stats.max_abs,
- freq_stats.mean, freq_stats.stddev);
+ c->offset_stats.rms, c->offset_stats.max_abs,
+ c->freq_stats.mean, c->freq_stats.stddev);
}
stats_reset(s->offset);
@@ -638,7 +654,7 @@ static enum servo_state clock_no_adjust(struct clock *c,
tmv_t ingress,
freq = (1.0 - ratio) * 1e9;
if (c->stats.max_count > 1) {
- clock_stats_update(&c->stats, tmv_dbl(c->master_offset), freq);
+ clock_stats_update(c, tmv_dbl(c->master_offset), freq);
} else {
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
"path delay %9" PRId64,
@@ -1190,6 +1206,8 @@ struct clock *clock_create(enum clock_type type, struct
config *config,
pr_err("failed to create stats");
return NULL;
}
+ c->offset_freq_stats_valid = 0;
+ c->delay_stats_valid = 0;
sfl = config_get_int(config, NULL, "sanity_freq_limit");
if (sfl) {
c->sanity_check = clockcheck_create(sfl); @@ -1857,7 +1875,7 @@
enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin)
}
if (c->stats.max_count > 1) {
- clock_stats_update(&c->stats, tmv_dbl(c->master_offset), adj);
+ clock_stats_update(c, tmv_dbl(c->master_offset), adj);
} else {
pr_info("master offset %10" PRId64 " s%d freq %+7.0f "
"path delay %9" PRId64,
diff --git a/pmc.c b/pmc.c
index 00d6014..a405f65 100644
--- a/pmc.c
+++ b/pmc.c
@@ -155,6 +155,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
struct parentDS *pds;
struct portDS *p;
struct TLV *tlv;
+ struct clock_stats_np_tlv *stats;
int action;
if (msg_type(msg) == SIGNALING) {
@@ -525,6 +526,53 @@ static void pmc_show(struct ptp_message *msg, FILE *fp)
fprintf(fp, "LOG_MIN_PDELAY_REQ_INTERVAL "
IFMT "logMinPdelayReqInterval %hhd", mtd->val);
break;
+ case TLV_CLOCK_STATS_NP:
+ stats = (struct clock_stats_np_tlv *) mgt->data;
+ fprintf(fp, "CLOCK_STATS_NP " IFMT);
+ if (stats->offset_freq_stats_valid) {
+ fprintf(fp,
+ "offset min %lf"
+ IFMT "offset max %lf"
+ IFMT "offset max_abs %lf"
+ IFMT "offset mean %lf"
+ IFMT "offset rms %lf"
+ IFMT "offset stdev %lf"
+ IFMT "freq min %lf"
+ IFMT "freq max %lf"
+ IFMT "freq max_abs %lf"
+ IFMT "freq mean %lf"
+ IFMT "freq rms %lf"
+ IFMT "freq stdev %lf"
+ IFMT,
+ stats->offset_stats.min,
+ stats->offset_stats.max,
+ stats->offset_stats.max_abs,
+ stats->offset_stats.mean,
+ stats->offset_stats.rms,
+ stats->offset_stats.stddev,
+ stats->freq_stats.min,
+ stats->freq_stats.max,
+ stats->freq_stats.max_abs,
+ stats->freq_stats.mean,
+ stats->freq_stats.rms,
+ stats->freq_stats.stddev);
+ }
+ if (stats->delay_stats_valid) {
+ fprintf(fp,
+ "delay min %lf"
+ IFMT "delay max %lf"
+ IFMT "delay max_abs %lf"
+ IFMT "delay mean %lf"
+ IFMT "delay rms %lf"
+ IFMT "delay stdev %lf",
+ stats->delay_stats.min,
+ stats->delay_stats.max,
+ stats->delay_stats.max_abs,
+ stats->delay_stats.mean,
+ stats->delay_stats.rms,
+ stats->delay_stats.stddev);
+ }
+ break;
}
out:
fprintf(fp, "\n");
diff --git a/pmc_common.c b/pmc_common.c index 101df55..a97e156 100644
--- a/pmc_common.c
+++ b/pmc_common.c
@@ -132,6 +132,7 @@ struct management_id idtab[] = {
{ "PORT_DATA_SET_NP", TLV_PORT_DATA_SET_NP, do_set_action },
{ "PORT_STATS_NP", TLV_PORT_STATS_NP, do_get_action },
{ "PORT_PROPERTIES_NP", TLV_PORT_PROPERTIES_NP, do_get_action },
+ { "CLOCK_STATS_NP", TLV_CLOCK_STATS_NP, do_get_action },
};
static void do_get_action(struct pmc *pmc, int action, int index, char *str)
diff --git a/tlv.h b/tlv.h index a205119..58857b2 100644
--- a/tlv.h
+++ b/tlv.h
@@ -24,6 +24,7 @@
#include "ddt.h"
#include "ds.h"
+#include "stats.h"
/* TLV types */
#define TLV_MANAGEMENT 0x0001
@@ -100,6 +101,7 @@ enum management_action {
#define TLV_GRANDMASTER_SETTINGS_NP 0xC001
#define TLV_SUBSCRIBE_EVENTS_NP 0xC003
#define TLV_SYNCHRONIZATION_UNCERTAIN_NP 0xC006
+#define TLV_CLOCK_STATS_NP 0xC007
/* Port management ID values */
#define TLV_NULL_MANAGEMENT 0x0000
@@ -180,6 +182,14 @@ struct management_tlv_datum {
uint8_t reserved;
} PACKED;
+struct clock_stats_np_tlv {
+ struct stats_result offset_stats;
+ struct stats_result freq_stats;
+ struct stats_result delay_stats;
+ uint8_t offset_freq_stats_valid;
+ uint8_t delay_stats_valid;
+} PACKED;
+
struct management_error_status {
Enumeration16 type;
UInteger16 length;
--
2.31.1
_______________________________________________
Linuxptp-devel mailing list
[email protected]
https://nam11.safelinks.protection.outlook.com/?url=https%3A%2F%2Flists.sourceforge.net%2Flists%2Flistinfo%2Flinuxptp-devel&data=04%7C01%7Ctimothym%40nvidia.com%7C969837d3e5dd4b02f94d08d92036c211%7C43083d15727340c1b7db39efd9ccc17a%7C0%7C0%7C637576241821860507%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C1000&sdata=gizbmB6l0a8hTbCuv9Rh5akGM0%2B4dlQli%2FvIYhi2Sl4%3D&reserved=0
_______________________________________________
Linuxptp-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel