--- config.c | 3 + ddt.h | 2 + e2e_tc.c | 36 +++++++ fd.h | 1 + makefile | 2 +- msg.h | 2 +- notification.h | 3 + p2p_tc.c | 36 +++++++ pmc.c | 123 +++++++++++++++++++++- pmc_common.c | 66 +++++++++++- port.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++- port_pm.c | 212 +++++++++++++++++++++++++++++++++++++ port_pm.h | 141 +++++++++++++++++++++++++ port_private.h | 7 ++ tlv.c | 80 ++++++++++++++ tlv.h | 35 +++++++ 16 files changed, 1017 insertions(+), 9 deletions(-) create mode 100644 port_pm.c create mode 100644 port_pm.h
diff --git a/config.c b/config.c index b104f1b..bc9852d 100644 --- a/config.c +++ b/config.c @@ -298,6 +298,9 @@ struct config_item config_tab[] = { 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("performance_monitor.15m_history", 0, 0, INT16_MAX), + PORT_ITEM_INT("performance_monitor.1h_history", 0, 0, INT16_MAX), + PORT_ITEM_INT("performance_monitor.24h_history", 0, 0, INT16_MAX), PORT_ITEM_INT("path_trace_enabled", 0, 0, 1), PORT_ITEM_INT("phc_index", -1, -1, INT_MAX), GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX), diff --git a/ddt.h b/ddt.h index 5dc5530..9016857 100644 --- a/ddt.h +++ b/ddt.h @@ -26,6 +26,8 @@ typedef Integer64 TimeInterval; /* nanoseconds << 16 */ +typedef Integer64 PMTimestamp; /* seconds */ + /** On the wire time stamp format. */ struct Timestamp { uint16_t seconds_msb; /* 16 bits + */ diff --git a/e2e_tc.c b/e2e_tc.c index 2f8e821..e6d8f3d 100644 --- a/e2e_tc.c +++ b/e2e_tc.c @@ -19,6 +19,7 @@ #include <errno.h> #include "port.h" +#include "port_pm.h" #include "port_private.h" #include "print.h" #include "rtnl.h" @@ -121,6 +122,41 @@ enum fsm_event e2e_event(struct port *p, int fd_index) pr_err("unexpected timer expiration"); return EV_NONE; + case FD_PM_TIMER: + pr_debug("%s: performance monitor timeout", p->log_name); + { + struct timespec now; + bool is_pm15m, is_pm1h, is_pm24h; + + clock_gettime(CLOCK_REALTIME, &now); + + pm_type(&now, &is_pm15m, &is_pm1h, &is_pm24h); + + if (is_pm15m) { + // 15min pm expired + port_pm_store_sample(p->pm15min, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm15min, now.tv_sec); + port_notify_event(p, NOTIFY_PM15M_UPDATE); + } + + if (is_pm1h) { + // 15min pm expired + port_pm_store_sample(p->pm1h, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm1h, now.tv_sec); + port_notify_event(p, NOTIFY_PM1H_UPDATE); + } + + if (is_pm24h) { + // 15min pm expired + port_pm_store_sample(p->pm24h, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm24h, now.tv_sec); + port_notify_event(p, NOTIFY_PM24H_UPDATE); + } + + port_tmo_pm(p, port_pm_period(p)); + } + return EV_NONE; + case FD_RTNL: pr_debug("%s: received link status notification", p->log_name); rtnl_link_status(fd, p->name, port_link_status, p); diff --git a/fd.h b/fd.h index 16420d7..df9ccd9 100644 --- a/fd.h +++ b/fd.h @@ -40,6 +40,7 @@ enum { FD_UNICAST_REQ_TIMER, FD_UNICAST_SRV_TIMER, FD_RTNL, + FD_PM_TIMER, N_POLLFD, }; diff --git a/makefile b/makefile index 3e3b8b3..16f4924 100644 --- a/makefile +++ b/makefile @@ -30,7 +30,7 @@ TS2PHC = ts2phc.o lstab.o nmea.o serial.o sock.o ts2phc_generic_pps_source.o \ ts2phc_nmea_pps_source.o ts2phc_phc_pps_source.o ts2phc_pps_sink.o ts2phc_pps_source.o OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \ e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o monitor.o msg.o phc.o \ - port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) \ + port.o port_pm.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) \ sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \ unicast_fsm.o unicast_service.o util.o version.o diff --git a/msg.h b/msg.h index 484435d..86fc346 100644 --- a/msg.h +++ b/msg.h @@ -178,7 +178,7 @@ struct management_msg { } PACKED; struct message_data { - uint8_t buffer[1500]; + uint8_t buffer[9000]; } PACKED; struct ptp_message { diff --git a/notification.h b/notification.h index 115f864..20c85e3 100644 --- a/notification.h +++ b/notification.h @@ -44,6 +44,9 @@ static inline bool event_bitmask_get(uint8_t *bitmask, unsigned int event) enum notification { NOTIFY_PORT_STATE, NOTIFY_TIME_SYNC, + NOTIFY_PM15M_UPDATE, + NOTIFY_PM1H_UPDATE, + NOTIFY_PM24H_UPDATE, }; #endif diff --git a/p2p_tc.c b/p2p_tc.c index 75cb3b9..3b2ce9c 100644 --- a/p2p_tc.c +++ b/p2p_tc.c @@ -19,6 +19,7 @@ #include <errno.h> #include "port.h" +#include "port_pm.h" #include "port_private.h" #include "print.h" #include "rtnl.h" @@ -124,6 +125,41 @@ enum fsm_event p2p_event(struct port *p, int fd_index) pr_err("unexpected timer expiration"); return EV_NONE; + case FD_PM_TIMER: + pr_debug("%s: performance monitor timeout", p->log_name); + { + struct timespec now; + bool is_pm15m, is_pm1h, is_pm24h; + + clock_gettime(CLOCK_REALTIME, &now); + + pm_type(&now, &is_pm15m, &is_pm1h, &is_pm24h); + + if (is_pm15m) { + // 15min pm expired + port_pm_store_sample(p->pm15min, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm15min, now.tv_sec); + port_notify_event(p, NOTIFY_PM15M_UPDATE); + } + + if (is_pm1h) { + // 15min pm expired + port_pm_store_sample(p->pm1h, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm1h, now.tv_sec); + port_notify_event(p, NOTIFY_PM1H_UPDATE); + } + + if (is_pm24h) { + // 15min pm expired + port_pm_store_sample(p->pm24h, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm24h, now.tv_sec); + port_notify_event(p, NOTIFY_PM24H_UPDATE); + } + + port_tmo_pm(p, port_pm_period(p)); + } + return EV_NONE; + case FD_RTNL: pr_debug("%s: received link status notification", p->log_name); rtnl_link_status(fd, p->name, port_link_status, p); diff --git a/pmc.c b/pmc.c index bc87058..9bb9b30 100644 --- a/pmc.c +++ b/pmc.c @@ -39,6 +39,7 @@ static struct pmc *pmc; #define IFMT "\n\t\t" +#define IFMT2 "\n\t\t\t" #define P41 ((double)(1ULL << 41)) static char *text2str(struct PTPText *text) @@ -155,6 +156,32 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp) fflush(fp); } +static const char *pmc_show_pm_type(Enumeration16 id) +{ + switch (id) { + case MID_PM15M_LAST_NP: + return "PM15M_LAST_NP"; + case MID_PM15M_CURRENT_NP: + return "PM15M_CURRENT_NP"; + case MID_PM15M_HISTORY_NP: + return "PM15M_HISTORY_NP"; + case MID_PM1H_LAST_NP: + return "PM1H_LAST_NP"; + case MID_PM1H_CURRENT_NP: + return "PM1H_CURRENT_NP"; + case MID_PM1H_HISTORY_NP: + return "PM1H_HISTORY_NP"; + case MID_PM24H_LAST_NP: + return "PM24H_LAST_NP"; + case MID_PM24H_CURRENT_NP: + return "PM24H_CURRENT_NP"; + case MID_PM24H_HISTORY_NP: + return "PM24H_HISTORY_NP"; + default: + } + return ""; +} + static void pmc_show(struct ptp_message *msg, FILE *fp) { struct alternate_time_offset_properties *atop; @@ -168,9 +195,11 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) struct unicast_master_entry *ume; struct subscribe_events_np *sen; struct port_properties_np *ppn; + struct pm_history_np *pm_history; struct port_hwclock_np *phn; struct timePropertiesDS *tp; struct management_tlv *mgt; + struct pm_conf_np *pm_conf; struct time_status_np *tsn; struct port_stats_np *pcp; struct tlv_extra *extra; @@ -451,10 +480,16 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) fprintf(fp, "SUBSCRIBE_EVENTS_NP " IFMT "duration %hu" IFMT "NOTIFY_PORT_STATE %s" - IFMT "NOTIFY_TIME_SYNC %s", + IFMT "NOTIFY_TIME_SYNC %s" + IFMT "NOTIFY_PM15M_UPDATE %s" + IFMT "NOTIFY_PM1H_UPDATE %s" + IFMT "NOTIFY_PM24H_UPDATE %s", sen->duration, event_bitmask_get(sen->bitmask, NOTIFY_PORT_STATE) ? "on" : "off", - event_bitmask_get(sen->bitmask, NOTIFY_TIME_SYNC) ? "on" : "off"); + event_bitmask_get(sen->bitmask, NOTIFY_TIME_SYNC) ? "on" : "off", + event_bitmask_get(sen->bitmask, NOTIFY_PM15M_UPDATE) ? "on" : "off", + event_bitmask_get(sen->bitmask, NOTIFY_PM1H_UPDATE) ? "on" : "off", + event_bitmask_get(sen->bitmask, NOTIFY_PM24H_UPDATE) ? "on" : "off"); break; case MID_SYNCHRONIZATION_UNCERTAIN_NP: mtd = (struct management_tlv_datum *) mgt->data; @@ -651,6 +686,90 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) fprintf(fp, "LOG_MIN_PDELAY_REQ_INTERVAL " IFMT "logMinPdelayReqInterval %hhd", mtd->val); break; + case MID_PM_CONF_NP: + pm_conf = (struct pm_conf_np *) mgt->data; + fprintf(fp, "PM_CONF_NP " + IFMT "15m_history %hhd" + IFMT "1h_history %hhd" + IFMT "24h_history %hhd", + pm_conf->pm15m_history, + pm_conf->pm1h_history, + pm_conf->pm24h_history); + break; + case MID_PM15M_CURRENT_NP: + case MID_PM15M_LAST_NP: + case MID_PM15M_HISTORY_NP: + case MID_PM1H_CURRENT_NP: + case MID_PM1H_LAST_NP: + case MID_PM1H_HISTORY_NP: + case MID_PM24H_CURRENT_NP: + case MID_PM24H_LAST_NP: + case MID_PM24H_HISTORY_NP: + pm_history = (struct pm_history_np *) mgt->data; + + fprintf(fp, "%s " + IFMT "portIdentity %s" + IFMT "length %hu", + pmc_show_pm_type(mgt->id), + pid2str(&pm_history->portIdentity), + pm_history->length); + + for (i = 0; i < pm_history->length; i++) { + fprintf(fp, + IFMT "Index %hu" + IFMT2 "Flags.Valid %d" + IFMT2 "Flags.Complete %d" + IFMT2 "Flags.Current %d" + IFMT2 "Start %" PRIu64 + IFMT2 "Stop %" PRIu64 + IFMT2 "rx_Sync %" PRIu64 + IFMT2 "rx_Delay_Req %" PRIu64 + IFMT2 "rx_Pdelay_Req %" PRIu64 + IFMT2 "rx_Pdelay_Resp %" PRIu64 + IFMT2 "rx_Follow_Up %" PRIu64 + IFMT2 "rx_Delay_Resp %" PRIu64 + IFMT2 "rx_Pdelay_Resp_Follow_Up %" PRIu64 + IFMT2 "rx_Announce %" PRIu64 + IFMT2 "rx_Signaling %" PRIu64 + IFMT2 "rx_Management %" PRIu64 + IFMT2 "tx_Sync %" PRIu64 + IFMT2 "tx_Delay_Req %" PRIu64 + IFMT2 "tx_Pdelay_Req %" PRIu64 + IFMT2 "tx_Pdelay_Resp %" PRIu64 + IFMT2 "tx_Follow_Up %" PRIu64 + IFMT2 "tx_Delay_Resp %" PRIu64 + IFMT2 "tx_Pdelay_Resp_Follow_Up %" PRIu64 + IFMT2 "tx_Announce %" PRIu64 + IFMT2 "tx_Signaling %" PRIu64 + IFMT2 "tx_Management %" PRIu64, + pm_history->records[i].index, + pm_history->records[i].flags & PM_RECORD_FLAGS_VALID ? 1 : 0, + pm_history->records[i].flags & PM_RECORD_FLAGS_COMPLETE ? 1 : 0, + pm_history->records[i].flags & PM_RECORD_FLAGS_CURRENT ? 1 : 0, + pm_history->records[i].start, + pm_history->records[i].end, + pm_history->records[i].stats.rxMsgType[SYNC], + pm_history->records[i].stats.rxMsgType[DELAY_REQ], + pm_history->records[i].stats.rxMsgType[PDELAY_REQ], + pm_history->records[i].stats.rxMsgType[PDELAY_RESP], + pm_history->records[i].stats.rxMsgType[FOLLOW_UP], + pm_history->records[i].stats.rxMsgType[DELAY_RESP], + pm_history->records[i].stats.rxMsgType[PDELAY_RESP_FOLLOW_UP], + pm_history->records[i].stats.rxMsgType[ANNOUNCE], + pm_history->records[i].stats.rxMsgType[SIGNALING], + pm_history->records[i].stats.rxMsgType[MANAGEMENT], + pm_history->records[i].stats.txMsgType[SYNC], + pm_history->records[i].stats.txMsgType[DELAY_REQ], + pm_history->records[i].stats.txMsgType[PDELAY_REQ], + pm_history->records[i].stats.txMsgType[PDELAY_RESP], + pm_history->records[i].stats.txMsgType[FOLLOW_UP], + pm_history->records[i].stats.txMsgType[DELAY_RESP], + pm_history->records[i].stats.txMsgType[PDELAY_RESP_FOLLOW_UP], + pm_history->records[i].stats.txMsgType[ANNOUNCE], + pm_history->records[i].stats.txMsgType[SIGNALING], + pm_history->records[i].stats.txMsgType[MANAGEMENT]); + } + break; } out: fprintf(fp, "\n"); diff --git a/pmc_common.c b/pmc_common.c index 9e251c4..c02a4d7 100644 --- a/pmc_common.c +++ b/pmc_common.c @@ -156,6 +156,16 @@ struct management_id idtab[] = { { "UNICAST_MASTER_TABLE_NP", MID_UNICAST_MASTER_TABLE_NP, do_get_action }, { "PORT_HWCLOCK_NP", MID_PORT_HWCLOCK_NP, do_get_action }, { "POWER_PROFILE_SETTINGS_NP", MID_POWER_PROFILE_SETTINGS_NP, do_set_action }, + { "PM_CONF_NP", MID_PM_CONF_NP, do_set_action }, + { "PM15M_LAST_NP", MID_PM15M_LAST_NP, do_get_action }, + { "PM15M_CURRENT_NP", MID_PM15M_CURRENT_NP, do_get_action }, + { "PM15M_HISTORY_NP", MID_PM15M_HISTORY_NP, do_get_action }, + { "PM1H_LAST_NP", MID_PM1H_LAST_NP, do_get_action }, + { "PM1H_CURRENT_NP", MID_PM1H_CURRENT_NP, do_get_action }, + { "PM1H_HISTORY_NP", MID_PM1H_HISTORY_NP, do_get_action }, + { "PM24H_LAST_NP", MID_PM24H_LAST_NP, do_get_action }, + { "PM24H_CURRENT_NP", MID_PM24H_CURRENT_NP, do_get_action }, + { "PM24H_HISTORY_NP", MID_PM24H_HISTORY_NP, do_get_action }, }; static void do_get_action(struct pmc *pmc, int action, int index, char *str) @@ -175,9 +185,13 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) struct grandmaster_settings_np gsn; struct management_tlv_datum mtd; struct subscribe_events_np sen; + struct pm_conf_np pm_conf; struct port_ds_np pnp; char onoff_port_state[4] = "off"; char onoff_time_status[4] = "off"; + char onoff_pm15m_update[4] = "off"; + char onoff_pm1h_update[4] = "off"; + char onoff_pm24h_update[4] = "off"; char display_name[11] = {0}; uint64_t jump; uint8_t key; @@ -303,12 +317,18 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) cnt = sscanf(str, " %*s %*s " "duration %hu " "NOTIFY_PORT_STATE %3s " - "NOTIFY_TIME_SYNC %3s ", + "NOTIFY_TIME_SYNC %3s " + "NOTIFY_PM15M_UPDATE %3s " + "NOTIFY_PM1H_UPDATE %3s " + "NOTIFY_PM24H_UPDATE %3s ", &sen.duration, onoff_port_state, - onoff_time_status); - if (cnt != 3) { - fprintf(stderr, "%s SET needs 3 values\n", + onoff_time_status, + onoff_pm15m_update, + onoff_pm1h_update, + onoff_pm24h_update); + if (cnt < 3) { + fprintf(stderr, "%s SET needs 3 values at least\n", idtab[index].name); break; } @@ -318,6 +338,15 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) if (!strcasecmp(onoff_time_status, "on")) { event_bitmask_set(sen.bitmask, NOTIFY_TIME_SYNC, TRUE); } + if (!strcasecmp(onoff_pm15m_update, "on")) { + event_bitmask_set(sen.bitmask, NOTIFY_PM15M_UPDATE, TRUE); + } + if (!strcasecmp(onoff_pm1h_update, "on")) { + event_bitmask_set(sen.bitmask, NOTIFY_PM1H_UPDATE, TRUE); + } + if (!strcasecmp(onoff_pm24h_update, "on")) { + event_bitmask_set(sen.bitmask, NOTIFY_PM24H_UPDATE, TRUE); + } pmc_send_set_action(pmc, code, &sen, sizeof(sen)); break; case MID_SYNCHRONIZATION_UNCERTAIN_NP: @@ -385,6 +414,21 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) IEEE_C37_238_VERSION_2017); } break; + case MID_PM_CONF_NP: + cnt = sscanf(str, " %*s %*s " + "15m_history %hu " + "1h_history %hu " + "24h_history %hu ", + &pm_conf.pm15m_history, + &pm_conf.pm1h_history, + &pm_conf.pm24h_history); + if (cnt != 3) { + fprintf(stderr, "%s SET needs 3 values\n", + idtab[index].name); + break; + } + pmc_send_set_action(pmc, code, &pm_conf, sizeof(pm_conf)); + break; } } @@ -675,6 +719,20 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id) case MID_LOG_MIN_PDELAY_REQ_INTERVAL: len += sizeof(struct management_tlv_datum); break; + case MID_PM_CONF_NP: + len += sizeof(struct pm_conf_np); + break; + case MID_PM15M_LAST_NP: + case MID_PM15M_CURRENT_NP: + case MID_PM15M_HISTORY_NP: + case MID_PM1H_LAST_NP: + case MID_PM1H_CURRENT_NP: + case MID_PM1H_HISTORY_NP: + case MID_PM24H_LAST_NP: + case MID_PM24H_CURRENT_NP: + case MID_PM24H_HISTORY_NP: + len += sizeof(struct pm_history_np); + break; } return len + len % 2; } diff --git a/port.c b/port.c index 5803cd3..86b7e98 100644 --- a/port.c +++ b/port.c @@ -21,8 +21,10 @@ #include <malloc.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include <unistd.h> #include <sys/queue.h> +#include <sys/timerfd.h> #include <net/if.h> #include "bmc.h" @@ -48,6 +50,10 @@ #define ALLOWED_LOST_RESPONSES 3 #define ANNOUNCE_SPAN 1 +#define PM_15M 15 +#define PM_1H 60 +#define PM_24H 1440 + enum syfu_event { SYNC_MISMATCH, SYNC_MATCH, @@ -258,6 +264,18 @@ int set_tmo_log(int fd, unsigned int scale, int log_seconds) return timerfd_settime(fd, 0, &tmo, NULL); } +int set_tmo_peridic(int fd, int seconds) +{ + struct itimerspec tmo = { + {0, 0}, {0, 0} + }; + + tmo.it_interval.tv_sec = seconds; + tmo.it_value.tv_sec = seconds; + + return timerfd_settime(fd, 0, &tmo, NULL); +} + int set_tmo_lin(int fd, int seconds) { struct itimerspec tmo = { @@ -291,6 +309,33 @@ int set_tmo_random(int fd, int min, int span, int log_seconds) return timerfd_settime(fd, 0, &tmo, NULL); } +int port_tmo_pm(struct port *p, unsigned int period_min) +{ + struct itimerspec tmo = { + {0, 0}, {0, 0} + }; + + struct timespec now; + struct tm tm; + time_t time; + + if (period_min) { + clock_gettime(CLOCK_REALTIME, &now); + time = now.tv_sec; + localtime_r(&time, &tm); + + tm.tm_sec = 0; + tm.tm_min = (tm.tm_min / period_min) * period_min + period_min; + + time = mktime(&tm); + tmo.it_value.tv_sec = time; + + return timerfd_settime(p->fda.fd[FD_PM_TIMER], TFD_TIMER_ABSTIME, &tmo, NULL); + } else { + return port_clr_tmo(p->fda.fd[FD_PM_TIMER]); + } +} + int port_set_fault_timer_log(struct port *port, unsigned int scale, int log_seconds) { @@ -338,6 +383,73 @@ static void fc_prune(struct foreign_clock *fc) } } + +static void port_init_pm(struct port *p) +{ + p->pm15min = NULL; + p->pm1h = NULL; + p->pm24h = NULL; + + p->fda.fd[FD_PM_TIMER] = timerfd_create(CLOCK_REALTIME, 0); +} + +unsigned int port_pm_period(struct port *p) +{ + if (port_pm_size(p->pm15min) > 0) { + return PM_15M; + } else if (port_pm_size(p->pm1h) > 0) { + return PM_1H; + } else if (port_pm_size(p->pm24h) > 0) { + return PM_24H; + } + return 0; +} + +static void port_enable_pm(struct port *p, unsigned int pm15min_hist, unsigned int pm1h_hist, unsigned int pm24h_hist) +{ + struct timespec now; + unsigned int size; + + clock_gettime(CLOCK_REALTIME, &now); + + size = port_pm_size(p->pm15min); + p->pm15min = port_pm_resize(p->pm15min, pm15min_hist, PM_15M); + if (!size && pm15min_hist) { + port_pm_prepare_sample(p->pm15min, now.tv_sec); + port_pm_checkpoint(p->pm15min, &p->stats); + } + + size = port_pm_size(p->pm1h); + p->pm1h = port_pm_resize(p->pm1h, pm1h_hist, PM_1H); + if (!size && pm1h_hist) { + port_pm_prepare_sample(p->pm1h, now.tv_sec); + port_pm_checkpoint(p->pm1h, &p->stats); + } + + size = port_pm_size(p->pm24h); + p->pm24h = port_pm_resize(p->pm24h, pm24h_hist, PM_24H); + if (!size && pm24h_hist) { + port_pm_prepare_sample(p->pm24h, now.tv_sec); + port_pm_checkpoint(p->pm24h, &p->stats); + } + + port_tmo_pm(p, port_pm_period(p)); +} + +static void port_destroy_pm(struct port *p) +{ + if (p->fda.fd[FD_PM_TIMER] >= 0) { + close(p->fda.fd[FD_PM_TIMER]); + p->fda.fd[FD_PM_TIMER] = -1; + } + port_pm_destroy(p->pm15min); + port_pm_destroy(p->pm1h); + port_pm_destroy(p->pm24h); + p->pm15min = NULL; + p->pm1h = NULL; + p->pm24h = NULL; +} + static int delay_req_current(struct ptp_message *m, struct timespec now) { int64_t t1, t2, tmo = 5 * NSEC2SEC; @@ -874,6 +986,9 @@ static const Octet profile_id_p2p[] = {0x00, 0x1B, 0x19, 0x00, 0x02, 0x00}; static const Octet profile_id_8275_1[] = {0x00, 0x19, 0xA7, 0x01, 0x02, 0x03}; static const Octet profile_id_8275_2[] = {0x00, 0x19, 0xA7, 0x02, 0x01, 0x02}; +#define __min(a, b) ((a) < (b) ? (a) : (b)) + + static int port_management_fill_response(struct port *target, struct ptp_message *rsp, int id) { @@ -884,17 +999,22 @@ static int port_management_fill_response(struct port *target, struct mgmt_clock_description *cd; struct management_tlv_datum *mtd; struct unicast_master_entry *ume; + struct pm_history_np *pm_history; struct clock_description *desc; struct port_properties_np *ppn; struct port_hwclock_np *phn; + struct pm_conf_np *pm_conf; struct management_tlv *tlv; struct port_stats_np *psn; struct foreign_clock *fc; struct port_ds_np *pdsnp; struct tlv_extra *extra; struct PortIdentity pid; + struct port_pm *port_pm; const char *ts_label; + struct timespec now; struct portDS *pds; + unsigned int i; uint16_t u16; uint8_t *buf; int datalen; @@ -1129,6 +1249,82 @@ static int port_management_fill_response(struct port *target, memcpy(pwr, &target->pwr, sizeof(*pwr)); datalen = sizeof(*pwr); break; + case MID_PM_CONF_NP: + pm_conf = (struct pm_conf_np *)tlv->data; + pm_conf->pm15m_history = port_pm_size(target->pm15min); + pm_conf->pm1h_history = port_pm_size(target->pm1h); + pm_conf->pm24h_history = port_pm_size(target->pm24h); + pm_conf->dummy = 0; + datalen = sizeof(*pm_conf); + break; + case MID_PM15M_LAST_NP: + case MID_PM15M_CURRENT_NP: + case MID_PM15M_HISTORY_NP: + case MID_PM1H_LAST_NP: + case MID_PM1H_CURRENT_NP: + case MID_PM1H_HISTORY_NP: + case MID_PM24H_LAST_NP: + case MID_PM24H_CURRENT_NP: + case MID_PM24H_HISTORY_NP: + clock_gettime(CLOCK_REALTIME, &now); + pm_history = (struct pm_history_np *)tlv->data; + switch (id) { + case MID_PM15M_LAST_NP: + port_pm = target->pm15min; + u16 = port_pm_last(port_pm); + pm_history->length = __min(1, port_pm_fill_level(port_pm)); + break; + case MID_PM15M_CURRENT_NP: + port_pm = target->pm15min; + u16 = port_pm_current(port_pm); + pm_history->length = 1; + break; + case MID_PM15M_HISTORY_NP: + port_pm = target->pm15min; + u16 = port_pm_current(port_pm); + pm_history->length = port_pm_fill_level(port_pm) + 1; + break; + case MID_PM1H_LAST_NP: + port_pm = target->pm1h; + u16 = port_pm_last(port_pm); + pm_history->length = __min(1, port_pm_fill_level(port_pm)); + break; + case MID_PM1H_CURRENT_NP: + port_pm = target->pm1h; + u16 = port_pm_current(port_pm); + pm_history->length = 1; + break; + case MID_PM1H_HISTORY_NP: + port_pm = target->pm1h; + u16 = port_pm_current(port_pm); + pm_history->length = port_pm_fill_level(port_pm) + 1; + break; + case MID_PM24H_LAST_NP: + port_pm = target->pm24h; + u16 = port_pm_last(port_pm); + pm_history->length = __min(1, port_pm_fill_level(port_pm)); + break; + case MID_PM24H_CURRENT_NP: + port_pm = target->pm24h; + u16 = port_pm_current(port_pm); + pm_history->length = 1; + break; + case MID_PM24H_HISTORY_NP: + port_pm = target->pm24h; + u16 = port_pm_current(port_pm); + pm_history->length = port_pm_fill_level(port_pm) + 1; + break; + } + for (i = u16 - pm_history->length + 1; i <= u16; i++) { + if (i == port_pm_current(port_pm)) { + port_pm_get_current_sample(port_pm, now.tv_sec, &target->stats, &pm_history->records[u16 - i]); + } else { + port_pm_get_sample(port_pm, i, &pm_history->records[u16 - i]); + } + } + pm_history->portIdentity = target->portIdentity; + datalen = sizeof(struct pm_history_np) + (sizeof(struct pm_record_np) * pm_history->length); + break; default: /* The caller should *not* respond to this message. */ tlv_extra_recycle(extra); @@ -1172,6 +1368,7 @@ static int port_management_set(struct port *target, { struct ieee_c37_238_settings_np *pwr; struct management_tlv *tlv; + struct pm_conf_np *pm_conf; struct port_ds_np *pdsnp; int respond = 0; @@ -1194,6 +1391,15 @@ static int port_management_set(struct port *target, break; } break; + case MID_PM_CONF_NP: + pm_conf = (struct pm_conf_np *) tlv->data; + + config_set_section_int(clock_config(target->clock), target->name, "performance_monitor.15m_history", pm_conf->pm15m_history); + config_set_section_int(clock_config(target->clock), target->name, "performance_monitor.1h_history", pm_conf->pm1h_history); + config_set_section_int(clock_config(target->clock), target->name, "performance_monitor.24h_history", pm_conf->pm24h_history); + port_enable_pm(target, pm_conf->pm15m_history, pm_conf->pm1h_history, pm_conf->pm24h_history); + respond = 1; + break; } if (respond && !port_management_get_response(target, ingress, id, req)) pr_err("%s: failed to send management set response", target->log_name); @@ -2593,6 +2799,7 @@ void port_close(struct port *p) unicast_service_cleanup(p); transport_destroy(p->trp); tsproc_destroy(p->tsproc); + port_destroy_pm(p); if (p->fault_fd >= 0) { close(p->fault_fd); } @@ -2841,6 +3048,22 @@ enum fsm_event port_event(struct port *p, int fd_index) return p->event(p, fd_index); } +void pm_type(struct timespec *now, bool *is_15m, bool *is_1h, bool *is_24h) +{ + struct tm tm; + time_t time; + int tm_min; + + time = now->tv_sec; + localtime_r(&time, &tm); + + tm_min = tm.tm_hour * 60 + tm.tm_min; + + *is_15m = (tm_min % PM_15M) <= 1 || (tm_min % PM_15M) >= (PM_15M - 1); + *is_1h = (tm_min % PM_1H) <= 1 || (tm_min % PM_1H) >= (PM_1H - 1); + *is_24h = (tm_min % PM_24H) <= 1 || (tm_min % PM_24H) >= (PM_24H - 1); +} + static enum fsm_event bc_event(struct port *p, int fd_index) { enum fsm_event event = EV_NONE; @@ -2937,6 +3160,41 @@ static enum fsm_event bc_event(struct port *p, int fd_index) p->service_stats.unicast_request_timeout++; return unicast_client_timer(p) ? EV_FAULT_DETECTED : EV_NONE; + case FD_PM_TIMER: + pr_debug("%s: performance monitor timeout", p->log_name); + { + struct timespec now; + bool is_pm15m, is_pm1h, is_pm24h; + + clock_gettime(CLOCK_REALTIME, &now); + + pm_type(&now, &is_pm15m, &is_pm1h, &is_pm24h); + + if (is_pm15m) { + // 15min pm expired + port_pm_store_sample(p->pm15min, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm15min, now.tv_sec); + port_notify_event(p, NOTIFY_PM15M_UPDATE); + } + + if (is_pm1h) { + // 15min pm expired + port_pm_store_sample(p->pm1h, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm1h, now.tv_sec); + port_notify_event(p, NOTIFY_PM1H_UPDATE); + } + + if (is_pm24h) { + // 15min pm expired + port_pm_store_sample(p->pm24h, now.tv_sec, &p->stats); + port_pm_prepare_sample(p->pm24h, now.tv_sec); + port_notify_event(p, NOTIFY_PM24H_UPDATE); + } + + port_tmo_pm(p, port_pm_period(p)); + } + return EV_NONE; + case FD_RTNL: pr_debug("%s: received link status notification", p->log_name); rtnl_link_status(fd, p->name, port_link_status, p); @@ -3258,6 +3516,15 @@ void port_notify_event(struct port *p, enum notification event) case NOTIFY_PORT_STATE: id = MID_PORT_DATA_SET; break; + case NOTIFY_PM15M_UPDATE: + id = MID_PM15M_LAST_NP; + break; + case NOTIFY_PM1H_UPDATE: + id = MID_PM1H_LAST_NP; + break; + case NOTIFY_PM24H_UPDATE: + id = MID_PM24H_LAST_NP; + break; default: return; } @@ -3285,6 +3552,7 @@ struct port *port_open(const char *phc_device, enum clock_type type = clock_type(clock); struct config *cfg = clock_config(clock); struct port *p = malloc(sizeof(*p)); + int pm15m_history, pm1h_history, pm24h_history; int i; if (!p) { @@ -3456,7 +3724,7 @@ struct port *port_open(const char *phc_device, } p->nrate.ratio = 1.0; - port_clear_fda(p, N_POLLFD); + port_clear_fda(p, FD_PM_TIMER); p->fault_fd = -1; if (!port_is_uds(p)) { p->fault_fd = timerfd_create(CLOCK_MONOTONIC, 0); @@ -3465,6 +3733,13 @@ struct port *port_open(const char *phc_device, goto err_tsproc; } } + + port_init_pm(p); + pm15m_history = config_get_int(cfg, p->name, "performance_monitor.15m_history"); + pm1h_history = config_get_int(cfg, p->name, "performance_monitor.1h_history"); + pm24h_history = config_get_int(cfg, p->name, "performance_monitor.24h_history"); + port_enable_pm(p, pm15m_history, pm1h_history, pm24h_history); + return p; err_tsproc: diff --git a/port_pm.c b/port_pm.c new file mode 100644 index 0000000..b6b007b --- /dev/null +++ b/port_pm.c @@ -0,0 +1,212 @@ +/** + * @file port_pm.c + * @brief Port Level Performance Monitor + * @note Copyright (C) 2023 Luigi Mantellini <luigi.mantell...@sm-optics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "port_pm.h" +#include "ddt.h" +#include "pdt.h" + +struct port_pm { + struct PortStats stats; + unsigned int period_min; + UInteger16 history_size; + UInteger16 fill_level; + UInteger16 index; + PMTimestamp start; + struct pm_record_np *entries; +}; + +struct port_pm* port_pm_init(unsigned int history_size, unsigned int period_min) +{ + struct port_pm *port_pm; + + port_pm = malloc(sizeof(struct port_pm)); + if (!port_pm) { + goto fail_port_pm; + } + + port_pm->entries = calloc(history_size, sizeof(struct pm_record_np)); + if (!port_pm->entries) { + goto fail_entries; + } + + port_pm->period_min = period_min; + port_pm->history_size = history_size; + port_pm->fill_level = 0; + port_pm->index = 0; + port_pm->start = 0; + + return port_pm; + +fail_entries: + free(port_pm); + port_pm = NULL; +fail_port_pm: + return port_pm; +} + +void port_pm_destroy(struct port_pm *port_pm) +{ + if (port_pm) { + if (port_pm->entries) { + free(port_pm->entries); + } + free(port_pm); + port_pm = NULL; + } +} + +#define __min(a, b) (a < b ? a : b) + +struct port_pm* port_pm_resize(struct port_pm *port_pm, unsigned int history_size, unsigned int period_min) +{ + if (!history_size) { + port_pm_destroy(port_pm); + return port_pm; + } + + if (!port_pm) { + return port_pm_init(history_size, period_min); + } + + if (history_size != port_pm->history_size) { + struct pm_record_np *entries = calloc(history_size, sizeof(struct pm_record_np)); + unsigned int n = __min(port_pm->history_size, history_size); + unsigned int first = (port_pm->index > n) ? (port_pm->index - n) : 0; + int i; + + for (i = first; i < first + n; i++) { + entries[i % history_size] = port_pm->entries[i % port_pm->history_size]; + } + free(port_pm->entries); + port_pm->entries = entries; + port_pm->history_size = history_size; + port_pm->fill_level = __min(port_pm->fill_level, history_size); + } + + return port_pm; +} + +UInteger16 port_pm_size(const struct port_pm *port_pm) +{ + if (port_pm) { + return port_pm->history_size; + } + return 0; +} + +UInteger16 port_pm_fill_level(const struct port_pm *port_pm) +{ + if (port_pm) { + return port_pm->fill_level; + } + return 0; +} + +int port_pm_has_sample(const struct port_pm *port_pm) +{ + if (port_pm) { + return port_pm->fill_level > 0; + } + return 0; +} + +UInteger16 port_pm_last(const struct port_pm *port_pm) +{ + if (port_pm) { + return port_pm->index - 1; + } + return 0; +} + +UInteger16 port_pm_current(const struct port_pm *port_pm) +{ + if (port_pm) { + return port_pm->index; + } + return 0; +} + +void port_pm_prepare_sample(struct port_pm *port_pm, PMTimestamp timestamp) +{ + if (port_pm) { + port_pm->start = timestamp; + } +} + +void port_pm_checkpoint(struct port_pm *port_pm, const struct PortStats *stats) +{ + if (port_pm && stats) { + port_pm->stats = *stats; + } +} + +void port_pm_get_current_sample(struct port_pm *port_pm, PMTimestamp timestamp, const struct PortStats *stats, struct pm_record_np *sample) +{ + if (port_pm && sample) { + if (port_pm->history_size) { + PMTimestamp delta; + + for (unsigned int i = 0; i < MAX_MESSAGE_TYPES; i++) { + sample->stats.rxMsgType[i] = stats->rxMsgType[i] - port_pm->stats.rxMsgType[i]; + sample->stats.txMsgType[i] = stats->txMsgType[i] - port_pm->stats.txMsgType[i]; + } + + sample->start = port_pm->start; + sample->end = timestamp; + delta = sample->end - sample->start; + sample->flags = PM_RECORD_FLAGS_VALID | PM_RECORD_FLAGS_CURRENT | (delta >= (port_pm->period_min * 60 * 2 / 3) ? PM_RECORD_FLAGS_COMPLETE : 0); + sample->index = port_pm->index; + } else { + memset(sample, 0, sizeof(struct pm_record_np)); + } + } +} + +int port_pm_get_sample(struct port_pm *port_pm, UInteger16 index, struct pm_record_np *sample) +{ + if (port_pm && sample) { + if ((index < port_pm->index) && (index >= (port_pm->index - port_pm->history_size))) { + memcpy(sample, &port_pm->entries[index % port_pm->history_size], sizeof(struct pm_record_np)); + return 1; + } else { + memset(sample, 0, sizeof(struct pm_record_np)); + } + } + return 0; +} + +void port_pm_store_sample(struct port_pm *port_pm, PMTimestamp timestamp, const struct PortStats *stats) +{ + struct pm_record_np *entry; + if (port_pm && stats) { + entry = &port_pm->entries[port_pm->index % port_pm->history_size]; + port_pm_get_current_sample(port_pm, timestamp, stats, entry); + entry->flags &= ~PM_RECORD_FLAGS_CURRENT; + port_pm_checkpoint(port_pm, stats); + + port_pm->index++; + if (port_pm->fill_level < port_pm->history_size) { + port_pm->fill_level++; + } + } +} diff --git a/port_pm.h b/port_pm.h new file mode 100644 index 0000000..01be6e6 --- /dev/null +++ b/port_pm.h @@ -0,0 +1,141 @@ +/** + * @file port_pm.h + * @brief Port Level Performance Monitoring Circular Buffer + * @note Copyright (C) 2023 Luigi Mantellini <luigi.mantell...@sm-optics.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef HAVE_PORT_PM_H +#define HAVE_PORT_PM_H + +#include "ddt.h" +#include "tlv.h" + +/** Opaque type. */ +struct port_pm; + +/** + * Create a new Port Performance Monitoring circular buffer. + * + * @param history_size Performance Monitoring circular buffer size. + * @param period_min Sampling period in minutes used to handle COMPLETE flag (See 1588-2019 Annex J) + * @return The created object + */ +struct port_pm* port_pm_init(unsigned int history_size, unsigned int period_min); + +/** + * Destroy a Port Performance Montitoring buffer and free its associated resources. + * After this call returns, @a port_pm is no longer a valid pointer. + * + * @param port_pm A pointer to an already allocated Port Performance Monitoring circular buffer. + */ +void port_pm_destroy(struct port_pm *port_pm); + +/** + * Resize a Port Performance Monitoring circular buffer. If the passed buffer is not created yet it will + * created calling port_pm_init(). Instead a zero history_size call will destroy the buffer calling the + * port_pm_destroy(). + * + * @param port_pm The Port Performance Monitoring circular buffer. + * @param history_size Performance Monitoring circular buffer size. + * @param period_min Sampling period in minutes used to handle COMPLETE flag (See 1588-2019 Annex J) + * @return The created object or NULL if destroyed. + */ +struct port_pm* port_pm_resize(struct port_pm *port_pm, unsigned int history_size, unsigned int period_min); + +/** + * Returns the circular buffer size. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + */ +UInteger16 port_pm_size(const struct port_pm *port_pm); + +/** + * Returns the filling level of a circular buffer. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + * @return The filling level of the circular buffer. + */ +UInteger16 port_pm_fill_level(const struct port_pm *port_pm); + +/** + * Returns true when the buffer has at least a sample. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + * @return True when the buffer has at least a sample, false otherwise. + */ +int port_pm_has_sample(const struct port_pm *port_pm); + +/** + * Return the index of the last sample. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + * @return The index of the last stored sample. + */ +UInteger16 port_pm_last(const struct port_pm *port_pm); + +/** + * Return the index of the current (and not finished) sample. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + * @param The index of the current sample. + */ +UInteger16 port_pm_current(const struct port_pm *port_pm); + +/** + * Store information required for the next sample. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + */ +void port_pm_prepare_sample(struct port_pm *port_pm, PMTimestamp timestamp); + +/** + * Store the current port counters (Stats) used for delta computaion. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + */ +void port_pm_checkpoint(struct port_pm *port_pm, const struct PortStats *stats); + +/** + * Get the current (not stored) sample. The caller must provide the timestamp (in seconds) and + * the current Port Counters. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + * @param timestamp The current timestamp in seconds. + * @param stats The current Port Counters. + * @param sample The pointer to sample to fill + */ +void port_pm_get_current_sample(struct port_pm *port_pm, PMTimestamp timestamp, const struct PortStats *stats, struct pm_record_np *sample); + +/** + * Get the stored sample at provied index + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + * @param index Index of the sample into circular buffer. + * @param sample The pointer to sample to fill + * @return Return 1 on succes, 0 otherwise. + */ +int port_pm_get_sample(struct port_pm *port_pm, UInteger16 index, struct pm_record_np *sample); + +/** + * Store the sample updating the timestamp and the actual stored Port couters. + * + * @param port_pm The pointer to a Port Performance Monitoring circular buffer. + * @param timestamp The current timestamp in seconds. + * @param stats The current Port Counters. + */ +void port_pm_store_sample(struct port_pm *port_pm, PMTimestamp timestamp, const struct PortStats *stats); + +#endif diff --git a/port_private.h b/port_private.h index 3b02d2f..9352bd6 100644 --- a/port_private.h +++ b/port_private.h @@ -27,6 +27,7 @@ #include "monitor.h" #include "msg.h" #include "power_profile.h" +#include "port_pm.h" #include "tmv.h" #define NSEC2SEC 1000000000LL @@ -164,6 +165,9 @@ struct port { /* slave event monitoring */ struct monitor *slave_event_monitor; bool unicast_state_dirty; + struct port_pm *pm15min; + struct port_pm *pm1h; + struct port_pm *pm24h; }; #define portnum(p) (p->portIdentity.portNumber) @@ -211,5 +215,8 @@ int process_signaling(struct port *p, struct ptp_message *m); void process_sync(struct port *p, struct ptp_message *m); int source_pid_eq(struct ptp_message *m1, struct ptp_message *m2); void ts_add(tmv_t *ts, Integer64 correction); +void pm_type(struct timespec *now, bool *is_15m, bool *is_1h, bool *is_24h); +int port_tmo_pm(struct port *p, unsigned int period_min); +unsigned int port_pm_period(struct port *p); #endif diff --git a/tlv.c b/tlv.c index 9b82bd9..af9d3d3 100644 --- a/tlv.c +++ b/tlv.c @@ -112,6 +112,32 @@ static int64_t net2host64_unaligned(void *p) return v; } +static void host2net_pm_entry(struct pm_record_np *entry) +{ + int i; + for (i = 0 ; i < MAX_MESSAGE_TYPES; i++) { + entry->stats.rxMsgType[i] = __cpu_to_le64(entry->stats.rxMsgType[i]); + entry->stats.txMsgType[i] = __cpu_to_le64(entry->stats.txMsgType[i]); + } + HTONS(entry->index); + HTONS(entry->flags); + entry->start = host2net64(entry->start); + entry->end = host2net64(entry->end); +} + +static void net2host_pm_entry(struct pm_record_np *entry) +{ + int i; + for (i = 0 ; i < MAX_MESSAGE_TYPES; i++) { + entry->stats.rxMsgType[i] = __le64_to_cpu(entry->stats.rxMsgType[i]); + entry->stats.txMsgType[i] = __le64_to_cpu(entry->stats.txMsgType[i]); + } + NTOHS(entry->index); + NTOHS(entry->flags); + entry->start =net2host64(entry->start); + entry->end = net2host64(entry->end); +} + static size_t tlv_array_count(struct TLV *tlv, size_t base_size, size_t item_size) { return (tlv->length - base_size) / item_size; @@ -173,9 +199,11 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, struct mgmt_clock_description *cd; struct unicast_master_entry *ume; struct subscribe_events_np *sen; + struct pm_history_np *pm_history; struct port_properties_np *ppn; struct port_hwclock_np *phn; struct timePropertiesDS *tp; + struct pm_conf_np *pm_conf; struct time_status_np *tsn; struct port_stats_np *psn; int extra_len = 0, i, len; @@ -490,6 +518,33 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, if (data_len != 0) goto bad_length; break; + case MID_PM_CONF_NP: + if (data_len < sizeof(struct pm_conf_np)) + goto bad_length; + pm_conf = (struct pm_conf_np *)m->data; + NTOHS(pm_conf->pm15m_history); + NTOHS(pm_conf->pm1h_history); + NTOHS(pm_conf->pm24h_history); + break; + case MID_PM15M_LAST_NP: + case MID_PM15M_CURRENT_NP: + case MID_PM15M_HISTORY_NP: + case MID_PM1H_LAST_NP: + case MID_PM1H_CURRENT_NP: + case MID_PM1H_HISTORY_NP: + case MID_PM24H_LAST_NP: + case MID_PM24H_CURRENT_NP: + case MID_PM24H_HISTORY_NP: + if (data_len < sizeof(struct pm_history_np)) + goto bad_length; + pm_history = (struct pm_history_np *)m->data; + pm_history->portIdentity.portNumber = + ntohs(pm_history->portIdentity.portNumber); + NTOHS(pm_history->length); + for (i = 0; i < pm_history->length; i++) { + net2host_pm_entry(&pm_history->records[i]); + } + extra_len = sizeof(struct pm_history_np) + (sizeof(struct pm_record_np) * pm_history->length); } if (extra_len) { if (extra_len % 2) @@ -510,11 +565,13 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) struct grandmaster_settings_np *gsn; struct port_service_stats_np *pssn; struct mgmt_clock_description *cd; + struct pm_history_np *pm_history; struct unicast_master_entry *ume; struct subscribe_events_np *sen; struct port_properties_np *ppn; struct port_hwclock_np *phn; struct timePropertiesDS *tp; + struct pm_conf_np *pm_conf; struct time_status_np *tsn; struct port_stats_np *psn; struct port_ds_np *pdsnp; @@ -672,6 +729,29 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) HTONL(pwr->networkTimeInaccuracy); HTONL(pwr->totalTimeInaccuracy); break; + case MID_PM_CONF_NP: + pm_conf = (struct pm_conf_np *)m->data; + HTONS(pm_conf->pm15m_history); + HTONS(pm_conf->pm1h_history); + HTONS(pm_conf->pm24h_history); + break; + case MID_PM15M_LAST_NP: + case MID_PM15M_CURRENT_NP: + case MID_PM15M_HISTORY_NP: + case MID_PM1H_LAST_NP: + case MID_PM1H_CURRENT_NP: + case MID_PM1H_HISTORY_NP: + case MID_PM24H_LAST_NP: + case MID_PM24H_CURRENT_NP: + case MID_PM24H_HISTORY_NP: + pm_history = (struct pm_history_np *)m->data; + pm_history->portIdentity.portNumber = + htons(pm_history->portIdentity.portNumber); + for (i = 0; i < pm_history->length; i++) { + host2net_pm_entry(&pm_history->records[i]); + } + HTONS(pm_history->length); + break; } } diff --git a/tlv.h b/tlv.h index 8b51ffd..59e59c1 100644 --- a/tlv.h +++ b/tlv.h @@ -129,6 +129,16 @@ enum management_action { #define MID_UNICAST_MASTER_TABLE_NP 0xC008 #define MID_PORT_HWCLOCK_NP 0xC009 #define MID_POWER_PROFILE_SETTINGS_NP 0xC00A +#define MID_PM_CONF_NP 0xC00B +#define MID_PM15M_LAST_NP 0xC00C +#define MID_PM15M_CURRENT_NP 0xC00D +#define MID_PM15M_HISTORY_NP 0xC00E +#define MID_PM1H_LAST_NP 0xC00F +#define MID_PM1H_CURRENT_NP 0xC010 +#define MID_PM1H_HISTORY_NP 0xC011 +#define MID_PM24H_LAST_NP 0xC012 +#define MID_PM24H_CURRENT_NP 0xC013 +#define MID_PM24H_HISTORY_NP 0xC014 /* Management error ID values */ #define MID_RESPONSE_TOO_BIG 0x0001 @@ -364,6 +374,31 @@ struct ieee_c37_238_settings_np { UInteger32 totalTimeInaccuracy; } PACKED; +struct pm_conf_np { + UInteger16 pm15m_history; + UInteger16 pm1h_history; + UInteger16 pm24h_history; + UInteger16 dummy; +} PACKED; + +struct pm_record_np { + UInteger16 index; + UInteger16 flags; + PMTimestamp start; + PMTimestamp end; + struct PortStats stats; +} PACKED; + +struct pm_history_np { + struct PortIdentity portIdentity; + UInteger16 length; + struct pm_record_np records[]; +} PACKED; + +#define PM_RECORD_FLAGS_VALID 0x0001 +#define PM_RECORD_FLAGS_COMPLETE 0x0002 +#define PM_RECORD_FLAGS_CURRENT 0x0004 + struct msg_interval_req_tlv { Enumeration16 type; UInteger16 length; -- 2.41.0 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel