---
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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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
[email protected]
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel