From 40e7cbcbe5cc3535975b87637429d5ded0654a19 Mon Sep 17 00:00:00 2001 From: Harshit Gupta Date: Thu, 23 Nov 2023 11:59:17 -0500 Subject: [PATCH] Add linuxptp v3.1.1 with High-Availability patches (#6404) Co-authored-by: Harshit Gupta --- .../linuxptp/linuxptp.signatures.json | 11 - SPECS/linuxptp/clknetsim-phc2sys.patch | 22 + SPECS/linuxptp/enable-ha.patch | 11615 ++++++++++++++++ SPECS/linuxptp/linuxptp-zerolength.patch | 37 + SPECS/linuxptp/linuxptp.signatures.json | 12 + .../linuxptp/linuxptp.spec | 92 +- .../linuxptp/phc2sys.service | 0 SPECS/linuxptp/ptp4l.conf | 14 + .../linuxptp/ptp4l.service | 0 .../linuxptp/timemaster.conf | 7 - .../linuxptp/timemaster.service | 0 cgmanifest.json | 4 +- 12 files changed, 11773 insertions(+), 41 deletions(-) delete mode 100644 SPECS-EXTENDED/linuxptp/linuxptp.signatures.json create mode 100644 SPECS/linuxptp/clknetsim-phc2sys.patch create mode 100644 SPECS/linuxptp/enable-ha.patch create mode 100644 SPECS/linuxptp/linuxptp-zerolength.patch create mode 100644 SPECS/linuxptp/linuxptp.signatures.json rename {SPECS-EXTENDED => SPECS}/linuxptp/linuxptp.spec (73%) rename {SPECS-EXTENDED => SPECS}/linuxptp/phc2sys.service (100%) create mode 100644 SPECS/linuxptp/ptp4l.conf rename {SPECS-EXTENDED => SPECS}/linuxptp/ptp4l.service (100%) rename {SPECS-EXTENDED => SPECS}/linuxptp/timemaster.conf (78%) rename {SPECS-EXTENDED => SPECS}/linuxptp/timemaster.service (100%) diff --git a/SPECS-EXTENDED/linuxptp/linuxptp.signatures.json b/SPECS-EXTENDED/linuxptp/linuxptp.signatures.json deleted file mode 100644 index 6896ea2f02..0000000000 --- a/SPECS-EXTENDED/linuxptp/linuxptp.signatures.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "Signatures": { - "clknetsim-79ffe4.tar.gz": "2d60fb5d6a12dd12fafa07b86a0ed8eba2bf552987e960c6d468c4869199dd6a", - "linuxptp-e05809.tar.gz": "296bcd84942ad0f9d0dccd703b89ba19a70a2633990598d0460dc7d017d15360", - "linuxptp-testsuite-a7f6e1.tar.gz": "ac0fc97a5e4b3ae61665bb07c922fdcf1e0d62ccc097a3de4367d72c2b0ec70a", - "phc2sys.service": "4bab3fe8ba6b801d6092d820c4fa4514973f441af5967f4c597ecd60d863c752", - "ptp4l.service": "2d85b55077bb99091ddf66917b86044f75fe31584b647a7df06994eee5ecf6bd", - "timemaster.conf": "c8bbe715944126ac4063199981c4e157228ee52ed7caa71e2a9523d5d0c57943", - "timemaster.service": "01af35467d2400f12e7c95df94e069bba89ad06c048b6f346fc26676db2e6b42" - } -} \ No newline at end of file diff --git a/SPECS/linuxptp/clknetsim-phc2sys.patch b/SPECS/linuxptp/clknetsim-phc2sys.patch new file mode 100644 index 0000000000..1d2b649105 --- /dev/null +++ b/SPECS/linuxptp/clknetsim-phc2sys.patch @@ -0,0 +1,22 @@ +commit 2c62b9a3d8aa61bbb45a522c47be1ff2261e9b0e +Author: Miroslav Lichvar +Date: Mon Mar 14 11:40:50 2022 +0100 + + bash: remove default options for phc2sys + + Don't set any options by default for phc2sys to avoid conflict between + -O and -a. + +diff --git a/clknetsim.bash b/clknetsim.bash +index becc94d..eed622c 100644 +--- a/clknetsim.bash ++++ b/clknetsim.bash +@@ -82,7 +82,7 @@ start_client() { + args+=($opts) + ;; + phc2sys) +- args=(-s /dev/ptp0 -O 0 $opts $config) ++ args=($opts $config) + ;; + nsm) + args=($opts) diff --git a/SPECS/linuxptp/enable-ha.patch b/SPECS/linuxptp/enable-ha.patch new file mode 100644 index 0000000000..740eec6294 --- /dev/null +++ b/SPECS/linuxptp/enable-ha.patch @@ -0,0 +1,11615 @@ +From 63b43924294da6cb177d0509120b2e957580441c Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:52 +0200 +Subject: [PATCH 1/47] clock: Reset state when switching port with same best clock. + +When the best port is changed, but the ID of the best clock doesn't +change (e.g. a passive port is activated on link failure), reset the +current delay and other master/link-specific state to avoid the switch +throwing the clock off. + +Reviewed-by: Jacob Keller +Signed-off-by: Miroslav Lichvar +[commit 7e8eba5332671abfd95d06dd191059eded1d2cca upstream] +Signed-off-by: Jim Somerville +--- + clock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/clock.c b/clock.c +index a66d189..96453f4 100644 +--- a/clock.c ++++ b/clock.c +@@ -1857,7 +1857,7 @@ static void handle_state_decision_event(struct clock *c) + cid2str(&best_id)); + } + +- if (!cid_eq(&best_id, &c->best_id)) { ++ if (!cid_eq(&best_id, &c->best_id) || best != c->best) { + clock_freq_est_reset(c); + tsproc_reset(c->tsproc, 1); + if (!tmv_is_zero(c->initial_delay)) +-- +2.25.1 + +From 1779482f39e6513995b13fdbd350f7aee8495b7e Mon Sep 17 00:00:00 2001 +Message-Id: <1779482f39e6513995b13fdbd350f7aee8495b7e.1630418391.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:53 +0200 +Subject: [PATCH 2/47] clock: Reset clock check on best clock/port change. + +Reset the clock check when the best clock or port changes, together with +the other state like current estimated delay and frequency. This avoids +false positives if the clock is controlled by an external process when +not synchronized by PTP (e.g. phc2sys -rr). + +Reviewed-by: Jacob Keller +Signed-off-by: Miroslav Lichvar +[commit 262a49b07eaccc0f0237e3cd4df01b185b8f664f upstream] +Signed-off-by: Jim Somerville +--- + clock.c | 2 ++ + clockcheck.c | 9 ++++++++- + clockcheck.h | 6 ++++++ + 3 files changed, 16 insertions(+), 1 deletion(-) + +diff --git a/clock.c b/clock.c +index d955710..49bd4a9 100644 +--- a/clock.c ++++ b/clock.c +@@ -1911,6 +1911,8 @@ static void handle_state_decision_event(struct clock *c) + + if (!cid_eq(&best_id, &c->best_id) || best != c->best) { + clock_freq_est_reset(c); ++ if (c->sanity_check) ++ clockcheck_reset(c->sanity_check); + tsproc_reset(c->tsproc, 1); + if (!tmv_is_zero(c->initial_delay)) + tsproc_set_delay(c->tsproc, c->initial_delay); +diff --git a/clockcheck.c b/clockcheck.c +index d48a578..d0b4714 100644 +--- a/clockcheck.c ++++ b/clockcheck.c +@@ -47,9 +47,16 @@ struct clockcheck *clockcheck_create(int freq_limit) + if (!cc) + return NULL; + cc->freq_limit = freq_limit; ++ clockcheck_reset(cc); ++ return cc; ++} ++ ++void clockcheck_reset(struct clockcheck *cc) ++{ ++ cc->freq_known = 0; + cc->max_freq = -CHECK_MAX_FREQ; + cc->min_freq = CHECK_MAX_FREQ; +- return cc; ++ cc->last_ts = 0; + } + + int clockcheck_sample(struct clockcheck *cc, uint64_t ts) +diff --git a/clockcheck.h b/clockcheck.h +index 78aca48..1ff86eb 100644 +--- a/clockcheck.h ++++ b/clockcheck.h +@@ -33,6 +33,12 @@ struct clockcheck; + */ + struct clockcheck *clockcheck_create(int freq_limit); + ++/** ++ * Reset a clock check. ++ * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). ++ */ ++void clockcheck_reset(struct clockcheck *cc); ++ + /** + * Perform the sanity check on a time stamp. + * @param cc Pointer to a clock check obtained via @ref clockcheck_create(). +-- +2.29.2 + +From a1ed560a712d611edf8b47756bc56542a57bff7d Mon Sep 17 00:00:00 2001 +Message-Id: +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:54 +0200 +Subject: [PATCH 3/47] port: Don't check timestamps from non-slave ports. + +Don't perform the sanity check on receive timestamps from ports in +non-slave states to avoid false positives in the jbod mode, where +the timestamps can be generated by different clocks. + +Reviewed-by: Jacob Keller +Signed-off-by: Miroslav Lichvar +[commit e117e37e379556fa23337db2518bb44d8793e039 upstream] +Signed-off-by: Jim Somerville +--- + port.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/port.c b/port.c +index 9e9d484..387d5a2 100644 +--- a/port.c ++++ b/port.c +@@ -2731,7 +2731,10 @@ static enum fsm_event bc_event(struct port *p, int fd_index) + } + if (msg_sots_valid(msg)) { + ts_add(&msg->hwts.ts, -p->rx_timestamp_offset); +- clock_check_ts(p->clock, tmv_to_nanoseconds(msg->hwts.ts)); ++ if (p->state == PS_SLAVE) { ++ clock_check_ts(p->clock, ++ tmv_to_nanoseconds(msg->hwts.ts)); ++ } + } + + switch (msg_type(msg)) { +-- +2.29.2 + +From 5caa4d0a9161e6a33e269c8e445b322e4437e6b3 Mon Sep 17 00:00:00 2001 +Message-Id: <5caa4d0a9161e6a33e269c8e445b322e4437e6b3.1630418391.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:55 +0200 +Subject: [PATCH 4/47] port: Don't renew raw transport. + +Renewing of the transport on announce/sync timeout is needed in the +client-only mode to avoid getting stuck with a broken multicast socket +when the link goes down. + +This shouldn't be necessary with the raw transport. Closing and binding +of raw sockets can apparently be so slow that it triggers a false +positive in the clock check. + +Reported-by: Amar Subramanyam +Signed-off-by: Miroslav Lichvar +Reviewed-by: Jacob Keller +[commit 6df84259647757bc53818a039734f8ff85618c02 upstream] +Signed-off-by: Jim Somerville +--- + port.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/port.c b/port.c +index 387d5a2..d26b87f 100644 +--- a/port.c ++++ b/port.c +@@ -1805,6 +1805,12 @@ static int port_renew_transport(struct port *p) + if (!port_is_enabled(p)) { + return 0; + } ++ ++ /* Closing and binding of raw sockets is too slow and unnecessary */ ++ if (transport_type(p->trp) == TRANS_IEEE_802_3) { ++ return 0; ++ } ++ + transport_close(p->trp, &p->fda); + port_clear_fda(p, FD_FIRST_TIMER); + res = transport_open(p->trp, p->iface, &p->fda, p->timestamping); +-- +2.29.2 + +From 3bf4f1784fa0a03a252961f400a78d963773f8f5 Mon Sep 17 00:00:00 2001 +Message-Id: <3bf4f1784fa0a03a252961f400a78d963773f8f5.1630611367.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630611367.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630611367.git.Jim.Somerville@windriver.com> +From: Miroslav Lichvar +Date: Mon, 31 May 2021 11:07:56 +0200 +Subject: [PATCH 5/47] clockcheck: Increase minimum interval. + +Increase the minimum check interval to 1 second to measure the frequency +offset more accurately and with default configuration make false +positives less likely due to a heavily overloaded system. + +Signed-off-by: Miroslav Lichvar +Reviewed-by: Jacob Keller +[commit a082bcd700e4955ebaa00d7039bf4bce92048ac4 upstream] +Signed-off-by: Jim Somerville +--- + clockcheck.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/clockcheck.c b/clockcheck.c +index d0b4714..f0141be 100644 +--- a/clockcheck.c ++++ b/clockcheck.c +@@ -23,7 +23,7 @@ + #include "clockcheck.h" + #include "print.h" + +-#define CHECK_MIN_INTERVAL 100000000 ++#define CHECK_MIN_INTERVAL 1000000000 + #define CHECK_MAX_FREQ 900000000 + + struct clockcheck { +-- +2.29.2 + +From 3a6de7b6208ccc64a20474db15abaac08e99d10b Mon Sep 17 00:00:00 2001 +Message-Id: <3a6de7b6208ccc64a20474db15abaac08e99d10b.1630418391.git.Jim.Somerville@windriver.com> +In-Reply-To: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +References: <0389752e3aecf8d2b2743f16ce1408a58088bea9.1630418391.git.Jim.Somerville@windriver.com> +From: Cole Walker +Date: Wed, 23 Jun 2021 11:14:41 -0400 +Subject: [PATCH 6/47] Add option to disable default port selection in phc2sys + +This change serves to address an issue in phc2sys +where the local ptp clocks are not synced together properly if the local +time is far behind the reference time. This issue occurs when phc2sys +starts and there is no client port currently synced to a grandmaster. In +the original behaviour, phc2sys selects the first configured port and +proceeds to sync all of the other clocks to it by performing the +first_step operation. + +Then ptp4l will evenually lock to the Grandmaster clock, and that +single port will have its time updated to the correct value, but +phc2sys has already performed the first_step operation and will not +step the other clocks again. + +This solution provides an option to disable the selection of a +default port by phc2sys. When no default port is selected, phc2sys waits +for ptp4l to sync to the Grandmaster before bringing the other clocks +into sync with the first_step operation. + +This option is configured via the default_sync +parameter or the -D flag. The default_sync parameter is set to on by +default in order to keep the behaviour the same as upstream linuxptp +but can be configured by users via +system service-parameter-add ptp global default_sync=0 + +Signed-off-by: Jim Somerville +--- + config.c | 1 + + phc2sys.c | 15 ++++++++++++--- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/config.c b/config.c +index ef5e833..cab7e4f 100644 +--- a/config.c ++++ b/config.c +@@ -333,6 +333,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("utc_offset", CURRENT_UTC_OFFSET, 0, INT_MAX), + GLOB_ITEM_INT("verbose", 0, 0, 1), + GLOB_ITEM_INT("write_phase_mode", 0, 0, 1), ++ GLOB_ITEM_INT("default_sync", 1, 0, 1), + }; + + static struct unicast_master_table *current_uc_mtab; +diff --git a/phc2sys.c b/phc2sys.c +index a36cbe0..44d6872 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -120,6 +120,7 @@ struct phc2sys_private { + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; + struct clock *master; ++ int default_sync; + }; + + static struct config *phc2sys_config; +@@ -437,7 +438,7 @@ static void reconfigure(struct phc2sys_private *priv) + } + last = c; + } +- if (dst_cnt > 1 && !src) { ++ if (dst_cnt > 1 && !src && priv->default_sync) { + if (!rt || rt->dest_only) { + priv->master = last; + /* Reset to original state in next reconfiguration. */ +@@ -1344,6 +1345,7 @@ static void usage(char *progname) + " -N [num] number of master clock readings per update (5)\n" + " -L [limit] sanity frequency limit in ppb (200000000)\n" + " -M [num] NTP SHM segment number (0)\n" ++ " -D [num] fall back to default clock in automatic mode (1)\n" + " -u [num] number of clock updates in summary stats (0)\n" + " -n [num] domain number (0)\n" + " -x apply leap seconds by servo instead of kernel\n" +@@ -1364,7 +1366,7 @@ int main(int argc, char *argv[]) + struct clock *src, *dst; + struct config *cfg; + struct option *opts; +- int autocfg = 0, c, domain_number = 0, index, ntpshm_segment; ++ int autocfg = 0, c, domain_number = 0, default_sync = 1, index, ntpshm_segment; + int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0, wait_sync = 0; + double phc_rate, tmp; + struct phc2sys_private priv = { +@@ -1388,7 +1390,7 @@ int main(int argc, char *argv[]) + progname = strrchr(argv[0], '/'); + progname = progname ? 1+progname : argv[0]; + while (EOF != (c = getopt_long(argc, argv, +- "arc:d:f:s:E:P:I:S:F:R:N:O:L:M:i:u:wn:xz:l:t:mqvh", ++ "arc:d:f:s:E:P:I:S:F:R:N:O:L:M:D:i:u:wn:xz:l:t:mqvh", + opts, &index))) { + switch (c) { + case 0: +@@ -1540,6 +1542,12 @@ int main(int argc, char *argv[]) + version_show(stdout); + config_destroy(cfg); + return 0; ++ case 'D': ++ if (get_arg_val_i(c, optarg, &default_sync, 0, 1) || ++ config_set_int(cfg, "default_sync", default_sync)) { ++ goto end; ++ } ++ break; + case 'h': + usage(progname); + config_destroy(cfg); +@@ -1588,6 +1596,7 @@ int main(int argc, char *argv[]) + } + priv.kernel_leap = config_get_int(cfg, NULL, "kernel_leap"); + priv.sanity_freq_limit = config_get_int(cfg, NULL, "sanity_freq_limit"); ++ priv.default_sync = config_get_int(cfg, NULL, "default_sync"); + + if (autocfg) { + if (init_pmc(cfg, &priv)) +-- +2.29.2 + +From 6428c2628c013c408ec09355ad37eb12fa6bb20f Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:35 +0200 +Subject: [PATCH 7/47] sysoff: Change sysoff_measure() to return errno. + +Return -errno from failed ioctl instead of the SYSOFF_* enum from the +measurement functions to allow the callers to check for specific errors. + +Signed-off-by: Miroslav Lichvar +[commit 7824b13db9533ddebe37cf444d7aaa5d235575d3 upstream] +Signed-off-by: Douglas Henrique Koerich +--- + sysoff.c | 15 ++++++++------- + sysoff.h | 2 +- + 2 files changed, 9 insertions(+), 8 deletions(-) + +diff --git a/sysoff.c b/sysoff.c +index 2743859..5d3b907 100644 +--- a/sysoff.c ++++ b/sysoff.c +@@ -17,6 +17,7 @@ + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ ++#include + #include + #include + #include +@@ -38,11 +39,11 @@ static int sysoff_precise(int fd, int64_t *result, uint64_t *ts) + memset(&pso, 0, sizeof(pso)); + if (ioctl(fd, PTP_SYS_OFFSET_PRECISE, &pso)) { + pr_debug("ioctl PTP_SYS_OFFSET_PRECISE: %m"); +- return SYSOFF_RUN_TIME_MISSING; ++ return -errno; + } + *result = pctns(&pso.sys_realtime) - pctns(&pso.device); + *ts = pctns(&pso.sys_realtime); +- return SYSOFF_PRECISE; ++ return 0; + } + + static int64_t sysoff_estimate(struct ptp_clock_time *pct, int extended, +@@ -98,10 +99,10 @@ static int sysoff_extended(int fd, int n_samples, + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET_EXTENDED, &pso)) { + pr_debug("ioctl PTP_SYS_OFFSET_EXTENDED: %m"); +- return SYSOFF_RUN_TIME_MISSING; ++ return -errno; + } + *result = sysoff_estimate(&pso.ts[0][0], 1, n_samples, ts, delay); +- return SYSOFF_EXTENDED; ++ return 0; + } + + static int sysoff_basic(int fd, int n_samples, +@@ -112,10 +113,10 @@ static int sysoff_basic(int fd, int n_samples, + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET, &pso)) { + perror("ioctl PTP_SYS_OFFSET"); +- return SYSOFF_RUN_TIME_MISSING; ++ return -errno; + } + *result = sysoff_estimate(pso.ts, 0, n_samples, ts, delay); +- return SYSOFF_BASIC; ++ return 0; + } + + int sysoff_measure(int fd, int method, int n_samples, +@@ -130,7 +131,7 @@ int sysoff_measure(int fd, int method, int n_samples, + case SYSOFF_BASIC: + return sysoff_basic(fd, n_samples, result, ts, delay); + } +- return SYSOFF_RUN_TIME_MISSING; ++ return -EOPNOTSUPP; + } + + int sysoff_probe(int fd, int n_samples) +diff --git a/sysoff.h b/sysoff.h +index e4de919..5480f8f 100644 +--- a/sysoff.h ++++ b/sysoff.h +@@ -44,7 +44,7 @@ int sysoff_probe(int fd, int n_samples); + * @param result The estimated offset in nanoseconds. + * @param ts The system time corresponding to the 'result'. + * @param delay The delay in reading of the clock in nanoseconds. +- * @return One of the SYSOFF_ enumeration values. ++ * @return Zero on success, negative error code otherwise. + */ + int sysoff_measure(int fd, int method, int n_samples, + int64_t *result, uint64_t *ts, int64_t *delay); +-- +2.29.2 + +From 38a530d94fc5aa73bde424d05e2e38348e64d7e5 Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:36 +0200 +Subject: [PATCH 8/47] sysoff: Change log level of ioctl error messages. + +Change the log level of ioctl error messages to the error level to make +them visible in default configuration, with the exception of EOPNOTSUPP +which is expected in probing and should stay at the debug level to avoid +confusing users. + +Signed-off-by: Miroslav Lichvar +[commit 270709323a161ff1cb83af511ce50691152c75cf upstream] +Signed-off-by: Douglas Henrique Koerich +--- + sysoff.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/sysoff.c b/sysoff.c +index 5d3b907..a425275 100644 +--- a/sysoff.c ++++ b/sysoff.c +@@ -28,6 +28,14 @@ + + #define NS_PER_SEC 1000000000LL + ++static void print_ioctl_error(const char *name) ++{ ++ if (errno == EOPNOTSUPP) ++ pr_debug("ioctl %s: %s", name, strerror(errno)); ++ else ++ pr_err("ioctl %s: %s", name, strerror(errno)); ++} ++ + static int64_t pctns(struct ptp_clock_time *t) + { + return t->sec * NS_PER_SEC + t->nsec; +@@ -38,7 +46,7 @@ static int sysoff_precise(int fd, int64_t *result, uint64_t *ts) + struct ptp_sys_offset_precise pso; + memset(&pso, 0, sizeof(pso)); + if (ioctl(fd, PTP_SYS_OFFSET_PRECISE, &pso)) { +- pr_debug("ioctl PTP_SYS_OFFSET_PRECISE: %m"); ++ print_ioctl_error("PTP_SYS_OFFSET_PRECISE"); + return -errno; + } + *result = pctns(&pso.sys_realtime) - pctns(&pso.device); +@@ -98,7 +106,7 @@ static int sysoff_extended(int fd, int n_samples, + memset(&pso, 0, sizeof(pso)); + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET_EXTENDED, &pso)) { +- pr_debug("ioctl PTP_SYS_OFFSET_EXTENDED: %m"); ++ print_ioctl_error("PTP_SYS_OFFSET_EXTENDED"); + return -errno; + } + *result = sysoff_estimate(&pso.ts[0][0], 1, n_samples, ts, delay); +@@ -112,7 +120,7 @@ static int sysoff_basic(int fd, int n_samples, + memset(&pso, 0, sizeof(pso)); + pso.n_samples = n_samples; + if (ioctl(fd, PTP_SYS_OFFSET, &pso)) { +- perror("ioctl PTP_SYS_OFFSET"); ++ print_ioctl_error("PTP_SYS_OFFSET"); + return -errno; + } + *result = sysoff_estimate(pso.ts, 0, n_samples, ts, delay); +-- +2.29.2 + +From 11ae077e31d9957df01aacfa17eea5b5d548b249 Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:37 +0200 +Subject: [PATCH 9/47] sysoff: Retry on EBUSY when probing supported ioctls. + +Handle EBUSY when probing support for a PTP_SYS_OFFSET ioctl. Try each +ioctl up to three times before giving up on it to make the detection +more reliable. + +Signed-off-by: Miroslav Lichvar +[commit dadd2593c7beaee9eba5828a7bd8a0b5849dd8bb upstream] +Signed-off-by: Douglas Henrique Koerich +--- + sysoff.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/sysoff.c b/sysoff.c +index a425275..fc1f7ca 100644 +--- a/sysoff.c ++++ b/sysoff.c +@@ -145,8 +145,8 @@ int sysoff_measure(int fd, int method, int n_samples, + int sysoff_probe(int fd, int n_samples) + { + int64_t junk, delay; ++ int i, j, err; + uint64_t ts; +- int i; + + if (n_samples > PTP_MAX_SAMPLES) { + fprintf(stderr, "warning: %d exceeds kernel max readings %d\n", +@@ -156,9 +156,15 @@ int sysoff_probe(int fd, int n_samples) + } + + for (i = 0; i < SYSOFF_LAST; i++) { +- if (sysoff_measure(fd, i, n_samples, &junk, &ts, &delay) < 0) +- continue; +- return i; ++ for (j = 0; j < 3; j++) { ++ err = sysoff_measure(fd, i, n_samples, &junk, &ts, ++ &delay); ++ if (err == -EBUSY) ++ continue; ++ if (err) ++ break; ++ return i; ++ } + } + + return SYSOFF_RUN_TIME_MISSING; +-- +2.29.2 + +From e4fd6a930213e6f0f009eb070d51b1e14db60d1b Mon Sep 17 00:00:00 2001 +From: Miroslav Lichvar +Date: Wed, 18 May 2022 11:33:38 +0200 +Subject: [PATCH 10/47] phc2sys: Don't exit when reading of PHC fails with EBUSY. + +Reading of the PHC can occasionally fail with some drivers, e.g. the ice +driver returns EBUSY when it fails to get a lock. Continue in the loop +instead of exiting on the error. + +Signed-off-by: Miroslav Lichvar + +[ commit e8dc364f9fd5fbdac5d2c5e433f28e9da0028d49 upstream + We drop two hunks of it, namely the stuff that applies to + clockadj_compare, because they apply to the source code + ahead of baseline currently used by StarlingX ] + +Signed-off-by: Douglas Henrique Koerich +--- + phc2sys.c | 31 +++++++++++++++++++------------ + 1 file changed, 19 insertions(+), 12 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 44d6872..7959015 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -722,6 +722,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + struct clock *clock; + uint64_t ts; + int64_t offset, delay; ++ int err; + + interval.tv_sec = priv->phc_interval; + interval.tv_nsec = (priv->phc_interval - interval.tv_sec) * 1e9; +@@ -765,28 +766,34 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + if (clock->clkid == CLOCK_REALTIME && + priv->master->sysoff_method >= 0) { + /* use sysoff */ +- if (sysoff_measure(CLOCKID_TO_FD(priv->master->clkid), +- priv->master->sysoff_method, +- priv->phc_readings, +- &offset, &ts, &delay) < 0) +- return -1; ++ err = sysoff_measure(CLOCKID_TO_FD(priv->master->clkid), ++ priv->master->sysoff_method, ++ priv->phc_readings, ++ &offset, &ts, &delay); + } else if (priv->master->clkid == CLOCK_REALTIME && + clock->sysoff_method >= 0) { + /* use reversed sysoff */ +- if (sysoff_measure(CLOCKID_TO_FD(clock->clkid), +- clock->sysoff_method, +- priv->phc_readings, +- &offset, &ts, &delay) < 0) +- return -1; +- offset = -offset; +- ts += offset; ++ err = sysoff_measure(CLOCKID_TO_FD(clock->clkid), ++ clock->sysoff_method, ++ priv->phc_readings, ++ &offset, &ts, &delay); ++ if (!err) { ++ offset = -offset; ++ ts += offset; ++ } + } else { ++ err = 0; + /* use phc */ + if (!read_phc(priv->master->clkid, clock->clkid, + priv->phc_readings, + &offset, &ts, &delay)) + continue; + } ++ if (err == -EBUSY) ++ continue; ++ if (err) ++ return -1; ++ + update_clock(priv, clock, offset, ts, delay); + } + } +-- +2.29.2 + +From 0c5c39a8cd3675d91e872a75d75854907950957d Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 13:47:47 -0300 +Subject: [PATCH 11/47] phc2sys: extract PMC functionality into a smaller + struct pmc_node + +This creates a smaller structure within phc2sys_private, which embeds +all properties related to the PMC. This structure is called "pmc_node", +which is somewhat reminiscent of the old name of phc2sys_private (struct +node). But the advantage is that struct pmc_node can be reused by other +modules. + +The phc2sys code that is executed upon a subscription update, +recv_subscribed, is now refactored into a function pointer callback. It +is imaginable that other programs might to do other things in it. +Note that putting this function pointer in struct pmc_node is, long +term, maybe not the best of choices. It is only needed from the +run_pmc_events() code path, and could be therefore passed as a more +local callback to that function only. However, for that, further +refactoring is needed inside the common run_pmc() function, so that is +being left for another time. + +Signed-off-by: Vladimir Oltean + +[commit 1ca1419ad7e6cc04cf893f5a9ca449a90f39f4e0 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 228 ++++++++++++++++++++++++++++++------------------------ + 1 file changed, 125 insertions(+), 103 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 7959015..86b9822 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -39,6 +39,7 @@ + + #include "clockadj.h" + #include "clockcheck.h" ++#include "contain.h" + #include "ds.h" + #include "fsm.h" + #include "missing.h" +@@ -99,23 +100,34 @@ struct port { + struct clock *clock; + }; + ++struct pmc_node; ++ ++typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++ struct ptp_message *msg, ++ int excluded); ++ ++struct pmc_node { ++ struct pmc *pmc; ++ int pmc_ds_requested; ++ uint64_t pmc_last_update; ++ int sync_offset; ++ int leap; ++ int utc_offset_traceable; ++ int clock_identity_set; ++ struct ClockIdentity clock_identity; ++ pmc_node_recv_subscribed_t *recv_subscribed; ++}; ++ + struct phc2sys_private { + unsigned int stats_max_count; + int sanity_freq_limit; + enum servo_type servo_type; + int phc_readings; + double phc_interval; +- int sync_offset; + int forced_sync_offset; +- int utc_offset_traceable; +- int leap; + int kernel_leap; +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; + int state_changed; +- int clock_identity_set; +- struct ClockIdentity clock_identity; ++ struct pmc_node node; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -125,16 +137,16 @@ struct phc2sys_private { + + static struct config *phc2sys_config; + +-static int update_pmc(struct phc2sys_private *priv, int subscribe); ++static int update_pmc_node(struct pmc_node *node, int subscribe); + static int clock_handle_leap(struct phc2sys_private *priv, + struct clock *clock, + int64_t offset, uint64_t ts); +-static int run_pmc_get_utc_offset(struct phc2sys_private *priv, ++static int run_pmc_get_utc_offset(struct pmc_node *node, + int timeout); +-static void run_pmc_events(struct phc2sys_private *priv); ++static void run_pmc_events(struct pmc_node *node); + + static int normalize_state(int state); +-static int run_pmc_port_properties(struct phc2sys_private *priv, ++static int run_pmc_port_properties(struct pmc_node *node, + int timeout, unsigned int port, + int *state, int *tstamping, char *iface); + +@@ -325,7 +337,7 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + + LIST_FOREACH(p, &priv->ports, list) { + if (p->clock == clock) { +- ret = run_pmc_port_properties(priv, 1000, p->number, ++ ret = run_pmc_port_properties(&priv->node, 1000, p->number, + &state, ×tamping, + iface); + if (ret > 0) +@@ -660,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + + if (src == CLOCK_INVALID) { + /* The sync offset can't be applied with PPS alone. */ +- priv->sync_offset = 0; ++ priv->node.sync_offset = 0; + } else { + enable_pps_output(priv->master->clkid); + } +@@ -691,7 +703,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc(priv, 0) < 0) ++ if (update_pmc_node(&priv->node, 0) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -729,15 +741,15 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); +- if (update_pmc(priv, subscriptions) < 0) ++ if (update_pmc_node(&priv->node, subscriptions) < 0) + continue; + + if (subscriptions) { +- run_pmc_events(priv); ++ run_pmc_events(&priv->node); + if (priv->state_changed) { + /* force getting offset, as it may have + * changed after the port state change */ +- if (run_pmc_get_utc_offset(priv, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + continue; + } +@@ -800,13 +812,12 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + return 0; + } + +-static int check_clock_identity(struct phc2sys_private *priv, +- struct ptp_message *msg) ++static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) + { +- if (!priv->clock_identity_set) ++ if (!node->clock_identity_set) + return 1; +- return cid_eq(&priv->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); ++ return cid_eq(&node->clock_identity, ++ &msg->header.sourcePortIdentity.clockIdentity); + } + + static int is_msg_mgt(struct ptp_message *msg) +@@ -876,9 +887,13 @@ static int clock_compute_state(struct phc2sys_private *priv, + return state; + } + +-static int recv_subscribed(struct phc2sys_private *priv, +- struct ptp_message *msg, int excluded) ++#define node_to_phc2sys(node) \ ++ container_of(node, struct phc2sys_private, node) ++ ++static int phc2sys_recv_subscribed(struct pmc_node *node, ++ struct ptp_message *msg, int excluded) + { ++ struct phc2sys_private *priv = node_to_phc2sys(node); + int mgt_id, state; + struct portDS *pds; + struct port *port; +@@ -913,29 +928,28 @@ static int recv_subscribed(struct phc2sys_private *priv, + return 0; + } + +-static void send_subscription(struct phc2sys_private *priv) ++static void send_subscription(struct pmc_node *node) + { + struct subscribe_events_np sen; + + memset(&sen, 0, sizeof(sen)); + sen.duration = PMC_SUBSCRIBE_DURATION; + sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; +- pmc_send_set_action(priv->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++ pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +-static int init_pmc(struct config *cfg, struct phc2sys_private *priv) ++static int init_pmc_node(struct config *cfg, struct pmc_node *node, ++ const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) + { +- char uds_local[MAX_IFNAME_SIZE + 1]; +- +- snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d", +- getpid()); +- priv->pmc = pmc_create(cfg, TRANS_UDS, uds_local, 0, ++ node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, + config_get_int(cfg, NULL, "domainNumber"), + config_get_int(cfg, NULL, "transportSpecific") << 4, 1); +- if (!priv->pmc) { ++ if (!node->pmc) { + pr_err("failed to create pmc"); + return -1; + } ++ node->recv_subscribed = recv_subscribed; + + return 0; + } +@@ -946,7 +960,7 @@ static int init_pmc(struct config *cfg, struct phc2sys_private *priv) + * -1: error reported by the other side + * -2: local error, fatal + */ +-static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, ++static int run_pmc(struct pmc_node *node, int timeout, int ds_id, + struct ptp_message **msg) + { + #define N_FD 1 +@@ -954,9 +968,9 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + int cnt, res; + + while (1) { +- pollfd[0].fd = pmc_get_transport_fd(priv->pmc); ++ pollfd[0].fd = pmc_get_transport_fd(node->pmc); + pollfd[0].events = POLLIN|POLLPRI; +- if (!priv->pmc_ds_requested && ds_id >= 0) ++ if (!node->pmc_ds_requested && ds_id >= 0) + pollfd[0].events |= POLLOUT; + + cnt = poll(pollfd, N_FD, timeout); +@@ -966,7 +980,7 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + } + if (!cnt) { + /* Request the data set again in the next run. */ +- priv->pmc_ds_requested = 0; ++ node->pmc_ds_requested = 0; + return 0; + } + +@@ -975,24 +989,24 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + !(pollfd[0].revents & (POLLIN|POLLPRI))) { + switch (ds_id) { + case TLV_SUBSCRIBE_EVENTS_NP: +- send_subscription(priv); ++ send_subscription(node); + break; + default: +- pmc_send_get_action(priv->pmc, ds_id); ++ pmc_send_get_action(node->pmc, ds_id); + break; + } +- priv->pmc_ds_requested = 1; ++ node->pmc_ds_requested = 1; + } + + if (!(pollfd[0].revents & (POLLIN|POLLPRI))) + continue; + +- *msg = pmc_recv(priv->pmc); ++ *msg = pmc_recv(node->pmc); + + if (!*msg) + continue; + +- if (!check_clock_identity(priv, *msg)) { ++ if (!check_clock_identity(node, *msg)) { + msg_put(*msg); + *msg = NULL; + continue; +@@ -1000,29 +1014,29 @@ static int run_pmc(struct phc2sys_private *priv, int timeout, int ds_id, + + res = is_msg_mgt(*msg); + if (res < 0 && get_mgt_err_id(*msg) == ds_id) { +- priv->pmc_ds_requested = 0; ++ node->pmc_ds_requested = 0; + return -1; + } +- if (res <= 0 || recv_subscribed(priv, *msg, ds_id) || ++ if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || + get_mgt_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; + continue; + } +- priv->pmc_ds_requested = 0; ++ node->pmc_ds_requested = 0; + return 1; + } + } + +-static int run_pmc_wait_sync(struct phc2sys_private *priv, int timeout) ++static int run_pmc_wait_sync(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; +- int res; +- void *data; + Enumeration8 portState; ++ void *data; ++ int res; + + while (1) { +- res = run_pmc(priv, timeout, TLV_PORT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); + if (res <= 0) + return res; + +@@ -1036,47 +1050,47 @@ static int run_pmc_wait_sync(struct phc2sys_private *priv, int timeout) + return 1; + } + /* try to get more data sets (for other ports) */ +- priv->pmc_ds_requested = 1; ++ node->pmc_ds_requested = 1; + } + } + +-static int run_pmc_get_utc_offset(struct phc2sys_private *priv, int timeout) ++static int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; + struct timePropertiesDS *tds; + +- res = run_pmc(priv, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); + if (res <= 0) + return res; + + tds = (struct timePropertiesDS *)get_mgt_data(msg); + if (tds->flags & PTP_TIMESCALE) { +- priv->sync_offset = tds->currentUtcOffset; ++ node->sync_offset = tds->currentUtcOffset; + if (tds->flags & LEAP_61) +- priv->leap = 1; ++ node->leap = 1; + else if (tds->flags & LEAP_59) +- priv->leap = -1; ++ node->leap = -1; + else +- priv->leap = 0; +- priv->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && + tds->flags & TIME_TRACEABLE; + } else { +- priv->sync_offset = 0; +- priv->leap = 0; +- priv->utc_offset_traceable = 0; ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; + } + msg_put(msg); + return 1; + } + +-static int run_pmc_get_number_ports(struct phc2sys_private *priv, int timeout) ++static int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; + struct defaultDS *dds; + +- res = run_pmc(priv, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); + if (res <= 0) + return res; + +@@ -1086,36 +1100,36 @@ static int run_pmc_get_number_ports(struct phc2sys_private *priv, int timeout) + return res; + } + +-static int run_pmc_subscribe(struct phc2sys_private *priv, int timeout) ++static int run_pmc_subscribe(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; + +- res = run_pmc(priv, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); + if (res <= 0) + return res; + msg_put(msg); + return 1; + } + +-static void run_pmc_events(struct phc2sys_private *priv) ++static void run_pmc_events(struct pmc_node *node) + { + struct ptp_message *msg; + +- run_pmc(priv, 0, -1, &msg); ++ run_pmc(node, 0, -1, &msg); + } + +-static int run_pmc_port_properties(struct phc2sys_private *priv, int timeout, +- unsigned int port, +- int *state, int *tstamping, char *iface) ++static int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) + { + struct ptp_message *msg; + int res, len; + struct port_properties_np *ppn; + +- pmc_target_port(priv->pmc, port); ++ pmc_target_port(node->pmc, port); + while (1) { +- res = run_pmc(priv, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); + if (res <= 0) + goto out; + +@@ -1138,32 +1152,35 @@ static int run_pmc_port_properties(struct phc2sys_private *priv, int timeout, + break; + } + out: +- pmc_target_all(priv->pmc); ++ pmc_target_all(node->pmc); + return res; + } + +-static int run_pmc_clock_identity(struct phc2sys_private *priv, int timeout) ++static int run_pmc_clock_identity(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + struct defaultDS *dds; + int res; + +- res = run_pmc(priv, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); + if (res <= 0) + return res; + + dds = (struct defaultDS *)get_mgt_data(msg); +- memcpy(&priv->clock_identity, &dds->clockIdentity, ++ memcpy(&node->clock_identity, &dds->clockIdentity, + sizeof(struct ClockIdentity)); +- priv->clock_identity_set = 1; ++ node->clock_identity_set = 1; + msg_put(msg); + return 1; + } + +-static void close_pmc(struct phc2sys_private *priv) ++static void close_pmc_node(struct pmc_node *node) + { +- pmc_destroy(priv->pmc); +- priv->pmc = NULL; ++ if (!node->pmc) ++ return; ++ ++ pmc_destroy(node->pmc); ++ node->pmc = NULL; + } + + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) +@@ -1178,7 +1195,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + while (1) { + if (!is_running()) + return -1; +- res = run_pmc_clock_identity(priv, 1000); ++ res = run_pmc_clock_identity(&priv->node, 1000); + if (res < 0) + return -1; + if (res > 0) +@@ -1187,20 +1204,20 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + pr_notice("Waiting for ptp4l..."); + } + +- number_ports = run_pmc_get_number_ports(priv, 1000); ++ number_ports = run_pmc_get_number_ports(&priv->node, 1000); + if (number_ports <= 0) { + pr_err("failed to get number of ports"); + return -1; + } + +- res = run_pmc_subscribe(priv, 1000); ++ res = run_pmc_subscribe(&priv->node, 1000); + if (res <= 0) { + pr_err("failed to subscribe"); + return -1; + } + + for (i = 1; i <= number_ports; i++) { +- res = run_pmc_port_properties(priv, 1000, i, &state, ++ res = run_pmc_port_properties(&priv->node, 1000, i, &state, + ×tamping, iface); + if (res == -1) { + /* port does not exist, ignore the port */ +@@ -1237,7 +1254,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (run_pmc_get_utc_offset(priv, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + return -1; + } +@@ -1245,7 +1262,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-static int update_pmc(struct phc2sys_private *priv, int subscribe) ++static int update_pmc_node(struct pmc_node *node, int subscribe) + { + struct timespec tp; + uint64_t ts; +@@ -1256,13 +1273,13 @@ static int update_pmc(struct phc2sys_private *priv, int subscribe) + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (priv->pmc && +- !(ts > priv->pmc_last_update && +- ts - priv->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (node->pmc && ++ !(ts > node->pmc_last_update && ++ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (subscribe) +- run_pmc_subscribe(priv, 0); +- if (run_pmc_get_utc_offset(priv, 0) > 0) +- priv->pmc_last_update = ts; ++ run_pmc_subscribe(node, 0); ++ if (run_pmc_get_utc_offset(node, 0) > 0) ++ node->pmc_last_update = ts; + } + + return 0; +@@ -1272,9 +1289,9 @@ static int update_pmc(struct phc2sys_private *priv, int subscribe) + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) + { +- int clock_leap, node_leap = priv->leap; ++ int clock_leap, node_leap = priv->node.leap; + +- clock->sync_offset = priv->sync_offset; ++ clock->sync_offset = priv->node.sync_offset; + + if ((node_leap || clock->leap_set) && + clock->is_utc != priv->master->is_utc) { +@@ -1315,7 +1332,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + } + } + +- if (priv->utc_offset_traceable && ++ if (priv->node.utc_offset_traceable && + clock->utc_offset_set != clock->sync_offset) { + if (clock->clkid == CLOCK_REALTIME) + sysclk_set_tai_offset(clock->sync_offset); +@@ -1370,6 +1387,7 @@ static void usage(char *progname) + int main(int argc, char *argv[]) + { + char *config = NULL, *dst_name = NULL, *progname, *src_name = NULL; ++ char uds_local[MAX_IFNAME_SIZE + 1]; + struct clock *src, *dst; + struct config *cfg; + struct option *opts; +@@ -1478,7 +1496,7 @@ int main(int argc, char *argv[]) + goto end; + break; + case 'O': +- if (get_arg_val_i(c, optarg, &priv.sync_offset, ++ if (get_arg_val_i(c, optarg, &priv.node.sync_offset, + INT_MIN, INT_MAX)) + goto end; + priv.forced_sync_offset = -1; +@@ -1605,8 +1623,12 @@ int main(int argc, char *argv[]) + priv.sanity_freq_limit = config_get_int(cfg, NULL, "sanity_freq_limit"); + priv.default_sync = config_get_int(cfg, NULL, "default_sync"); + ++ snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d", ++ getpid()); ++ + if (autocfg) { +- if (init_pmc(cfg, &priv)) ++ if (init_pmc_node(cfg, &priv.node, uds_local, ++ phc2sys_recv_subscribed)) + goto end; + if (auto_init_ports(&priv, rt) < 0) + goto end; +@@ -1643,11 +1665,12 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- if (init_pmc(cfg, &priv)) ++ if (init_pmc_node(cfg, &priv.node, uds_local, ++ phc2sys_recv_subscribed)) + goto end; + + while (is_running()) { +- r = run_pmc_wait_sync(&priv, 1000); ++ r = run_pmc_wait_sync(&priv.node, 1000); + if (r < 0) + goto end; + if (r > 0) +@@ -1657,7 +1680,7 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = run_pmc_get_utc_offset(&priv, 1000); ++ r = run_pmc_get_utc_offset(&priv.node, 1000); + if (r <= 0) { + pr_err("failed to get UTC offset"); + goto end; +@@ -1667,7 +1690,7 @@ int main(int argc, char *argv[]) + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || + src->clkid == CLOCK_INVALID) +- close_pmc(&priv); ++ close_pmc_node(&priv.node); + } + + if (pps_fd >= 0) { +@@ -1680,8 +1703,7 @@ int main(int argc, char *argv[]) + } + + end: +- if (priv.pmc) +- close_pmc(&priv); ++ close_pmc_node(&priv.node); + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +-- +2.25.1 + +From 87d8e7281e3e66813d0c669bea0b5335a8cbb6b6 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 13:59:48 -0300 +Subject: [PATCH 12/47] phc2sys: make PMC functions non-static + +In preparation of a trivial movement of code to pmc_common.c, remove the +"static" keyword from the functions that will end up there, since they +will be still called from phc2sys.c for now. + +Signed-off-by: Vladimir Oltean + +[commit 2ccbb14450e1e96168a2604c0e8c96ae5a6a5bf0 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 40 +++++++++++++++++++--------------------- + 1 file changed, 19 insertions(+), 21 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 86b9822..d5b8e71 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -137,18 +137,17 @@ struct phc2sys_private { + + static struct config *phc2sys_config; + +-static int update_pmc_node(struct pmc_node *node, int subscribe); ++int update_pmc_node(struct pmc_node *node, int subscribe); + static int clock_handle_leap(struct phc2sys_private *priv, + struct clock *clock, + int64_t offset, uint64_t ts); +-static int run_pmc_get_utc_offset(struct pmc_node *node, +- int timeout); +-static void run_pmc_events(struct pmc_node *node); ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++void run_pmc_events(struct pmc_node *node); + + static int normalize_state(int state); +-static int run_pmc_port_properties(struct pmc_node *node, +- int timeout, unsigned int port, +- int *state, int *tstamping, char *iface); ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); + + static struct servo *servo_add(struct phc2sys_private *priv, + struct clock *clock) +@@ -838,13 +837,13 @@ static int is_msg_mgt(struct ptp_message *msg) + return 0; + } + +-static int get_mgt_id(struct ptp_message *msg) ++int get_mgt_id(struct ptp_message *msg) + { + struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; + return mgt->id; + } + +-static void *get_mgt_data(struct ptp_message *msg) ++void *get_mgt_data(struct ptp_message *msg) + { + struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; + return mgt->data; +@@ -938,9 +937,8 @@ static void send_subscription(struct pmc_node *node) + pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +-static int init_pmc_node(struct config *cfg, struct pmc_node *node, +- const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) + { + node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, + config_get_int(cfg, NULL, "domainNumber"), +@@ -1054,7 +1052,7 @@ static int run_pmc_wait_sync(struct pmc_node *node, int timeout) + } + } + +-static int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -1084,7 +1082,7 @@ static int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + return 1; + } + +-static int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -1100,7 +1098,7 @@ static int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + return res; + } + +-static int run_pmc_subscribe(struct pmc_node *node, int timeout) ++int run_pmc_subscribe(struct pmc_node *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -1112,16 +1110,16 @@ static int run_pmc_subscribe(struct pmc_node *node, int timeout) + return 1; + } + +-static void run_pmc_events(struct pmc_node *node) ++void run_pmc_events(struct pmc_node *node) + { + struct ptp_message *msg; + + run_pmc(node, 0, -1, &msg); + } + +-static int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) + { + struct ptp_message *msg; + int res, len; +@@ -1174,7 +1172,7 @@ static int run_pmc_clock_identity(struct pmc_node *node, int timeout) + return 1; + } + +-static void close_pmc_node(struct pmc_node *node) ++void close_pmc_node(struct pmc_node *node) + { + if (!node->pmc) + return; +@@ -1262,7 +1260,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-static int update_pmc_node(struct pmc_node *node, int subscribe) ++int update_pmc_node(struct pmc_node *node, int subscribe) + { + struct timespec tp; + uint64_t ts; +-- +2.25.1 + +From ba9c2ea0e1f7a96093bca1147d4745a7d0ce17b6 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:34:19 -0300 +Subject: [PATCH 13/47] phc2sys: break out pmc code into pmc_common.c + +The code through which phc2sys sends various PTP management messages to +ptp4l via pmc can be reused. + +This patch is a trivial movement of that code to a separate translation +module, outside of phc2sys. This makes it available to other programs +that want to subscribe to port state change events too, such as ts2phc. + +Signed-off-by: Vladimir Oltean +Reviewed-by: Jacob Keller + +[commit abc75482332752b630b023178ccdf636f5fe7de7 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 354 --------------------------------------------------- + pmc_common.c | 337 ++++++++++++++++++++++++++++++++++++++++++++++++ + pmc_common.h | 35 +++++ + 3 files changed, 372 insertions(+), 354 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index d5b8e71..9184db6 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -63,12 +63,6 @@ + #define NS_PER_SEC 1000000000LL + + #define PHC_PPS_OFFSET_LIMIT 10000000 +-#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) +-#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ +-/* Note that PMC_SUBSCRIBE_DURATION has to be longer than +- * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is +- * renewed. +- */ + + struct clock { + LIST_ENTRY(clock) list; +@@ -100,24 +94,6 @@ struct port { + struct clock *clock; + }; + +-struct pmc_node; +- +-typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, +- struct ptp_message *msg, +- int excluded); +- +-struct pmc_node { +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; +- int sync_offset; +- int leap; +- int utc_offset_traceable; +- int clock_identity_set; +- struct ClockIdentity clock_identity; +- pmc_node_recv_subscribed_t *recv_subscribed; +-}; +- + struct phc2sys_private { + unsigned int stats_max_count; + int sanity_freq_limit; +@@ -137,17 +113,11 @@ struct phc2sys_private { + + static struct config *phc2sys_config; + +-int update_pmc_node(struct pmc_node *node, int subscribe); + static int clock_handle_leap(struct phc2sys_private *priv, + struct clock *clock, + int64_t offset, uint64_t ts); +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); +-void run_pmc_events(struct pmc_node *node); + + static int normalize_state(int state); +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface); + + static struct servo *servo_add(struct phc2sys_private *priv, + struct clock *clock) +@@ -811,52 +781,6 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + return 0; + } + +-static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) +-{ +- if (!node->clock_identity_set) +- return 1; +- return cid_eq(&node->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); +-} +- +-static int is_msg_mgt(struct ptp_message *msg) +-{ +- struct TLV *tlv; +- +- if (msg_type(msg) != MANAGEMENT) +- return 0; +- if (management_action(msg) != RESPONSE) +- return 0; +- if (msg_tlv_count(msg) != 1) +- return 0; +- tlv = (struct TLV *) msg->management.suffix; +- if (tlv->type == TLV_MANAGEMENT) +- return 1; +- if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) +- return -1; +- return 0; +-} +- +-int get_mgt_id(struct ptp_message *msg) +-{ +- struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->id; +-} +- +-void *get_mgt_data(struct ptp_message *msg) +-{ +- struct management_tlv *mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->data; +-} +- +-static int get_mgt_err_id(struct ptp_message *msg) +-{ +- struct management_error_status *mgt; +- +- mgt = (struct management_error_status *)msg->management.suffix; +- return mgt->id; +-} +- + static int normalize_state(int state) + { + if (state != PS_MASTER && state != PS_SLAVE && +@@ -927,260 +851,6 @@ static int phc2sys_recv_subscribed(struct pmc_node *node, + return 0; + } + +-static void send_subscription(struct pmc_node *node) +-{ +- struct subscribe_events_np sen; +- +- memset(&sen, 0, sizeof(sen)); +- sen.duration = PMC_SUBSCRIBE_DURATION; +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; +- pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); +-} +- +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) +-{ +- node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, +- config_get_int(cfg, NULL, "domainNumber"), +- config_get_int(cfg, NULL, "transportSpecific") << 4, 1); +- if (!node->pmc) { +- pr_err("failed to create pmc"); +- return -1; +- } +- node->recv_subscribed = recv_subscribed; +- +- return 0; +-} +- +-/* Return values: +- * 1: success +- * 0: timeout +- * -1: error reported by the other side +- * -2: local error, fatal +- */ +-static int run_pmc(struct pmc_node *node, int timeout, int ds_id, +- struct ptp_message **msg) +-{ +-#define N_FD 1 +- struct pollfd pollfd[N_FD]; +- int cnt, res; +- +- while (1) { +- pollfd[0].fd = pmc_get_transport_fd(node->pmc); +- pollfd[0].events = POLLIN|POLLPRI; +- if (!node->pmc_ds_requested && ds_id >= 0) +- pollfd[0].events |= POLLOUT; +- +- cnt = poll(pollfd, N_FD, timeout); +- if (cnt < 0) { +- pr_err("poll failed"); +- return -2; +- } +- if (!cnt) { +- /* Request the data set again in the next run. */ +- node->pmc_ds_requested = 0; +- return 0; +- } +- +- /* Send a new request if there are no pending messages. */ +- if ((pollfd[0].revents & POLLOUT) && +- !(pollfd[0].revents & (POLLIN|POLLPRI))) { +- switch (ds_id) { +- case TLV_SUBSCRIBE_EVENTS_NP: +- send_subscription(node); +- break; +- default: +- pmc_send_get_action(node->pmc, ds_id); +- break; +- } +- node->pmc_ds_requested = 1; +- } +- +- if (!(pollfd[0].revents & (POLLIN|POLLPRI))) +- continue; +- +- *msg = pmc_recv(node->pmc); +- +- if (!*msg) +- continue; +- +- if (!check_clock_identity(node, *msg)) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- +- res = is_msg_mgt(*msg); +- if (res < 0 && get_mgt_err_id(*msg) == ds_id) { +- node->pmc_ds_requested = 0; +- return -1; +- } +- if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || +- get_mgt_id(*msg) != ds_id) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- node->pmc_ds_requested = 0; +- return 1; +- } +-} +- +-static int run_pmc_wait_sync(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- Enumeration8 portState; +- void *data; +- int res; +- +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- data = get_mgt_data(msg); +- portState = ((struct portDS *)data)->portState; +- msg_put(msg); +- +- switch (portState) { +- case PS_MASTER: +- case PS_SLAVE: +- return 1; +- } +- /* try to get more data sets (for other ports) */ +- node->pmc_ds_requested = 1; +- } +-} +- +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct timePropertiesDS *tds; +- +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- tds = (struct timePropertiesDS *)get_mgt_data(msg); +- if (tds->flags & PTP_TIMESCALE) { +- node->sync_offset = tds->currentUtcOffset; +- if (tds->flags & LEAP_61) +- node->leap = 1; +- else if (tds->flags & LEAP_59) +- node->leap = -1; +- else +- node->leap = 0; +- node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && +- tds->flags & TIME_TRACEABLE; +- } else { +- node->sync_offset = 0; +- node->leap = 0; +- node->utc_offset_traceable = 0; +- } +- msg_put(msg); +- return 1; +-} +- +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct defaultDS *dds; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- res = dds->numberPorts; +- msg_put(msg); +- return res; +-} +- +-int run_pmc_subscribe(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (res <= 0) +- return res; +- msg_put(msg); +- return 1; +-} +- +-void run_pmc_events(struct pmc_node *node) +-{ +- struct ptp_message *msg; +- +- run_pmc(node, 0, -1, &msg); +-} +- +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) +-{ +- struct ptp_message *msg; +- int res, len; +- struct port_properties_np *ppn; +- +- pmc_target_port(node->pmc, port); +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); +- if (res <= 0) +- goto out; +- +- ppn = get_mgt_data(msg); +- if (ppn->portIdentity.portNumber != port) { +- msg_put(msg); +- continue; +- } +- +- *state = ppn->port_state; +- *tstamping = ppn->timestamping; +- len = ppn->interface.length; +- if (len > IFNAMSIZ - 1) +- len = IFNAMSIZ - 1; +- memcpy(iface, ppn->interface.text, len); +- iface[len] = '\0'; +- +- msg_put(msg); +- res = 1; +- break; +- } +-out: +- pmc_target_all(node->pmc); +- return res; +-} +- +-static int run_pmc_clock_identity(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- struct defaultDS *dds; +- int res; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- memcpy(&node->clock_identity, &dds->clockIdentity, +- sizeof(struct ClockIdentity)); +- node->clock_identity_set = 1; +- msg_put(msg); +- return 1; +-} +- +-void close_pmc_node(struct pmc_node *node) +-{ +- if (!node->pmc) +- return; +- +- pmc_destroy(node->pmc); +- node->pmc = NULL; +-} +- + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + { + struct port *port; +@@ -1259,30 +929,6 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + return 0; + } + +-/* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_node *node, int subscribe) +-{ +- struct timespec tp; +- uint64_t ts; +- +- if (clock_gettime(CLOCK_MONOTONIC, &tp)) { +- pr_err("failed to read clock: %m"); +- return -1; +- } +- ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; +- +- if (node->pmc && +- !(ts > node->pmc_last_update && +- ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { +- if (subscribe) +- run_pmc_subscribe(node, 0); +- if (run_pmc_get_utc_offset(node, 0) > 0) +- node->pmc_last_update = ts; +- } +- +- return 0; +-} +- + /* Returns: non-zero to skip clock update */ + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) +diff --git a/pmc_common.c b/pmc_common.c +index f07f6f6..c9cdf18 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -18,6 +18,8 @@ + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #include ++#include ++#include + #include + #include + #include +@@ -56,6 +58,13 @@ + /* Includes one extra byte to make length even. */ + #define EMPTY_PTP_TEXT 2 + ++#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) ++#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ ++/* Note that PMC_SUBSCRIBE_DURATION has to be longer than ++ * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is ++ * renewed. ++ */ ++ + static void do_get_action(struct pmc *pmc, int action, int index, char *str); + static void do_set_action(struct pmc *pmc, int action, int index, char *str); + static void not_supported(struct pmc *pmc, int action, int index, char *str); +@@ -711,3 +720,331 @@ int pmc_do_command(struct pmc *pmc, char *str) + + return 0; + } ++ ++static void send_subscription(struct pmc_node *node) ++{ ++ struct subscribe_events_np sen; ++ ++ memset(&sen, 0, sizeof(sen)); ++ sen.duration = PMC_SUBSCRIBE_DURATION; ++ sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++} ++ ++static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) ++{ ++ if (!node->clock_identity_set) ++ return 1; ++ return cid_eq(&node->clock_identity, ++ &msg->header.sourcePortIdentity.clockIdentity); ++} ++ ++static int is_msg_mgt(struct ptp_message *msg) ++{ ++ struct TLV *tlv; ++ ++ if (msg_type(msg) != MANAGEMENT) ++ return 0; ++ if (management_action(msg) != RESPONSE) ++ return 0; ++ if (msg_tlv_count(msg) != 1) ++ return 0; ++ tlv = (struct TLV *) msg->management.suffix; ++ if (tlv->type == TLV_MANAGEMENT) ++ return 1; ++ if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) ++ return -1; ++ return 0; ++} ++ ++int get_mgt_id(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->id; ++} ++ ++void *get_mgt_data(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->data; ++} ++ ++static int get_mgt_err_id(struct ptp_message *msg) ++{ ++ struct management_error_status *mgt; ++ ++ mgt = (struct management_error_status *)msg->management.suffix; ++ return mgt->id; ++} ++ ++/* Return values: ++ * 1: success ++ * 0: timeout ++ * -1: error reported by the other side ++ * -2: local error, fatal ++ */ ++static int run_pmc(struct pmc_node *node, int timeout, int ds_id, ++ struct ptp_message **msg) ++{ ++#define N_FD 1 ++ struct pollfd pollfd[N_FD]; ++ int cnt, res; ++ ++ while (1) { ++ pollfd[0].fd = pmc_get_transport_fd(node->pmc); ++ pollfd[0].events = POLLIN|POLLPRI; ++ if (!node->pmc_ds_requested && ds_id >= 0) ++ pollfd[0].events |= POLLOUT; ++ ++ cnt = poll(pollfd, N_FD, timeout); ++ if (cnt < 0) { ++ pr_err("poll failed"); ++ return -2; ++ } ++ if (!cnt) { ++ /* Request the data set again in the next run. */ ++ node->pmc_ds_requested = 0; ++ return 0; ++ } ++ ++ /* Send a new request if there are no pending messages. */ ++ if ((pollfd[0].revents & POLLOUT) && ++ !(pollfd[0].revents & (POLLIN|POLLPRI))) { ++ switch (ds_id) { ++ case TLV_SUBSCRIBE_EVENTS_NP: ++ send_subscription(node); ++ break; ++ default: ++ pmc_send_get_action(node->pmc, ds_id); ++ break; ++ } ++ node->pmc_ds_requested = 1; ++ } ++ ++ if (!(pollfd[0].revents & (POLLIN|POLLPRI))) ++ continue; ++ ++ *msg = pmc_recv(node->pmc); ++ ++ if (!*msg) ++ continue; ++ ++ if (!check_clock_identity(node, *msg)) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ ++ res = is_msg_mgt(*msg); ++ if (res < 0 && get_mgt_err_id(*msg) == ds_id) { ++ node->pmc_ds_requested = 0; ++ return -1; ++ } ++ if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || ++ get_mgt_id(*msg) != ds_id) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ node->pmc_ds_requested = 0; ++ return 1; ++ } ++} ++ ++int run_pmc_wait_sync(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ Enumeration8 portState; ++ void *data; ++ int res; ++ ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ data = get_mgt_data(msg); ++ portState = ((struct portDS *)data)->portState; ++ msg_put(msg); ++ ++ switch (portState) { ++ case PS_MASTER: ++ case PS_SLAVE: ++ return 1; ++ } ++ /* try to get more data sets (for other ports) */ ++ node->pmc_ds_requested = 1; ++ } ++} ++ ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct timePropertiesDS *tds; ++ ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ tds = (struct timePropertiesDS *)get_mgt_data(msg); ++ if (tds->flags & PTP_TIMESCALE) { ++ node->sync_offset = tds->currentUtcOffset; ++ if (tds->flags & LEAP_61) ++ node->leap = 1; ++ else if (tds->flags & LEAP_59) ++ node->leap = -1; ++ else ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ tds->flags & TIME_TRACEABLE; ++ } else { ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; ++ } ++ msg_put(msg); ++ return 1; ++} ++ ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct defaultDS *dds; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ res = dds->numberPorts; ++ msg_put(msg); ++ return res; ++} ++ ++int run_pmc_subscribe(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (res <= 0) ++ return res; ++ msg_put(msg); ++ return 1; ++} ++ ++void run_pmc_events(struct pmc_node *node) ++{ ++ struct ptp_message *msg; ++ ++ run_pmc(node, 0, -1, &msg); ++} ++ ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) ++{ ++ struct ptp_message *msg; ++ int res, len; ++ struct port_properties_np *ppn; ++ ++ pmc_target_port(node->pmc, port); ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ if (res <= 0) ++ goto out; ++ ++ ppn = get_mgt_data(msg); ++ if (ppn->portIdentity.portNumber != port) { ++ msg_put(msg); ++ continue; ++ } ++ ++ *state = ppn->port_state; ++ *tstamping = ppn->timestamping; ++ len = ppn->interface.length; ++ if (len > IFNAMSIZ - 1) ++ len = IFNAMSIZ - 1; ++ memcpy(iface, ppn->interface.text, len); ++ iface[len] = '\0'; ++ ++ msg_put(msg); ++ res = 1; ++ break; ++ } ++out: ++ pmc_target_all(node->pmc); ++ return res; ++} ++ ++int run_pmc_clock_identity(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ struct defaultDS *dds; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ memcpy(&node->clock_identity, &dds->clockIdentity, ++ sizeof(struct ClockIdentity)); ++ node->clock_identity_set = 1; ++ msg_put(msg); ++ return 1; ++} ++ ++/* Returns: -1 in case of error, 0 otherwise */ ++int update_pmc_node(struct pmc_node *node, int subscribe) ++{ ++ struct timespec tp; ++ uint64_t ts; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &tp)) { ++ pr_err("failed to read clock: %m"); ++ return -1; ++ } ++ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; ++ ++ if (node->pmc && ++ !(ts > node->pmc_last_update && ++ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (subscribe) ++ run_pmc_subscribe(node, 0); ++ if (run_pmc_get_utc_offset(node, 0) > 0) ++ node->pmc_last_update = ts; ++ } ++ ++ return 0; ++} ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) ++{ ++ node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, ++ config_get_int(cfg, NULL, "domainNumber"), ++ config_get_int(cfg, NULL, "transportSpecific") << 4, 1); ++ if (!node->pmc) { ++ pr_err("failed to create pmc"); ++ return -1; ++ } ++ node->recv_subscribed = recv_subscribed; ++ ++ return 0; ++} ++ ++void close_pmc_node(struct pmc_node *node) ++{ ++ if (!node->pmc) ++ return; ++ ++ pmc_destroy(node->pmc); ++ node->pmc = NULL; ++} +diff --git a/pmc_common.h b/pmc_common.h +index 9fa72de..476ccea 100644 +--- a/pmc_common.h ++++ b/pmc_common.h +@@ -22,6 +22,7 @@ + #define HAVE_PMC_COMMON_H + + #include "config.h" ++#include "fsm.h" + #include "msg.h" + #include "transport.h" + +@@ -49,4 +50,38 @@ void pmc_target_all(struct pmc *pmc); + const char *pmc_action_string(int action); + int pmc_do_command(struct pmc *pmc, char *str); + ++struct pmc_node; ++ ++typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++ struct ptp_message *msg, ++ int excluded); ++ ++struct pmc_node { ++ struct pmc *pmc; ++ int pmc_ds_requested; ++ uint64_t pmc_last_update; ++ int sync_offset; ++ int leap; ++ int utc_offset_traceable; ++ int clock_identity_set; ++ struct ClockIdentity clock_identity; ++ pmc_node_recv_subscribed_t *recv_subscribed; ++}; ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed); ++void close_pmc_node(struct pmc_node *node); ++int update_pmc_node(struct pmc_node *node, int subscribe); ++int run_pmc_subscribe(struct pmc_node *node, int timeout); ++int run_pmc_clock_identity(struct pmc_node *node, int timeout); ++int run_pmc_wait_sync(struct pmc_node *node, int timeout); ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout); ++void run_pmc_events(struct pmc_node *node); ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++int get_mgt_id(struct ptp_message *msg); ++void *get_mgt_data(struct ptp_message *msg); ++ + #endif +-- +2.25.1 + +From c00e75286b2ad882cf8e89549ea58e438c877f95 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:40:59 -0300 +Subject: [PATCH 14/47] Introduce the PMC agent module. + +The logic for placing PTP management queries migrated out of phc2sys into +pmc_common in order to be shared with other programs in the future. This +logic uses pmc_common rather than extending it, and so it should live in +its own module stacked on top of pmc_common. + +This patch moves the code into its own file verbatim without making any +other changes. + +Signed-off-by: Richard Cochran + +[commit f266740e1a8aacc03f97205ae14fc43c59081433 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + makefile | 6 +- + phc2sys.c | 2 +- + pmc_agent.c | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++ + pmc_agent.h | 62 +++++++++ + pmc_common.c | 338 ----------------------------------------------- + pmc_common.h | 34 ----- + 6 files changed, 427 insertions(+), 376 deletions(-) + create mode 100644 pmc_agent.c + create mode 100644 pmc_agent.h + +diff --git a/makefile b/makefile +index 27c4d78..33e7ca0 100644 +--- a/makefile ++++ b/makefile +@@ -34,8 +34,8 @@ OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \ + 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 + +-OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_common.o \ +- sysoff.o timemaster.o $(TS2PHC) ++OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_agent.o \ ++ pmc_common.o sysoff.o timemaster.o $(TS2PHC) + SRC = $(OBJECTS:.o=.c) + DEPEND = $(OBJECTS:.o=.d) + srcdir := $(dir $(lastword $(MAKEFILE_LIST))) +@@ -59,7 +59,7 @@ pmc: config.o hash.o interface.o msg.o phc.o pmc.o pmc_common.o print.o sk.o \ + tlv.o $(TRANSP) util.o version.o + + phc2sys: clockadj.o clockcheck.o config.o hash.o interface.o msg.o \ +- phc.o phc2sys.o pmc_common.o print.o $(SERVOS) sk.o stats.o \ ++ phc.o phc2sys.o pmc_agent.o pmc_common.o print.o $(SERVOS) sk.o stats.o \ + sysoff.o tlv.o $(TRANSP) util.o version.o + + hwstamp_ctl: hwstamp_ctl.o version.o +diff --git a/phc2sys.c b/phc2sys.c +index 9184db6..648ba61 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -47,7 +47,7 @@ + #include "ntpshm.h" + #include "phc.h" + #include "pi.h" +-#include "pmc_common.h" ++#include "pmc_agent.h" + #include "print.h" + #include "servo.h" + #include "sk.h" +diff --git a/pmc_agent.c b/pmc_agent.c +new file mode 100644 +index 0000000..774e94d +--- /dev/null ++++ b/pmc_agent.c +@@ -0,0 +1,361 @@ ++/** ++ * @file pmc_agent.c ++ * @note Copyright (C) 2012 Richard Cochran ++ * @note Copyright (C) 2013 Miroslav Lichvar ++ * ++ * 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 ++#include ++ ++#include "notification.h" ++#include "pmc_agent.h" ++#include "print.h" ++#include "util.h" ++ ++#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) ++#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ ++/* Note that PMC_SUBSCRIBE_DURATION has to be longer than ++ * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is ++ * renewed. ++ */ ++ ++static void send_subscription(struct pmc_node *node) ++{ ++ struct subscribe_events_np sen; ++ ++ memset(&sen, 0, sizeof(sen)); ++ sen.duration = PMC_SUBSCRIBE_DURATION; ++ sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++} ++ ++static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) ++{ ++ if (!node->clock_identity_set) ++ return 1; ++ return cid_eq(&node->clock_identity, ++ &msg->header.sourcePortIdentity.clockIdentity); ++} ++ ++static int is_msg_mgt(struct ptp_message *msg) ++{ ++ struct TLV *tlv; ++ ++ if (msg_type(msg) != MANAGEMENT) ++ return 0; ++ if (management_action(msg) != RESPONSE) ++ return 0; ++ if (msg_tlv_count(msg) != 1) ++ return 0; ++ tlv = (struct TLV *) msg->management.suffix; ++ if (tlv->type == TLV_MANAGEMENT) ++ return 1; ++ if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) ++ return -1; ++ return 0; ++} ++ ++int get_mgt_id(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->id; ++} ++ ++void *get_mgt_data(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->data; ++} ++ ++static int get_mgt_err_id(struct ptp_message *msg) ++{ ++ struct management_error_status *mgt; ++ ++ mgt = (struct management_error_status *)msg->management.suffix; ++ return mgt->id; ++} ++ ++/* Return values: ++ * 1: success ++ * 0: timeout ++ * -1: error reported by the other side ++ * -2: local error, fatal ++ */ ++static int run_pmc(struct pmc_node *node, int timeout, int ds_id, ++ struct ptp_message **msg) ++{ ++#define N_FD 1 ++ struct pollfd pollfd[N_FD]; ++ int cnt, res; ++ ++ while (1) { ++ pollfd[0].fd = pmc_get_transport_fd(node->pmc); ++ pollfd[0].events = POLLIN|POLLPRI; ++ if (!node->pmc_ds_requested && ds_id >= 0) ++ pollfd[0].events |= POLLOUT; ++ ++ cnt = poll(pollfd, N_FD, timeout); ++ if (cnt < 0) { ++ pr_err("poll failed"); ++ return -2; ++ } ++ if (!cnt) { ++ /* Request the data set again in the next run. */ ++ node->pmc_ds_requested = 0; ++ return 0; ++ } ++ ++ /* Send a new request if there are no pending messages. */ ++ if ((pollfd[0].revents & POLLOUT) && ++ !(pollfd[0].revents & (POLLIN|POLLPRI))) { ++ switch (ds_id) { ++ case TLV_SUBSCRIBE_EVENTS_NP: ++ send_subscription(node); ++ break; ++ default: ++ pmc_send_get_action(node->pmc, ds_id); ++ break; ++ } ++ node->pmc_ds_requested = 1; ++ } ++ ++ if (!(pollfd[0].revents & (POLLIN|POLLPRI))) ++ continue; ++ ++ *msg = pmc_recv(node->pmc); ++ ++ if (!*msg) ++ continue; ++ ++ if (!check_clock_identity(node, *msg)) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ ++ res = is_msg_mgt(*msg); ++ if (res < 0 && get_mgt_err_id(*msg) == ds_id) { ++ node->pmc_ds_requested = 0; ++ return -1; ++ } ++ if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || ++ get_mgt_id(*msg) != ds_id) { ++ msg_put(*msg); ++ *msg = NULL; ++ continue; ++ } ++ node->pmc_ds_requested = 0; ++ return 1; ++ } ++} ++ ++int run_pmc_wait_sync(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ Enumeration8 portState; ++ void *data; ++ int res; ++ ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ data = get_mgt_data(msg); ++ portState = ((struct portDS *)data)->portState; ++ msg_put(msg); ++ ++ switch (portState) { ++ case PS_MASTER: ++ case PS_SLAVE: ++ return 1; ++ } ++ /* try to get more data sets (for other ports) */ ++ node->pmc_ds_requested = 1; ++ } ++} ++ ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct timePropertiesDS *tds; ++ ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ tds = (struct timePropertiesDS *)get_mgt_data(msg); ++ if (tds->flags & PTP_TIMESCALE) { ++ node->sync_offset = tds->currentUtcOffset; ++ if (tds->flags & LEAP_61) ++ node->leap = 1; ++ else if (tds->flags & LEAP_59) ++ node->leap = -1; ++ else ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ tds->flags & TIME_TRACEABLE; ++ } else { ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; ++ } ++ msg_put(msg); ++ return 1; ++} ++ ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ struct defaultDS *dds; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ res = dds->numberPorts; ++ msg_put(msg); ++ return res; ++} ++ ++int run_pmc_subscribe(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (res <= 0) ++ return res; ++ msg_put(msg); ++ return 1; ++} ++ ++void run_pmc_events(struct pmc_node *node) ++{ ++ struct ptp_message *msg; ++ ++ run_pmc(node, 0, -1, &msg); ++} ++ ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) ++{ ++ struct ptp_message *msg; ++ int res, len; ++ struct port_properties_np *ppn; ++ ++ pmc_target_port(node->pmc, port); ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ if (res <= 0) ++ goto out; ++ ++ ppn = get_mgt_data(msg); ++ if (ppn->portIdentity.portNumber != port) { ++ msg_put(msg); ++ continue; ++ } ++ ++ *state = ppn->port_state; ++ *tstamping = ppn->timestamping; ++ len = ppn->interface.length; ++ if (len > IFNAMSIZ - 1) ++ len = IFNAMSIZ - 1; ++ memcpy(iface, ppn->interface.text, len); ++ iface[len] = '\0'; ++ ++ msg_put(msg); ++ res = 1; ++ break; ++ } ++out: ++ pmc_target_all(node->pmc); ++ return res; ++} ++ ++int run_pmc_clock_identity(struct pmc_node *node, int timeout) ++{ ++ struct ptp_message *msg; ++ struct defaultDS *dds; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (res <= 0) ++ return res; ++ ++ dds = (struct defaultDS *)get_mgt_data(msg); ++ memcpy(&node->clock_identity, &dds->clockIdentity, ++ sizeof(struct ClockIdentity)); ++ node->clock_identity_set = 1; ++ msg_put(msg); ++ return 1; ++} ++ ++/* Returns: -1 in case of error, 0 otherwise */ ++int update_pmc_node(struct pmc_node *node, int subscribe) ++{ ++ struct timespec tp; ++ uint64_t ts; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &tp)) { ++ pr_err("failed to read clock: %m"); ++ return -1; ++ } ++ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; ++ ++ if (node->pmc && ++ !(ts > node->pmc_last_update && ++ ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (subscribe) ++ run_pmc_subscribe(node, 0); ++ if (run_pmc_get_utc_offset(node, 0) > 0) ++ node->pmc_last_update = ts; ++ } ++ ++ return 0; ++} ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed) ++{ ++ node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, ++ config_get_int(cfg, NULL, "domainNumber"), ++ config_get_int(cfg, NULL, "transportSpecific") << 4, 1); ++ if (!node->pmc) { ++ pr_err("failed to create pmc"); ++ return -1; ++ } ++ node->recv_subscribed = recv_subscribed; ++ ++ return 0; ++} ++ ++void close_pmc_node(struct pmc_node *node) ++{ ++ if (!node->pmc) ++ return; ++ ++ pmc_destroy(node->pmc); ++ node->pmc = NULL; ++} +diff --git a/pmc_agent.h b/pmc_agent.h +new file mode 100644 +index 0000000..90245b1 +--- /dev/null ++++ b/pmc_agent.h +@@ -0,0 +1,62 @@ ++/** ++ * @file pmc_agent.h ++ * @brief Client code for making PTP management requests. ++ * @note Copyright (C) 2013 Miroslav Lichvar ++ * @note Copyright (C) 2020 Richard Cochran ++ * ++ * 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_PMC_AGENT_H ++#define HAVE_PMC_AGENT_H ++ ++#include "pmc_common.h" ++ ++struct pmc_node; ++ ++typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++ struct ptp_message *msg, ++ int excluded); ++ ++struct pmc_node { ++ struct pmc *pmc; ++ int pmc_ds_requested; ++ uint64_t pmc_last_update; ++ int sync_offset; ++ int leap; ++ int utc_offset_traceable; ++ int clock_identity_set; ++ struct ClockIdentity clock_identity; ++ pmc_node_recv_subscribed_t *recv_subscribed; ++}; ++ ++int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++ pmc_node_recv_subscribed_t *recv_subscribed); ++void close_pmc_node(struct pmc_node *node); ++int update_pmc_node(struct pmc_node *node, int subscribe); ++int run_pmc_subscribe(struct pmc_node *node, int timeout); ++int run_pmc_clock_identity(struct pmc_node *node, int timeout); ++int run_pmc_wait_sync(struct pmc_node *node, int timeout); ++int run_pmc_get_number_ports(struct pmc_node *node, int timeout); ++void run_pmc_events(struct pmc_node *node); ++int run_pmc_port_properties(struct pmc_node *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); ++int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++int get_mgt_id(struct ptp_message *msg); ++void *get_mgt_data(struct ptp_message *msg); ++ ++#endif ++ +diff --git a/pmc_common.c b/pmc_common.c +index c9cdf18..a117904 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -18,8 +18,6 @@ + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + #include +-#include +-#include + #include + #include + #include +@@ -29,7 +27,6 @@ + #include "print.h" + #include "tlv.h" + #include "transport.h" +-#include "util.h" + #include "pmc_common.h" + + #define BAD_ACTION -1 +@@ -58,13 +55,6 @@ + /* Includes one extra byte to make length even. */ + #define EMPTY_PTP_TEXT 2 + +-#define PMC_UPDATE_INTERVAL (60 * NS_PER_SEC) +-#define PMC_SUBSCRIBE_DURATION 180 /* 3 minutes */ +-/* Note that PMC_SUBSCRIBE_DURATION has to be longer than +- * PMC_UPDATE_INTERVAL otherwise subscription will time out before it is +- * renewed. +- */ +- + static void do_get_action(struct pmc *pmc, int action, int index, char *str); + static void do_set_action(struct pmc *pmc, int action, int index, char *str); + static void not_supported(struct pmc *pmc, int action, int index, char *str); +@@ -720,331 +710,3 @@ int pmc_do_command(struct pmc *pmc, char *str) + + return 0; + } +- +-static void send_subscription(struct pmc_node *node) +-{ +- struct subscribe_events_np sen; +- +- memset(&sen, 0, sizeof(sen)); +- sen.duration = PMC_SUBSCRIBE_DURATION; +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; +- pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); +-} +- +-static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) +-{ +- if (!node->clock_identity_set) +- return 1; +- return cid_eq(&node->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); +-} +- +-static int is_msg_mgt(struct ptp_message *msg) +-{ +- struct TLV *tlv; +- +- if (msg_type(msg) != MANAGEMENT) +- return 0; +- if (management_action(msg) != RESPONSE) +- return 0; +- if (msg_tlv_count(msg) != 1) +- return 0; +- tlv = (struct TLV *) msg->management.suffix; +- if (tlv->type == TLV_MANAGEMENT) +- return 1; +- if (tlv->type == TLV_MANAGEMENT_ERROR_STATUS) +- return -1; +- return 0; +-} +- +-int get_mgt_id(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->id; +-} +- +-void *get_mgt_data(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->data; +-} +- +-static int get_mgt_err_id(struct ptp_message *msg) +-{ +- struct management_error_status *mgt; +- +- mgt = (struct management_error_status *)msg->management.suffix; +- return mgt->id; +-} +- +-/* Return values: +- * 1: success +- * 0: timeout +- * -1: error reported by the other side +- * -2: local error, fatal +- */ +-static int run_pmc(struct pmc_node *node, int timeout, int ds_id, +- struct ptp_message **msg) +-{ +-#define N_FD 1 +- struct pollfd pollfd[N_FD]; +- int cnt, res; +- +- while (1) { +- pollfd[0].fd = pmc_get_transport_fd(node->pmc); +- pollfd[0].events = POLLIN|POLLPRI; +- if (!node->pmc_ds_requested && ds_id >= 0) +- pollfd[0].events |= POLLOUT; +- +- cnt = poll(pollfd, N_FD, timeout); +- if (cnt < 0) { +- pr_err("poll failed"); +- return -2; +- } +- if (!cnt) { +- /* Request the data set again in the next run. */ +- node->pmc_ds_requested = 0; +- return 0; +- } +- +- /* Send a new request if there are no pending messages. */ +- if ((pollfd[0].revents & POLLOUT) && +- !(pollfd[0].revents & (POLLIN|POLLPRI))) { +- switch (ds_id) { +- case TLV_SUBSCRIBE_EVENTS_NP: +- send_subscription(node); +- break; +- default: +- pmc_send_get_action(node->pmc, ds_id); +- break; +- } +- node->pmc_ds_requested = 1; +- } +- +- if (!(pollfd[0].revents & (POLLIN|POLLPRI))) +- continue; +- +- *msg = pmc_recv(node->pmc); +- +- if (!*msg) +- continue; +- +- if (!check_clock_identity(node, *msg)) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- +- res = is_msg_mgt(*msg); +- if (res < 0 && get_mgt_err_id(*msg) == ds_id) { +- node->pmc_ds_requested = 0; +- return -1; +- } +- if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || +- get_mgt_id(*msg) != ds_id) { +- msg_put(*msg); +- *msg = NULL; +- continue; +- } +- node->pmc_ds_requested = 0; +- return 1; +- } +-} +- +-int run_pmc_wait_sync(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- Enumeration8 portState; +- void *data; +- int res; +- +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- data = get_mgt_data(msg); +- portState = ((struct portDS *)data)->portState; +- msg_put(msg); +- +- switch (portState) { +- case PS_MASTER: +- case PS_SLAVE: +- return 1; +- } +- /* try to get more data sets (for other ports) */ +- node->pmc_ds_requested = 1; +- } +-} +- +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct timePropertiesDS *tds; +- +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- tds = (struct timePropertiesDS *)get_mgt_data(msg); +- if (tds->flags & PTP_TIMESCALE) { +- node->sync_offset = tds->currentUtcOffset; +- if (tds->flags & LEAP_61) +- node->leap = 1; +- else if (tds->flags & LEAP_59) +- node->leap = -1; +- else +- node->leap = 0; +- node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && +- tds->flags & TIME_TRACEABLE; +- } else { +- node->sync_offset = 0; +- node->leap = 0; +- node->utc_offset_traceable = 0; +- } +- msg_put(msg); +- return 1; +-} +- +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct defaultDS *dds; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- res = dds->numberPorts; +- msg_put(msg); +- return res; +-} +- +-int run_pmc_subscribe(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (res <= 0) +- return res; +- msg_put(msg); +- return 1; +-} +- +-void run_pmc_events(struct pmc_node *node) +-{ +- struct ptp_message *msg; +- +- run_pmc(node, 0, -1, &msg); +-} +- +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) +-{ +- struct ptp_message *msg; +- int res, len; +- struct port_properties_np *ppn; +- +- pmc_target_port(node->pmc, port); +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); +- if (res <= 0) +- goto out; +- +- ppn = get_mgt_data(msg); +- if (ppn->portIdentity.portNumber != port) { +- msg_put(msg); +- continue; +- } +- +- *state = ppn->port_state; +- *tstamping = ppn->timestamping; +- len = ppn->interface.length; +- if (len > IFNAMSIZ - 1) +- len = IFNAMSIZ - 1; +- memcpy(iface, ppn->interface.text, len); +- iface[len] = '\0'; +- +- msg_put(msg); +- res = 1; +- break; +- } +-out: +- pmc_target_all(node->pmc); +- return res; +-} +- +-int run_pmc_clock_identity(struct pmc_node *node, int timeout) +-{ +- struct ptp_message *msg; +- struct defaultDS *dds; +- int res; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *)get_mgt_data(msg); +- memcpy(&node->clock_identity, &dds->clockIdentity, +- sizeof(struct ClockIdentity)); +- node->clock_identity_set = 1; +- msg_put(msg); +- return 1; +-} +- +-/* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_node *node, int subscribe) +-{ +- struct timespec tp; +- uint64_t ts; +- +- if (clock_gettime(CLOCK_MONOTONIC, &tp)) { +- pr_err("failed to read clock: %m"); +- return -1; +- } +- ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; +- +- if (node->pmc && +- !(ts > node->pmc_last_update && +- ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { +- if (subscribe) +- run_pmc_subscribe(node, 0); +- if (run_pmc_get_utc_offset(node, 0) > 0) +- node->pmc_last_update = ts; +- } +- +- return 0; +-} +- +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) +-{ +- node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, +- config_get_int(cfg, NULL, "domainNumber"), +- config_get_int(cfg, NULL, "transportSpecific") << 4, 1); +- if (!node->pmc) { +- pr_err("failed to create pmc"); +- return -1; +- } +- node->recv_subscribed = recv_subscribed; +- +- return 0; +-} +- +-void close_pmc_node(struct pmc_node *node) +-{ +- if (!node->pmc) +- return; +- +- pmc_destroy(node->pmc); +- node->pmc = NULL; +-} +diff --git a/pmc_common.h b/pmc_common.h +index 476ccea..8bea2e0 100644 +--- a/pmc_common.h ++++ b/pmc_common.h +@@ -50,38 +50,4 @@ void pmc_target_all(struct pmc *pmc); + const char *pmc_action_string(int action); + int pmc_do_command(struct pmc *pmc, char *str); + +-struct pmc_node; +- +-typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, +- struct ptp_message *msg, +- int excluded); +- +-struct pmc_node { +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; +- int sync_offset; +- int leap; +- int utc_offset_traceable; +- int clock_identity_set; +- struct ClockIdentity clock_identity; +- pmc_node_recv_subscribed_t *recv_subscribed; +-}; +- +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed); +-void close_pmc_node(struct pmc_node *node); +-int update_pmc_node(struct pmc_node *node, int subscribe); +-int run_pmc_subscribe(struct pmc_node *node, int timeout); +-int run_pmc_clock_identity(struct pmc_node *node, int timeout); +-int run_pmc_wait_sync(struct pmc_node *node, int timeout); +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout); +-void run_pmc_events(struct pmc_node *node); +-int run_pmc_port_properties(struct pmc_node *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface); +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); +-int get_mgt_id(struct ptp_message *msg); +-void *get_mgt_data(struct ptp_message *msg); +- + #endif +-- +2.25.1 + +From 82258917b8de7110545f3d4f99d3ac88a609f019 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:47:36 -0300 +Subject: [PATCH 15/47] pmc_agent: Rename pmc_node to something more + descriptive. + +Signed-off-by: Richard Cochran + +[commit bb6865cdf59572fcb09c11d549828269281c6841 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 4 ++-- + pmc_agent.c | 26 +++++++++++++------------- + pmc_agent.h | 26 +++++++++++++------------- + 3 files changed, 28 insertions(+), 28 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 648ba61..74ee9d1 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -103,7 +103,7 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; +- struct pmc_node node; ++ struct pmc_agent node; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -813,7 +813,7 @@ static int clock_compute_state(struct phc2sys_private *priv, + #define node_to_phc2sys(node) \ + container_of(node, struct phc2sys_private, node) + +-static int phc2sys_recv_subscribed(struct pmc_node *node, ++static int phc2sys_recv_subscribed(struct pmc_agent *node, + struct ptp_message *msg, int excluded) + { + struct phc2sys_private *priv = node_to_phc2sys(node); +diff --git a/pmc_agent.c b/pmc_agent.c +index 774e94d..e83895c 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -32,7 +32,7 @@ + * renewed. + */ + +-static void send_subscription(struct pmc_node *node) ++static void send_subscription(struct pmc_agent *node) + { + struct subscribe_events_np sen; + +@@ -42,7 +42,7 @@ static void send_subscription(struct pmc_node *node) + pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +-static int check_clock_identity(struct pmc_node *node, struct ptp_message *msg) ++static int check_clock_identity(struct pmc_agent *node, struct ptp_message *msg) + { + if (!node->clock_identity_set) + return 1; +@@ -98,7 +98,7 @@ static int get_mgt_err_id(struct ptp_message *msg) + * -1: error reported by the other side + * -2: local error, fatal + */ +-static int run_pmc(struct pmc_node *node, int timeout, int ds_id, ++static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + struct ptp_message **msg) + { + #define N_FD 1 +@@ -166,7 +166,7 @@ static int run_pmc(struct pmc_node *node, int timeout, int ds_id, + } + } + +-int run_pmc_wait_sync(struct pmc_node *node, int timeout) ++int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + Enumeration8 portState; +@@ -192,7 +192,7 @@ int run_pmc_wait_sync(struct pmc_node *node, int timeout) + } + } + +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) ++int run_pmc_get_utc_offset(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -222,7 +222,7 @@ int run_pmc_get_utc_offset(struct pmc_node *node, int timeout) + return 1; + } + +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout) ++int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -238,7 +238,7 @@ int run_pmc_get_number_ports(struct pmc_node *node, int timeout) + return res; + } + +-int run_pmc_subscribe(struct pmc_node *node, int timeout) ++int run_pmc_subscribe(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + int res; +@@ -250,14 +250,14 @@ int run_pmc_subscribe(struct pmc_node *node, int timeout) + return 1; + } + +-void run_pmc_events(struct pmc_node *node) ++void run_pmc_events(struct pmc_agent *node) + { + struct ptp_message *msg; + + run_pmc(node, 0, -1, &msg); + } + +-int run_pmc_port_properties(struct pmc_node *node, int timeout, ++int run_pmc_port_properties(struct pmc_agent *node, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface) + { +@@ -294,7 +294,7 @@ out: + return res; + } + +-int run_pmc_clock_identity(struct pmc_node *node, int timeout) ++int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; + struct defaultDS *dds; +@@ -313,7 +313,7 @@ int run_pmc_clock_identity(struct pmc_node *node, int timeout) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_node *node, int subscribe) ++int update_pmc_node(struct pmc_agent *node, int subscribe) + { + struct timespec tp; + uint64_t ts; +@@ -336,7 +336,7 @@ int update_pmc_node(struct pmc_node *node, int subscribe) + return 0; + } + +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed) + { + node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, +@@ -351,7 +351,7 @@ int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, + return 0; + } + +-void close_pmc_node(struct pmc_node *node) ++void close_pmc_node(struct pmc_agent *node) + { + if (!node->pmc) + return; +diff --git a/pmc_agent.h b/pmc_agent.h +index 90245b1..10ef4b5 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -24,13 +24,13 @@ + + #include "pmc_common.h" + +-struct pmc_node; ++struct pmc_agent; + +-typedef int pmc_node_recv_subscribed_t(struct pmc_node *node, ++typedef int pmc_node_recv_subscribed_t(struct pmc_agent *agent, + struct ptp_message *msg, + int excluded); + +-struct pmc_node { ++struct pmc_agent { + struct pmc *pmc; + int pmc_ds_requested; + uint64_t pmc_last_update; +@@ -42,19 +42,19 @@ struct pmc_node { + pmc_node_recv_subscribed_t *recv_subscribed; + }; + +-int init_pmc_node(struct config *cfg, struct pmc_node *node, const char *uds, ++int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed); +-void close_pmc_node(struct pmc_node *node); +-int update_pmc_node(struct pmc_node *node, int subscribe); +-int run_pmc_subscribe(struct pmc_node *node, int timeout); +-int run_pmc_clock_identity(struct pmc_node *node, int timeout); +-int run_pmc_wait_sync(struct pmc_node *node, int timeout); +-int run_pmc_get_number_ports(struct pmc_node *node, int timeout); +-void run_pmc_events(struct pmc_node *node); +-int run_pmc_port_properties(struct pmc_node *node, int timeout, ++void close_pmc_node(struct pmc_agent *agent); ++int update_pmc_node(struct pmc_agent *agent, int subscribe); ++int run_pmc_subscribe(struct pmc_agent *agent, int timeout); ++int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); ++int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); ++int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); ++void run_pmc_events(struct pmc_agent *agent); ++int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); +-int run_pmc_get_utc_offset(struct pmc_node *node, int timeout); ++int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); + int get_mgt_id(struct ptp_message *msg); + void *get_mgt_data(struct ptp_message *msg); + +-- +2.25.1 + +From f6d7bb0a62f15fcca0343c42891f7e056f502949 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:55:29 -0300 +Subject: [PATCH 16/47] pmc_agent: Hide the implementation. + +The PMC agent's implementation should not be exposed to its users. This +patch hides the details and provides a method to create an instance. In +addition, the signature of the receive callback is made generic, removing +the container_of pattern meant for sub-classing modules. + +Signed-off-by: Richard Cochran + +[commit 826698791769e0ba4431fe98f02d4d09c109542e upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 76 +++++++++++++++++++++++++++++------------------------ + pmc_agent.c | 58 +++++++++++++++++++++++++++++++++++----- + pmc_agent.h | 62 +++++++++++++++++++++++++++++++------------ + 3 files changed, 138 insertions(+), 58 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 74ee9d1..037b1b9 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -103,7 +103,7 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; +- struct pmc_agent node; ++ struct pmc_agent *node; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -306,7 +306,7 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + + LIST_FOREACH(p, &priv->ports, list) { + if (p->clock == clock) { +- ret = run_pmc_port_properties(&priv->node, 1000, p->number, ++ ret = run_pmc_port_properties(priv->node, 1000, p->number, + &state, ×tamping, + iface); + if (ret > 0) +@@ -641,7 +641,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + + if (src == CLOCK_INVALID) { + /* The sync offset can't be applied with PPS alone. */ +- priv->node.sync_offset = 0; ++ pmc_agent_set_sync_offset(priv->node, 0); + } else { + enable_pps_output(priv->master->clkid); + } +@@ -672,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc_node(&priv->node, 0) < 0) ++ if (update_pmc_node(priv->node, 0) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -710,15 +710,15 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); +- if (update_pmc_node(&priv->node, subscriptions) < 0) ++ if (update_pmc_node(priv->node, subscriptions) < 0) + continue; + + if (subscriptions) { +- run_pmc_events(&priv->node); ++ run_pmc_events(priv->node); + if (priv->state_changed) { + /* force getting offset, as it may have + * changed after the port state change */ +- if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + continue; + } +@@ -810,13 +810,10 @@ static int clock_compute_state(struct phc2sys_private *priv, + return state; + } + +-#define node_to_phc2sys(node) \ +- container_of(node, struct phc2sys_private, node) +- +-static int phc2sys_recv_subscribed(struct pmc_agent *node, +- struct ptp_message *msg, int excluded) ++static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, ++ int excluded) + { +- struct phc2sys_private *priv = node_to_phc2sys(node); ++ struct phc2sys_private *priv = (struct phc2sys_private *) context; + int mgt_id, state; + struct portDS *pds; + struct port *port; +@@ -863,7 +860,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + while (1) { + if (!is_running()) + return -1; +- res = run_pmc_clock_identity(&priv->node, 1000); ++ res = run_pmc_clock_identity(priv->node, 1000); + if (res < 0) + return -1; + if (res > 0) +@@ -872,20 +869,20 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + pr_notice("Waiting for ptp4l..."); + } + +- number_ports = run_pmc_get_number_ports(&priv->node, 1000); ++ number_ports = run_pmc_get_number_ports(priv->node, 1000); + if (number_ports <= 0) { + pr_err("failed to get number of ports"); + return -1; + } + +- res = run_pmc_subscribe(&priv->node, 1000); ++ res = run_pmc_subscribe(priv->node, 1000); + if (res <= 0) { + pr_err("failed to subscribe"); + return -1; + } + + for (i = 1; i <= number_ports; i++) { +- res = run_pmc_port_properties(&priv->node, 1000, i, &state, ++ res = run_pmc_port_properties(priv->node, 1000, i, &state, + ×tamping, iface); + if (res == -1) { + /* port does not exist, ignore the port */ +@@ -922,7 +919,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (run_pmc_get_utc_offset(&priv->node, 1000) <= 0) { ++ if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { + pr_err("failed to get UTC offset"); + return -1; + } +@@ -933,9 +930,9 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) + { +- int clock_leap, node_leap = priv->node.leap; ++ int clock_leap, node_leap = pmc_agent_get_leap(priv->node); + +- clock->sync_offset = priv->node.sync_offset; ++ clock->sync_offset = pmc_agent_get_sync_offset(priv->node); + + if ((node_leap || clock->leap_set) && + clock->is_utc != priv->master->is_utc) { +@@ -976,7 +973,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + } + } + +- if (priv->node.utc_offset_traceable && ++ if (pmc_agent_utc_offset_traceable(priv->node) && + clock->utc_offset_set != clock->sync_offset) { + if (clock->clkid == CLOCK_REALTIME) + sysclk_set_tai_offset(clock->sync_offset); +@@ -1032,11 +1029,13 @@ int main(int argc, char *argv[]) + { + char *config = NULL, *dst_name = NULL, *progname, *src_name = NULL; + char uds_local[MAX_IFNAME_SIZE + 1]; ++ int autocfg = 0, c, domain_number = 0, index, ntpshm_segment, offset; ++ int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0; ++ int wait_sync = 0; + struct clock *src, *dst; + struct config *cfg; + struct option *opts; +- int autocfg = 0, c, domain_number = 0, default_sync = 1, index, ntpshm_segment; +- int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0, wait_sync = 0; ++ int default_sync = 1; + double phc_rate, tmp; + struct phc2sys_private priv = { + .phc_readings = 5, +@@ -1049,6 +1048,10 @@ int main(int argc, char *argv[]) + if (!cfg) { + return -1; + } ++ priv.node = pmc_agent_create(); ++ if (!priv.node) { ++ return -1; ++ } + + opts = config_long_options(cfg); + +@@ -1140,9 +1143,10 @@ int main(int argc, char *argv[]) + goto end; + break; + case 'O': +- if (get_arg_val_i(c, optarg, &priv.node.sync_offset, +- INT_MIN, INT_MAX)) ++ if (get_arg_val_i(c, optarg, &offset, INT_MIN, INT_MAX)) { + goto end; ++ } ++ pmc_agent_set_sync_offset(priv.node, offset); + priv.forced_sync_offset = -1; + break; + case 'L': +@@ -1271,8 +1275,8 @@ int main(int argc, char *argv[]) + getpid()); + + if (autocfg) { +- if (init_pmc_node(cfg, &priv.node, uds_local, +- phc2sys_recv_subscribed)) ++ if (init_pmc_node(cfg, priv.node, uds_local, ++ phc2sys_recv_subscribed, &priv)) + goto end; + if (auto_init_ports(&priv, rt) < 0) + goto end; +@@ -1309,12 +1313,12 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- if (init_pmc_node(cfg, &priv.node, uds_local, +- phc2sys_recv_subscribed)) ++ if (init_pmc_node(cfg, priv.node, uds_local, ++ phc2sys_recv_subscribed, &priv)) + goto end; + + while (is_running()) { +- r = run_pmc_wait_sync(&priv.node, 1000); ++ r = run_pmc_wait_sync(priv.node, 1000); + if (r < 0) + goto end; + if (r > 0) +@@ -1324,7 +1328,7 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = run_pmc_get_utc_offset(&priv.node, 1000); ++ r = run_pmc_get_utc_offset(priv.node, 1000); + if (r <= 0) { + pr_err("failed to get UTC offset"); + goto end; +@@ -1333,8 +1337,10 @@ int main(int argc, char *argv[]) + + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || +- src->clkid == CLOCK_INVALID) +- close_pmc_node(&priv.node); ++ src->clkid == CLOCK_INVALID) { ++ pmc_agent_destroy(priv.node); ++ priv.node = NULL; ++ } + } + + if (pps_fd >= 0) { +@@ -1347,7 +1353,9 @@ int main(int argc, char *argv[]) + } + + end: +- close_pmc_node(&priv.node); ++ if (priv.node) { ++ pmc_agent_destroy(priv.node); ++ } + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +diff --git a/pmc_agent.c b/pmc_agent.c +index e83895c..8ccafe2 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -19,6 +19,7 @@ + */ + #include + #include ++#include + + #include "notification.h" + #include "pmc_agent.h" +@@ -32,6 +33,22 @@ + * renewed. + */ + ++struct pmc_agent { ++ struct pmc *pmc; ++ uint64_t pmc_last_update; ++ ++ struct ClockIdentity clock_identity; ++ int clock_identity_set; ++ int leap; ++ int pmc_ds_requested; ++ int sync_offset; ++ int utc_offset_traceable; ++ ++ /* Callback on message reception */ ++ pmc_node_recv_subscribed_t *recv_subscribed; ++ void *recv_context; ++}; ++ + static void send_subscription(struct pmc_agent *node) + { + struct subscribe_events_np sen; +@@ -155,7 +172,8 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + node->pmc_ds_requested = 0; + return -1; + } +- if (res <= 0 || node->recv_subscribed(node, *msg, ds_id) || ++ if (res <= 0 || ++ node->recv_subscribed(node->recv_context, *msg, ds_id) || + get_mgt_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; +@@ -337,7 +355,7 @@ int update_pmc_node(struct pmc_agent *node, int subscribe) + } + + int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed) ++ pmc_node_recv_subscribed_t *recv_subscribed, void *context) + { + node->pmc = pmc_create(cfg, TRANS_UDS, uds, 0, + config_get_int(cfg, NULL, "domainNumber"), +@@ -347,15 +365,41 @@ int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + return -1; + } + node->recv_subscribed = recv_subscribed; ++ node->recv_context = context; + + return 0; + } + +-void close_pmc_node(struct pmc_agent *node) ++struct pmc_agent *pmc_agent_create(void) ++{ ++ struct pmc_agent *agent = calloc(1, sizeof(*agent)); ++ return agent; ++} ++ ++void pmc_agent_destroy(struct pmc_agent *agent) ++{ ++ if (agent->pmc) { ++ pmc_destroy(agent->pmc); ++ } ++ free(agent); ++} ++ ++int pmc_agent_get_leap(struct pmc_agent *agent) + { +- if (!node->pmc) +- return; ++ return agent->leap; ++} ++ ++int pmc_agent_get_sync_offset(struct pmc_agent *agent) ++{ ++ return agent->sync_offset; ++} + +- pmc_destroy(node->pmc); +- node->pmc = NULL; ++void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) ++{ ++ agent->sync_offset = offset; ++} ++ ++bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) ++{ ++ return agent->utc_offset_traceable; + } +diff --git a/pmc_agent.h b/pmc_agent.h +index 10ef4b5..c0b4525 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -22,29 +22,17 @@ + #ifndef HAVE_PMC_AGENT_H + #define HAVE_PMC_AGENT_H + ++#include ++ + #include "pmc_common.h" + + struct pmc_agent; + +-typedef int pmc_node_recv_subscribed_t(struct pmc_agent *agent, +- struct ptp_message *msg, ++typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int excluded); + +-struct pmc_agent { +- struct pmc *pmc; +- int pmc_ds_requested; +- uint64_t pmc_last_update; +- int sync_offset; +- int leap; +- int utc_offset_traceable; +- int clock_identity_set; +- struct ClockIdentity clock_identity; +- pmc_node_recv_subscribed_t *recv_subscribed; +-}; +- + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, +- pmc_node_recv_subscribed_t *recv_subscribed); +-void close_pmc_node(struct pmc_agent *agent); ++ pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int update_pmc_node(struct pmc_agent *agent, int subscribe); + int run_pmc_subscribe(struct pmc_agent *agent, int timeout); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); +@@ -58,5 +46,45 @@ int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); + int get_mgt_id(struct ptp_message *msg); + void *get_mgt_data(struct ptp_message *msg); + +-#endif + ++/** ++ * Creates an instance of a PMC agent. ++ * @return Pointer to a PMC instance on success, NULL otherwise. ++ */ ++struct pmc_agent *pmc_agent_create(void); ++ ++/** ++ * Destroys an instance of a PMC agent. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ */ ++void pmc_agent_destroy(struct pmc_agent *agent); ++ ++/** ++ * Gets the current leap adjustment. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return The leap adjustment in seconds, either 1, 0, or -1. ++ */ ++int pmc_agent_get_leap(struct pmc_agent *agent); ++ ++/** ++ * Gets the TAI-UTC offset. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return Current offset in seconds. ++ */ ++int pmc_agent_get_sync_offset(struct pmc_agent *agent); ++ ++/** ++ * Sets the TAI-UTC offset. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param offset Desired offset in seconds. ++ */ ++void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); ++ ++/** ++ * Tests whether the current UTC offset is traceable. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return True is the offset is traceable, false otherwise. ++ */ ++bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent); ++ ++#endif +-- +2.25.1 + +From 4ebb69f5c55e7f1f08d1a73df87d42fe70147ec9 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:58:09 -0300 +Subject: [PATCH 17/47] Find a better home for the management TLV ID helper + function. + +Signed-off-by: Richard Cochran + +[commit d95bb9f9d62f4f372934905e97e052aa68dcfc58 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + msg.h | 12 ++++++++++++ + phc2sys.c | 2 +- + pmc_agent.c | 10 +--------- + pmc_agent.h | 1 - + 4 files changed, 14 insertions(+), 11 deletions(-) + +diff --git a/msg.h b/msg.h +index e71d3ce..b600ff0 100644 +--- a/msg.h ++++ b/msg.h +@@ -247,6 +247,18 @@ static inline uint8_t management_action(struct ptp_message *m) + return m->management.flags & 0x0f; + } + ++/** ++ * Obtain the ID field from the TLV in a management message. ++ * @param m A management message. ++ * @return The value of the ID field. ++ */ ++static inline int management_tlv_id(struct ptp_message *m) ++{ ++ struct management_tlv *mgt; ++ mgt = (struct management_tlv *) m->management.suffix; ++ return mgt->id; ++} ++ + /** + * Test a given bit in a message's flag field. + * @param m Message to test. +diff --git a/phc2sys.c b/phc2sys.c +index 037b1b9..1f74f27 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -819,7 +819,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + struct port *port; + struct clock *clock; + +- mgt_id = get_mgt_id(msg); ++ mgt_id = management_tlv_id(msg); + if (mgt_id == excluded) + return 0; + switch (mgt_id) { +diff --git a/pmc_agent.c b/pmc_agent.c +index 8ccafe2..6dfb3ca 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -85,14 +85,6 @@ static int is_msg_mgt(struct ptp_message *msg) + return 0; + } + +-int get_mgt_id(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->id; +-} +- + void *get_mgt_data(struct ptp_message *msg) + { + struct management_tlv *mgt; +@@ -174,7 +166,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + } + if (res <= 0 || + node->recv_subscribed(node->recv_context, *msg, ds_id) || +- get_mgt_id(*msg) != ds_id) { ++ management_tlv_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; + continue; +diff --git a/pmc_agent.h b/pmc_agent.h +index c0b4525..09249ff 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -43,7 +43,6 @@ int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); + int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); +-int get_mgt_id(struct ptp_message *msg); + void *get_mgt_data(struct ptp_message *msg); + + +-- +2.25.1 + +From 6e4f8ea8531b7678a44a9b3ed021fda94eccdc27 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 14:59:57 -0300 +Subject: [PATCH 18/47] Find a better home for the management TLV data helper + function. + +Signed-off-by: Richard Cochran + +[commit 5dd47c873cae8e0a2815b43c1ef3a86b9aca9dac upstream] +Signed-off-by: Andre Mauricio Zelak +--- + msg.h | 12 ++++++++++++ + phc2sys.c | 2 +- + pmc_agent.c | 18 +++++------------- + pmc_agent.h | 1 - + 4 files changed, 18 insertions(+), 15 deletions(-) + +diff --git a/msg.h b/msg.h +index b600ff0..84380da 100644 +--- a/msg.h ++++ b/msg.h +@@ -247,6 +247,18 @@ static inline uint8_t management_action(struct ptp_message *m) + return m->management.flags & 0x0f; + } + ++/** ++ * Obtain the data field from the TLV in a management message. ++ * @param m A management message. ++ * @return A pointer to the TLV data field. ++ */ ++static inline void *management_tlv_data(struct ptp_message *msg) ++{ ++ struct management_tlv *mgt; ++ mgt = (struct management_tlv *) msg->management.suffix; ++ return mgt->data; ++} ++ + /** + * Obtain the ID field from the TLV in a management message. + * @param m A management message. +diff --git a/phc2sys.c b/phc2sys.c +index 1f74f27..280e249 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -824,7 +824,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + return 0; + switch (mgt_id) { + case TLV_PORT_DATA_SET: +- pds = get_mgt_data(msg); ++ pds = management_tlv_data(msg); + port = port_get(priv, pds->portIdentity.portNumber); + if (!port) { + pr_info("received data for unknown port %s", +diff --git a/pmc_agent.c b/pmc_agent.c +index 6dfb3ca..6e9c023 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -85,14 +85,6 @@ static int is_msg_mgt(struct ptp_message *msg) + return 0; + } + +-void *get_mgt_data(struct ptp_message *msg) +-{ +- struct management_tlv *mgt; +- +- mgt = (struct management_tlv *) msg->management.suffix; +- return mgt->data; +-} +- + static int get_mgt_err_id(struct ptp_message *msg) + { + struct management_error_status *mgt; +@@ -188,7 +180,7 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- data = get_mgt_data(msg); ++ data = management_tlv_data(msg); + portState = ((struct portDS *)data)->portState; + msg_put(msg); + +@@ -212,7 +204,7 @@ int run_pmc_get_utc_offset(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- tds = (struct timePropertiesDS *)get_mgt_data(msg); ++ tds = (struct timePropertiesDS *) management_tlv_data(msg); + if (tds->flags & PTP_TIMESCALE) { + node->sync_offset = tds->currentUtcOffset; + if (tds->flags & LEAP_61) +@@ -242,7 +234,7 @@ int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- dds = (struct defaultDS *)get_mgt_data(msg); ++ dds = (struct defaultDS *) management_tlv_data(msg); + res = dds->numberPorts; + msg_put(msg); + return res; +@@ -281,7 +273,7 @@ int run_pmc_port_properties(struct pmc_agent *node, int timeout, + if (res <= 0) + goto out; + +- ppn = get_mgt_data(msg); ++ ppn = management_tlv_data(msg); + if (ppn->portIdentity.portNumber != port) { + msg_put(msg); + continue; +@@ -314,7 +306,7 @@ int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + if (res <= 0) + return res; + +- dds = (struct defaultDS *)get_mgt_data(msg); ++ dds = (struct defaultDS *) management_tlv_data(msg); + memcpy(&node->clock_identity, &dds->clockIdentity, + sizeof(struct ClockIdentity)); + node->clock_identity_set = 1; +diff --git a/pmc_agent.h b/pmc_agent.h +index 09249ff..f3a26fe 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -43,7 +43,6 @@ int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); + int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); +-void *get_mgt_data(struct ptp_message *msg); + + + /** +-- +2.25.1 + +From 95e4983c9ab517b9dda1faf171721f0dd877e076 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:04:11 -0300 +Subject: [PATCH 19/47] Introduce error codes for the run_pmc method. + +The run_pmc function is used by several of the PMC agent methods, but it +breaks the pattern of returning zero on success. However, the user facing +PMC agent methods will need to conform to the return code convention used +throughout the stack. + +In order to migrate to proper return codes, this patch replaces the hard +coded result values with macros so that the interface methods can translate +them to the required semantics of zero on success. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 802259bbe40faa5f8bdebab36e6fbcbc51c3c2a2 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 19 +++++++++---------- + 1 file changed, 9 insertions(+), 10 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index 6e9c023..22d9c5b 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -93,12 +93,11 @@ static int get_mgt_err_id(struct ptp_message *msg) + return mgt->id; + } + +-/* Return values: +- * 1: success +- * 0: timeout +- * -1: error reported by the other side +- * -2: local error, fatal +- */ ++#define RUN_PMC_OKAY 1 ++#define RUN_PMC_TMO 0 ++#define RUN_PMC_NODEV -1 ++#define RUN_PMC_INTR -2 ++ + static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + struct ptp_message **msg) + { +@@ -115,12 +114,12 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + cnt = poll(pollfd, N_FD, timeout); + if (cnt < 0) { + pr_err("poll failed"); +- return -2; ++ return RUN_PMC_INTR; + } + if (!cnt) { + /* Request the data set again in the next run. */ + node->pmc_ds_requested = 0; +- return 0; ++ return RUN_PMC_TMO; + } + + /* Send a new request if there are no pending messages. */ +@@ -154,7 +153,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + res = is_msg_mgt(*msg); + if (res < 0 && get_mgt_err_id(*msg) == ds_id) { + node->pmc_ds_requested = 0; +- return -1; ++ return RUN_PMC_NODEV; + } + if (res <= 0 || + node->recv_subscribed(node->recv_context, *msg, ds_id) || +@@ -164,7 +163,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + continue; + } + node->pmc_ds_requested = 0; +- return 1; ++ return RUN_PMC_OKAY; + } + } + +-- +2.25.1 + +From 8c1dd261683d27acba49e047d9f6da52dada3c98 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:08:01 -0300 +Subject: [PATCH 20/47] pmc_agent: Convert the subscribe method into the + canonical form. + +This patch renames the function to have the module prefix and corrects the +return code semantics. + +Signed-off-by: Richard Cochran +Reviewed-by: Jacob Keller +Reviewed-by: Vladimir Oltean + +[commit cc98d39f58adc1fd05db0038acfdcc5669f2ba8c upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 4 ++-- + pmc_agent.c | 48 +++++++++++++++++++++++++++++++++++------------- + pmc_agent.h | 9 ++++++++- + 3 files changed, 45 insertions(+), 16 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 280e249..f61e699 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -875,8 +875,8 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + return -1; + } + +- res = run_pmc_subscribe(priv->node, 1000); +- if (res <= 0) { ++ res = pmc_agent_subscribe(priv->node, 1000); ++ if (res) { + pr_err("failed to subscribe"); + return -1; + } +diff --git a/pmc_agent.c b/pmc_agent.c +index 22d9c5b..9c5eb71 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -17,6 +17,7 @@ + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ ++#include + #include + #include + #include +@@ -98,6 +99,26 @@ static int get_mgt_err_id(struct ptp_message *msg) + #define RUN_PMC_NODEV -1 + #define RUN_PMC_INTR -2 + ++static bool is_run_pmc_error(int code) ++{ ++ return code != RUN_PMC_OKAY; ++} ++ ++static int run_pmc_err2errno(int code) ++{ ++ switch (code) { ++ case RUN_PMC_TMO: ++ return -ETIMEDOUT; ++ case RUN_PMC_NODEV: ++ return -ENODEV; ++ case RUN_PMC_INTR: ++ return -EINTR; ++ case RUN_PMC_OKAY: ++ default: ++ return 0; ++ } ++} ++ + static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + struct ptp_message **msg) + { +@@ -239,18 +260,6 @@ int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + return res; + } + +-int run_pmc_subscribe(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (res <= 0) +- return res; +- msg_put(msg); +- return 1; +-} +- + void run_pmc_events(struct pmc_agent *node) + { + struct ptp_message *msg; +@@ -329,7 +338,7 @@ int update_pmc_node(struct pmc_agent *node, int subscribe) + !(ts > node->pmc_last_update && + ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (subscribe) +- run_pmc_subscribe(node, 0); ++ pmc_agent_subscribe(node, 0); + if (run_pmc_get_utc_offset(node, 0) > 0) + node->pmc_last_update = ts; + } +@@ -382,6 +391,19 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + agent->sync_offset = offset; + } + ++int pmc_agent_subscribe(struct pmc_agent *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ msg_put(msg); ++ return 0; ++} ++ + bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) + { + return agent->utc_offset_traceable; +diff --git a/pmc_agent.h b/pmc_agent.h +index f3a26fe..9dc684e 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -34,7 +34,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int update_pmc_node(struct pmc_agent *agent, int subscribe); +-int run_pmc_subscribe(struct pmc_agent *agent, int timeout); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); +@@ -78,6 +77,14 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent); + */ + void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); + ++/** ++ * Subscribes to push notifications of changes in port state. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_subscribe(struct pmc_agent *agent, int timeout); ++ + /** + * Tests whether the current UTC offset is traceable. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From 82a369b4fe44a7cea41fb0ccf408c02b1b6aa694 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:17:26 -0300 +Subject: [PATCH 21/47] pmc_agent: Simplify the update method. + +The main method that causes the PMC agent to update its status takes a flag +that results in different behavior when push notifications are active. +This patch simplifies the interface by letting the agent remember whether +or not the caller subscribed to the notifications in the first place. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 1126f8f67e853199f05a7c993c910ebc7807bd3d upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 6 ++++-- + pmc_agent.c | 32 ++++++++++++++++++++------------ + pmc_agent.h | 2 +- + 3 files changed, 25 insertions(+), 15 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index f61e699..b155961 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -672,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc_node(priv->node, 0) < 0) ++ if (update_pmc_node(priv->node) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -710,8 +710,10 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); +- if (update_pmc_node(priv->node, subscriptions) < 0) ++ ++ if (update_pmc_node(priv->node) < 0) { + continue; ++ } + + if (subscriptions) { + run_pmc_events(priv->node); +diff --git a/pmc_agent.c b/pmc_agent.c +index 9c5eb71..dd509af 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -42,6 +42,7 @@ struct pmc_agent { + int clock_identity_set; + int leap; + int pmc_ds_requested; ++ bool stay_subscribed; + int sync_offset; + int utc_offset_traceable; + +@@ -188,6 +189,19 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + } + } + ++static int renew_subscription(struct pmc_agent *node, int timeout) ++{ ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ msg_put(msg); ++ return 0; ++} ++ + int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +@@ -323,7 +337,7 @@ int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + } + + /* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_agent *node, int subscribe) ++int update_pmc_node(struct pmc_agent *node) + { + struct timespec tp; + uint64_t ts; +@@ -337,8 +351,9 @@ int update_pmc_node(struct pmc_agent *node, int subscribe) + if (node->pmc && + !(ts > node->pmc_last_update && + ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { +- if (subscribe) +- pmc_agent_subscribe(node, 0); ++ if (node->stay_subscribed) { ++ renew_subscription(node, 0); ++ } + if (run_pmc_get_utc_offset(node, 0) > 0) + node->pmc_last_update = ts; + } +@@ -393,15 +408,8 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + + int pmc_agent_subscribe(struct pmc_agent *node, int timeout) + { +- struct ptp_message *msg; +- int res; +- +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); +- if (is_run_pmc_error(res)) { +- return run_pmc_err2errno(res); +- } +- msg_put(msg); +- return 0; ++ node->stay_subscribed = true; ++ return renew_subscription(node, timeout); + } + + bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) +diff --git a/pmc_agent.h b/pmc_agent.h +index 9dc684e..743818f 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -33,7 +33,7 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); +-int update_pmc_node(struct pmc_agent *agent, int subscribe); ++int update_pmc_node(struct pmc_agent *agent); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); +-- +2.25.1 + +From 731e8938953e56578007a679dbaa29e9471650ac Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:18:36 -0300 +Subject: [PATCH 22/47] pmc_agent: Simplify logic in update method. + +If the pmc pointer is not set, then there is no need to read the time only +to later discard the result. This patch simplifies the flow by returning +early if there is no work to be done. + +Signed-off-by: Richard Cochran +Reviewed-by: Jacob Keller +Reviewed-by: Vladimir Oltean + +[commit 956b7eeb8247e3f0658b1205dfd3bea3e1011ee2 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index dd509af..f30f174 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -342,14 +342,16 @@ int update_pmc_node(struct pmc_agent *node) + struct timespec tp; + uint64_t ts; + ++ if (!node->pmc) { ++ return 0; ++ } + if (clock_gettime(CLOCK_MONOTONIC, &tp)) { + pr_err("failed to read clock: %m"); + return -1; + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (node->pmc && +- !(ts > node->pmc_last_update && ++ if (!(ts > node->pmc_last_update && + ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (node->stay_subscribed) { + renew_subscription(node, 0); +-- +2.25.1 + +From 357e24c897e1e2d29cf011b3a38c3a6b2a7943c3 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:33:43 -0300 +Subject: [PATCH 23/47] pmc_agent: Remove bogus comparison between last update + and now. + +The monotonic clock can never go backwards. If you take T1 and later T2 +from that clock, then (T2 > T1) is always true. + +This patch removes the useless test. + +[ This test evolved over the years. Originally the time stamp in question + came from a PHC. ] + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 2f2f7fc5881a88295350430edaf4505dc03b1602 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index f30f174..df3a562 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -351,8 +351,7 @@ int update_pmc_node(struct pmc_agent *node) + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (!(ts > node->pmc_last_update && +- ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (!(ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +-- +2.25.1 + +From d5421e4d4d86907648a59810ab9c27e739591971 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:35:23 -0300 +Subject: [PATCH 24/47] pmc_agent: Perform time comparison using positive + logic. + +In the update_pmc_node() method, reduce the expression +!(x < y) to (x >= y). + +While we're at it, clean the coding style as well. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit fb92fec7cef9ee3345950c2633a7781b8bd3ca08 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index df3a562..ea6b3b7 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -351,12 +351,13 @@ int update_pmc_node(struct pmc_agent *node) + } + ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; + +- if (!(ts - node->pmc_last_update < PMC_UPDATE_INTERVAL)) { ++ if (ts - node->pmc_last_update >= PMC_UPDATE_INTERVAL) { + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +- if (run_pmc_get_utc_offset(node, 0) > 0) ++ if (run_pmc_get_utc_offset(node, 0) > 0) { + node->pmc_last_update = ts; ++ } + } + + return 0; +-- +2.25.1 + +From a304d4df86a76c187fc7074755fe9b5ad349efbe Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:36:38 -0300 +Subject: [PATCH 25/47] pmc_agent: Rename the update method and attempt to + document it. + +This patch renames the function to have the module prefix and tries to +put into words what it does. + +Signed-off-by: Richard Cochran +Reviewed-by: Vladimir Oltean + +[commit 9a2dae984e0d355d751913e3308f9a954da11aa3 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 4 ++-- + pmc_agent.c | 53 ++++++++++++++++++++++++++--------------------------- + pmc_agent.h | 21 ++++++++++++++++++++- + 3 files changed, 48 insertions(+), 30 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index b155961..cbe80f2 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -672,7 +672,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (update_pmc_node(priv->node) < 0) ++ if (pmc_agent_update(priv->node) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -711,7 +711,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); + +- if (update_pmc_node(priv->node) < 0) { ++ if (pmc_agent_update(priv->node) < 0) { + continue; + } + +diff --git a/pmc_agent.c b/pmc_agent.c +index ea6b3b7..22af306 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -336,33 +336,6 @@ int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + return 1; + } + +-/* Returns: -1 in case of error, 0 otherwise */ +-int update_pmc_node(struct pmc_agent *node) +-{ +- struct timespec tp; +- uint64_t ts; +- +- if (!node->pmc) { +- return 0; +- } +- if (clock_gettime(CLOCK_MONOTONIC, &tp)) { +- pr_err("failed to read clock: %m"); +- return -1; +- } +- ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; +- +- if (ts - node->pmc_last_update >= PMC_UPDATE_INTERVAL) { +- if (node->stay_subscribed) { +- renew_subscription(node, 0); +- } +- if (run_pmc_get_utc_offset(node, 0) > 0) { +- node->pmc_last_update = ts; +- } +- } +- +- return 0; +-} +- + int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context) + { +@@ -414,6 +387,32 @@ int pmc_agent_subscribe(struct pmc_agent *node, int timeout) + return renew_subscription(node, timeout); + } + ++int pmc_agent_update(struct pmc_agent *node) ++{ ++ struct timespec tp; ++ uint64_t ts; ++ ++ if (!node->pmc) { ++ return 0; ++ } ++ if (clock_gettime(CLOCK_MONOTONIC, &tp)) { ++ pr_err("failed to read clock: %m"); ++ return -errno; ++ } ++ ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec; ++ ++ if (ts - node->pmc_last_update >= PMC_UPDATE_INTERVAL) { ++ if (node->stay_subscribed) { ++ renew_subscription(node, 0); ++ } ++ if (run_pmc_get_utc_offset(node, 0) > 0) { ++ node->pmc_last_update = ts; ++ } ++ } ++ ++ return 0; ++} ++ + bool pmc_agent_utc_offset_traceable(struct pmc_agent *agent) + { + return agent->utc_offset_traceable; +diff --git a/pmc_agent.h b/pmc_agent.h +index 743818f..483a21b 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -33,7 +33,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); +-int update_pmc_node(struct pmc_agent *agent); + int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); +@@ -85,6 +84,26 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); + */ + int pmc_agent_subscribe(struct pmc_agent *agent, int timeout); + ++/** ++ * Queries the local ptp4l instance to update the TAI-UTC offset and ++ * the current leap second flags. ++ * ++ * In addition: ++ * ++ * - Any active port state subscription will be renewed. ++ * - The port state notification callback might be invoked. ++ * ++ * This function should be called periodically at least once per ++ * minute to keep both the port state and the leap second flags up to ++ * date. Note that the PMC agent rate limits the query to once per ++ * minute, and so the caller may safely invoke this method more often ++ * than that. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_update(struct pmc_agent *agent); ++ + /** + * Tests whether the current UTC offset is traceable. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From 5aacbe319db97907a15741005e2790bbf4c742a0 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 15:37:46 -0300 +Subject: [PATCH 26/47] phc2sys: Fix null pointer de-reference in manual mode. + +If both the -w and -O command line options are specified (or when +using -w when both source and destination clocks are PHCs), then +pointer to the PMC agent will be incorrectly freed. + +Fix the segfault by introducing a method to "disable" the agent as was +done before the PMC agent code was introduced. + +Unfortunately the resulting PMC agent API now has both create/destroy +and init/disable methods. This clunky arrangement can be cleaned up +later on, but it entails re-factoring the phc2sys program even more. + +Signed-off-by: Richard Cochran +Fixes: 8266987 ("pmc_agent: Hide the implementation.") + +[commit 68fd0b010e9761e3dc580026eb6f2366c7c8e82d upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 7 ++----- + pmc_agent.c | 8 ++++++++ + pmc_agent.h | 6 ++++++ + 3 files changed, 16 insertions(+), 5 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index cbe80f2..3cafbb2 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1340,8 +1340,7 @@ int main(int argc, char *argv[]) + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || + src->clkid == CLOCK_INVALID) { +- pmc_agent_destroy(priv.node); +- priv.node = NULL; ++ pmc_agent_disable(priv.node); + } + } + +@@ -1355,9 +1354,7 @@ int main(int argc, char *argv[]) + } + + end: +- if (priv.node) { +- pmc_agent_destroy(priv.node); +- } ++ pmc_agent_destroy(priv.node); + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +diff --git a/pmc_agent.c b/pmc_agent.c +index 22af306..833d1c1 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -366,6 +366,14 @@ void pmc_agent_destroy(struct pmc_agent *agent) + free(agent); + } + ++void pmc_agent_disable(struct pmc_agent *agent) ++{ ++ if (agent->pmc) { ++ pmc_destroy(agent->pmc); ++ } ++ agent->pmc = NULL; ++} ++ + int pmc_agent_get_leap(struct pmc_agent *agent) + { + return agent->leap; +diff --git a/pmc_agent.h b/pmc_agent.h +index 483a21b..0ed10f8 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -55,6 +55,12 @@ struct pmc_agent *pmc_agent_create(void); + */ + void pmc_agent_destroy(struct pmc_agent *agent); + ++/** ++ * Disconnects the PMC agent from the ptp4l service. ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ */ ++void pmc_agent_disable(struct pmc_agent *agent); ++ + /** + * Gets the current leap adjustment. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From b8188a4fd51bc8983e5d19f18fe37b8ca39d03a6 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:20:04 -0300 +Subject: [PATCH 27/47] pmc_agent: Convert the method that queries TAI-UTC + offset into the canonical form. + +This patch renames the function to have the module prefix and corrects the +return code semantics. + +The active word in the function's name is "query" rather that "get" in +order to distinguish methods that send and receive over the network +from those that merely return a cached value. + +Signed-off-by: Richard Cochran +Reviewed-by: Jacob Keller + +[commit 943c8f51c56acb72277d1a9459bbf7b7a5ac5fe7 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 8 +++---- + pmc_agent.c | 63 +++++++++++++++++++++++++++-------------------------- + pmc_agent.h | 16 ++++++++++++-- + 3 files changed, 50 insertions(+), 37 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 3cafbb2..78d662b 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -720,7 +720,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + if (priv->state_changed) { + /* force getting offset, as it may have + * changed after the port state change */ +- if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { ++ if (pmc_agent_query_utc_offset(priv->node, 1000)) { + pr_err("failed to get UTC offset"); + continue; + } +@@ -921,7 +921,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (run_pmc_get_utc_offset(priv->node, 1000) <= 0) { ++ if (pmc_agent_query_utc_offset(priv->node, 1000)) { + pr_err("failed to get UTC offset"); + return -1; + } +@@ -1330,8 +1330,8 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = run_pmc_get_utc_offset(priv.node, 1000); +- if (r <= 0) { ++ r = pmc_agent_query_utc_offset(priv.node, 1000); ++ if (r) { + pr_err("failed to get UTC offset"); + goto end; + } +diff --git a/pmc_agent.c b/pmc_agent.c +index 833d1c1..7a57a2f 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -228,36 +228,6 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + } + } + +-int run_pmc_get_utc_offset(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct timePropertiesDS *tds; +- +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- tds = (struct timePropertiesDS *) management_tlv_data(msg); +- if (tds->flags & PTP_TIMESCALE) { +- node->sync_offset = tds->currentUtcOffset; +- if (tds->flags & LEAP_61) +- node->leap = 1; +- else if (tds->flags & LEAP_59) +- node->leap = -1; +- else +- node->leap = 0; +- node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && +- tds->flags & TIME_TRACEABLE; +- } else { +- node->sync_offset = 0; +- node->leap = 0; +- node->utc_offset_traceable = 0; +- } +- msg_put(msg); +- return 1; +-} +- + int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +@@ -384,6 +354,37 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) ++{ ++ struct timePropertiesDS *tds; ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ ++ tds = (struct timePropertiesDS *) management_tlv_data(msg); ++ if (tds->flags & PTP_TIMESCALE) { ++ node->sync_offset = tds->currentUtcOffset; ++ if (tds->flags & LEAP_61) ++ node->leap = 1; ++ else if (tds->flags & LEAP_59) ++ node->leap = -1; ++ else ++ node->leap = 0; ++ node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && ++ tds->flags & TIME_TRACEABLE; ++ } else { ++ node->sync_offset = 0; ++ node->leap = 0; ++ node->utc_offset_traceable = 0; ++ } ++ msg_put(msg); ++ return 0; ++} ++ + void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + { + agent->sync_offset = offset; +@@ -413,7 +414,7 @@ int pmc_agent_update(struct pmc_agent *node) + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +- if (run_pmc_get_utc_offset(node, 0) > 0) { ++ if (!pmc_agent_query_utc_offset(node, 0)) { + node->pmc_last_update = ts; + } + } +diff --git a/pmc_agent.h b/pmc_agent.h +index 0ed10f8..44326d2 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -40,8 +40,6 @@ void run_pmc_events(struct pmc_agent *agent); + int run_pmc_port_properties(struct pmc_agent *agent, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface); +-int run_pmc_get_utc_offset(struct pmc_agent *agent, int timeout); +- + + /** + * Creates an instance of a PMC agent. +@@ -75,6 +73,20 @@ int pmc_agent_get_leap(struct pmc_agent *agent); + */ + int pmc_agent_get_sync_offset(struct pmc_agent *agent); + ++/** ++ * Queries the TAI-UTC offset and the current leap adjustment from the ++ * ptp4l service. ++ * ++ * In addition: ++ * ++ * - The port state notification callback might be invoked. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_utc_offset(struct pmc_agent *agent, int timeout); ++ + /** + * Sets the TAI-UTC offset. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From acdf74df9fa69b81c1e9332f10d4efcd3e9bae48 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:23:29 -0300 +Subject: [PATCH 28/47] pmc_agent: Convert the method that queries the port + properties. + +Prefix the function with the module name and correct the return code +semantics. + +The active word in the function's name is "query" rather that "get" in +order to distinguish methods that send and receive over the network +from those that merely return a cached value. + +Signed-off-by: Richard Cochran + +[commit ac7d69bbc476b94d76e5cee4992b9682f003feaf upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 41 +++++++++++++++-------------- + pmc_agent.c | 74 ++++++++++++++++++++++++++--------------------------- + pmc_agent.h | 22 +++++++++++++--- + 3 files changed, 78 insertions(+), 59 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 78d662b..32e6e13 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -296,8 +296,7 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + int new_state) + { +- int phc_index = -1, phc_switched = 0; +- int state, timestamping, ret = -1; ++ int err = -1, phc_index = -1, phc_switched = 0, state, timestamping; + struct port *p; + struct servo *servo; + struct sk_ts_info ts_info; +@@ -305,16 +304,19 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + clockid_t clkid = CLOCK_INVALID; + + LIST_FOREACH(p, &priv->ports, list) { +- if (p->clock == clock) { +- ret = run_pmc_port_properties(priv->node, 1000, p->number, +- &state, ×tamping, +- iface); +- if (ret > 0) +- p->state = normalize_state(state); ++ if (p->clock != clock) { ++ continue; ++ } ++ err = pmc_agent_query_port_properties(priv->node, 1000, ++ p->number, &state, ++ ×tamping, iface); ++ if (!err) { ++ p->state = normalize_state(state); + } ++ break; + } + +- if (ret > 0 && timestamping != TS_SOFTWARE) { ++ if (!err && timestamping != TS_SOFTWARE) { + /* Check if device changed */ + if (strcmp(clock->device, iface)) { + free(clock->device); +@@ -852,12 +854,12 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + { +- struct port *port; +- struct clock *clock; +- int number_ports, res; +- unsigned int i; ++ int err, number_ports, res; + int state, timestamping; + char iface[IFNAMSIZ]; ++ struct clock *clock; ++ struct port *port; ++ unsigned int i; + + while (1) { + if (!is_running()) +@@ -877,20 +879,21 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + return -1; + } + +- res = pmc_agent_subscribe(priv->node, 1000); +- if (res) { ++ err = pmc_agent_subscribe(priv->node, 1000); ++ if (err) { + pr_err("failed to subscribe"); + return -1; + } + + for (i = 1; i <= number_ports; i++) { +- res = run_pmc_port_properties(priv->node, 1000, i, &state, +- ×tamping, iface); +- if (res == -1) { ++ err = pmc_agent_query_port_properties(priv->node, 1000, i, ++ &state, ×tamping, ++ iface); ++ if (err == -ENODEV) { + /* port does not exist, ignore the port */ + continue; + } +- if (res <= 0) { ++ if (err) { + pr_err("failed to get port properties"); + return -1; + } +diff --git a/pmc_agent.c b/pmc_agent.c +index 7a57a2f..cc729ab 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -251,43 +251,6 @@ void run_pmc_events(struct pmc_agent *node) + run_pmc(node, 0, -1, &msg); + } + +-int run_pmc_port_properties(struct pmc_agent *node, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface) +-{ +- struct ptp_message *msg; +- int res, len; +- struct port_properties_np *ppn; +- +- pmc_target_port(node->pmc, port); +- while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); +- if (res <= 0) +- goto out; +- +- ppn = management_tlv_data(msg); +- if (ppn->portIdentity.portNumber != port) { +- msg_put(msg); +- continue; +- } +- +- *state = ppn->port_state; +- *tstamping = ppn->timestamping; +- len = ppn->interface.length; +- if (len > IFNAMSIZ - 1) +- len = IFNAMSIZ - 1; +- memcpy(iface, ppn->interface.text, len); +- iface[len] = '\0'; +- +- msg_put(msg); +- res = 1; +- break; +- } +-out: +- pmc_target_all(node->pmc); +- return res; +-} +- + int run_pmc_clock_identity(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +@@ -354,6 +317,43 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface) ++{ ++ struct port_properties_np *ppn; ++ struct ptp_message *msg; ++ int res, len; ++ ++ pmc_target_port(node->pmc, port); ++ while (1) { ++ res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ if (is_run_pmc_error(res)) { ++ goto out; ++ } ++ ppn = management_tlv_data(msg); ++ if (ppn->portIdentity.portNumber != port) { ++ msg_put(msg); ++ continue; ++ } ++ *state = ppn->port_state; ++ *tstamping = ppn->timestamping; ++ len = ppn->interface.length; ++ if (len > IFNAMSIZ - 1) { ++ len = IFNAMSIZ - 1; ++ } ++ memcpy(iface, ppn->interface.text, len); ++ iface[len] = '\0'; ++ ++ msg_put(msg); ++ res = 0; ++ break; ++ } ++out: ++ pmc_target_all(node->pmc); ++ return run_pmc_err2errno(res); ++} ++ + int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + { + struct timePropertiesDS *tds; +diff --git a/pmc_agent.h b/pmc_agent.h +index 44326d2..ea37bf9 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -37,9 +37,6 @@ int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); + void run_pmc_events(struct pmc_agent *agent); +-int run_pmc_port_properties(struct pmc_agent *agent, int timeout, +- unsigned int port, int *state, +- int *tstamping, char *iface); + + /** + * Creates an instance of a PMC agent. +@@ -73,6 +70,25 @@ int pmc_agent_get_leap(struct pmc_agent *agent); + */ + int pmc_agent_get_sync_offset(struct pmc_agent *agent); + ++/** ++ * Queries the port properties of a given port from the ptp4l service. ++ * ++ * In addition: ++ * ++ * - The port state notification callback might be invoked. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @param port The port index of interest. ++ * @param state Buffer to hold the returned port state. ++ * @param tstamping Buffer to hold the returned time stamping flavor. ++ * @param iface Buffer to hold the returned interface name. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_port_properties(struct pmc_agent *agent, int timeout, ++ unsigned int port, int *state, ++ int *tstamping, char *iface); ++ + /** + * Queries the TAI-UTC offset and the current leap adjustment from the + * ptp4l service. +-- +2.25.1 + +From 3e6dd047083625ca03df9b4bbdc781e7dd079ff2 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:29:30 -0300 +Subject: [PATCH 29/47] pmc_agent: Generalize the method that queries the local + clock identity. + +When started in automatic mode, the phc2sys program first queries the +local clock identification and then the number of ports immediately +afterwords. However, both of those values come from the default data +set. Make code both simpler and more efficient by caching the entire +data set inside of the agent. + +A subsequent patch will fix the run_pmc_get_number_ports() method to +return the cached result. + +The active word in the function's name is "query" rather that "get" in +order to distinguish methods that send and receive over the network +from those that merely return a cached value. + +Signed-off-by: Richard Cochran + +[commit 919703eb06b7ee9679308597e01e1da0162736d7 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 20 +++++++++++--------- + pmc_agent.c | 46 +++++++++++++++++++++++----------------------- + pmc_agent.h | 15 ++++++++++++++- + 3 files changed, 48 insertions(+), 33 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 32e6e13..0f33630 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -854,23 +854,25 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + + static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + { +- int err, number_ports, res; +- int state, timestamping; ++ int err, number_ports, state, timestamping; + char iface[IFNAMSIZ]; + struct clock *clock; + struct port *port; + unsigned int i; + + while (1) { +- if (!is_running()) ++ if (!is_running()) { + return -1; +- res = run_pmc_clock_identity(priv->node, 1000); +- if (res < 0) +- return -1; +- if (res > 0) ++ } ++ err = pmc_agent_query_dds(priv->node, 1000); ++ if (!err) { + break; +- /* res == 0, timeout */ +- pr_notice("Waiting for ptp4l..."); ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ } else { ++ return -1; ++ } + } + + number_ports = run_pmc_get_number_ports(priv->node, 1000); +diff --git a/pmc_agent.c b/pmc_agent.c +index cc729ab..51023d1 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -38,8 +38,8 @@ struct pmc_agent { + struct pmc *pmc; + uint64_t pmc_last_update; + +- struct ClockIdentity clock_identity; +- int clock_identity_set; ++ struct defaultDS dds; ++ bool dds_valid; + int leap; + int pmc_ds_requested; + bool stay_subscribed; +@@ -63,10 +63,11 @@ static void send_subscription(struct pmc_agent *node) + + static int check_clock_identity(struct pmc_agent *node, struct ptp_message *msg) + { +- if (!node->clock_identity_set) ++ if (!node->dds_valid) { + return 1; +- return cid_eq(&node->clock_identity, +- &msg->header.sourcePortIdentity.clockIdentity); ++ } ++ return cid_eq(&node->dds.clockIdentity, ++ &msg->header.sourcePortIdentity.clockIdentity); + } + + static int is_msg_mgt(struct ptp_message *msg) +@@ -251,24 +252,6 @@ void run_pmc_events(struct pmc_agent *node) + run_pmc(node, 0, -1, &msg); + } + +-int run_pmc_clock_identity(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- struct defaultDS *dds; +- int res; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *) management_tlv_data(msg); +- memcpy(&node->clock_identity, &dds->clockIdentity, +- sizeof(struct ClockIdentity)); +- node->clock_identity_set = 1; +- msg_put(msg); +- return 1; +-} +- + int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context) + { +@@ -317,6 +300,23 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_query_dds(struct pmc_agent *node, int timeout) ++{ ++ struct ptp_message *msg; ++ struct defaultDS *dds; ++ int res; ++ ++ res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ dds = (struct defaultDS *) management_tlv_data(msg); ++ memcpy(&node->dds, dds, sizeof(node->dds)); ++ node->dds_valid = true; ++ msg_put(msg); ++ return 0; ++} ++ + int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, + unsigned int port, int *state, + int *tstamping, char *iface) +diff --git a/pmc_agent.h b/pmc_agent.h +index ea37bf9..9d8bd1c 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -33,7 +33,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); +-int run_pmc_clock_identity(struct pmc_agent *agent, int timeout); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); + int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); + void run_pmc_events(struct pmc_agent *agent); +@@ -70,6 +69,20 @@ int pmc_agent_get_leap(struct pmc_agent *agent); + */ + int pmc_agent_get_sync_offset(struct pmc_agent *agent); + ++/** ++ * Queries the local clock's default data set from the ptp4l service. ++ * The result of the query will be cached inside of the agent. ++ * ++ * In addition: ++ * ++ * - The port state notification callback might be invoked. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_dds(struct pmc_agent *agent, int timeout); ++ + /** + * Queries the port properties of a given port from the ptp4l service. + * +-- +2.25.1 + +From d3b877cae9576beddb00d4c5db67bf49c944b222 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:30:57 -0300 +Subject: [PATCH 30/47] pmc_agent: Simplify the method that gets of the number + of local ports. + +The number of ports is already available in the cached default data +set. Use it directly. + +Signed-off-by: Richard Cochran + +[commit 6bc9eb81dd254d90b5fe059684271b9beebf6b9b upstream] +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 2 +- + pmc_agent.c | 24 ++++++++---------------- + pmc_agent.h | 11 ++++++++++- + 3 files changed, 19 insertions(+), 18 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 0f33630..569544e 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -875,7 +875,7 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + } + +- number_ports = run_pmc_get_number_ports(priv->node, 1000); ++ number_ports = pmc_agent_get_number_ports(priv->node); + if (number_ports <= 0) { + pr_err("failed to get number of ports"); + return -1; +diff --git a/pmc_agent.c b/pmc_agent.c +index 51023d1..aa2347d 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -229,22 +229,6 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + } + } + +-int run_pmc_get_number_ports(struct pmc_agent *node, int timeout) +-{ +- struct ptp_message *msg; +- int res; +- struct defaultDS *dds; +- +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); +- if (res <= 0) +- return res; +- +- dds = (struct defaultDS *) management_tlv_data(msg); +- res = dds->numberPorts; +- msg_put(msg); +- return res; +-} +- + void run_pmc_events(struct pmc_agent *node) + { + struct ptp_message *msg; +@@ -300,6 +284,14 @@ int pmc_agent_get_sync_offset(struct pmc_agent *agent) + return agent->sync_offset; + } + ++int pmc_agent_get_number_ports(struct pmc_agent *node) ++{ ++ if (!node->dds_valid) { ++ return -1; ++ } ++ return node->dds.numberPorts; ++} ++ + int pmc_agent_query_dds(struct pmc_agent *node, int timeout) + { + struct ptp_message *msg; +diff --git a/pmc_agent.h b/pmc_agent.h +index 9d8bd1c..f0e2c7a 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -34,7 +34,6 @@ typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); +-int run_pmc_get_number_ports(struct pmc_agent *agent, int timeout); + void run_pmc_events(struct pmc_agent *agent); + + /** +@@ -62,6 +61,16 @@ void pmc_agent_disable(struct pmc_agent *agent); + */ + int pmc_agent_get_leap(struct pmc_agent *agent); + ++/** ++ * Gets the number of local ports from the default data set. Users ++ * should first call pmc_agent_query_dds() before invoking this ++ * function. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @return The non-negative number of ports, or -1 if unknown. ++ */ ++int pmc_agent_get_number_ports(struct pmc_agent *agent); ++ + /** + * Gets the TAI-UTC offset. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From 156728d14591dd2b3131bcff49959e806523c1bb Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:32:27 -0300 +Subject: [PATCH 31/47] pmc_agent: Let the update method poll for push events. + +Signed-off-by: Richard Cochran + +[commit c4a5eef1f4763805e6e2a2d25eb1d436018d4745 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 3 +++ + pmc_agent.h | 5 +++-- + 2 files changed, 6 insertions(+), 2 deletions(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index aa2347d..6e6627d 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -390,6 +390,7 @@ int pmc_agent_subscribe(struct pmc_agent *node, int timeout) + + int pmc_agent_update(struct pmc_agent *node) + { ++ struct ptp_message *msg; + struct timespec tp; + uint64_t ts; + +@@ -411,6 +412,8 @@ int pmc_agent_update(struct pmc_agent *node) + } + } + ++ run_pmc(node, 0, -1, &msg); ++ + return 0; + } + +diff --git a/pmc_agent.h b/pmc_agent.h +index f0e2c7a..dd34d30 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -141,11 +141,12 @@ void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset); + int pmc_agent_subscribe(struct pmc_agent *agent, int timeout); + + /** +- * Queries the local ptp4l instance to update the TAI-UTC offset and +- * the current leap second flags. ++ * Polls for push notifications from the local ptp4l service. + * + * In addition: + * ++ * - Queries the local ptp4l instance to update the TAI-UTC offset and ++ * the current leap second flags. + * - Any active port state subscription will be renewed. + * - The port state notification callback might be invoked. + * +-- +2.25.1 + +From 0e504e57af6c576202bbe1abe5a99eb24a981b73 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:51:10 -0300 +Subject: [PATCH 32/47] phc2sys: Fix regression in the automatic mode. + +Commit ac7d69bbc476 ("pmc_agent: Convert the method that queries the +port properties.") had the well meant intention of the cleaning up the +error code semantics of the port properties query function. However, +that commit mixed up the normal, external semantics of zero meaning +success with the internal semantics where zero is an error. Correct +the issue by replacing the hard coded number with the proper macro. + +Signed-off-by: Richard Cochran +Fixes: ac7d69bbc476 ("pmc_agent: Convert the method that queries the port properties.") + +[commit 0fb1be2f5c4d6905f33a2b1c31e7496d52296748 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + pmc_agent.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/pmc_agent.c b/pmc_agent.c +index 6e6627d..623f300 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -338,7 +338,7 @@ int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, + iface[len] = '\0'; + + msg_put(msg); +- res = 0; ++ res = RUN_PMC_OKAY; + break; + } + out: +-- +2.25.1 + +From 06a6734e3350e4020b4bb7b24a15d43aa42b4ca7 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 17:57:11 -0300 +Subject: [PATCH 33/47] Implement push notification for TIME_STATUS_NP + +Subscribers to NOTIFY_TIME_SYNC will be notified on every clock +synchronization. + +[ RC: + - Don't subscribe this in pmc_agent. + - Use stdbool/stdint types in event_bitmask_get/set. ] + +Signed-off-by: Juergen Werner +Signed-off-by: Richard Cochran + +[commit 6d7c090706e76af334185ffcec9cc56d0570e215 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 14 +++++++++----- + notification.h | 22 ++++++++++++++++++++++ + pmc.c | 6 ++++-- + pmc_agent.c | 2 +- + pmc_common.c | 23 +++++++++++++++-------- + 5 files changed, 51 insertions(+), 16 deletions(-) + +diff --git a/clock.c b/clock.c +index 437cd1c..f048771 100644 +--- a/clock.c ++++ b/clock.c +@@ -243,13 +243,11 @@ static void clock_prune_subscriptions(struct clock *c) + void clock_send_notification(struct clock *c, struct ptp_message *msg, + enum notification event) + { +- unsigned int event_pos = event / 8; +- uint8_t mask = 1 << (event % 8); + struct port *uds = c->uds_port; + struct clock_subscriber *s; + + LIST_FOREACH(s, &c->subscribers, list) { +- if (!(s->events[event_pos] & mask)) ++ if (!event_bitmask_get(s->events, event)) + continue; + /* send event */ + msg->header.sequenceId = htons(s->sequenceId); +@@ -1501,7 +1499,9 @@ void clock_notify_event(struct clock *c, enum notification event) + int id; + + switch (event) { +- /* set id */ ++ case NOTIFY_TIME_SYNC: ++ id = TLV_TIME_STATUS_NP; ++ break; + default: + return; + } +@@ -1731,7 +1731,9 @@ enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin) + c->cur.offsetFromMaster = tmv_to_TimeInterval(c->master_offset); + + if (c->free_running) { +- return clock_no_adjust(c, ingress, origin); ++ state = clock_no_adjust(c, ingress, origin); ++ clock_notify_event(c, NOTIFY_TIME_SYNC); ++ return state; + } + + offset = tmv_to_nanoseconds(c->master_offset); +@@ -1777,6 +1779,8 @@ enum servo_state clock_synchronize(struct clock *c, tmv_t ingress, tmv_t origin) + tmv_to_nanoseconds(c->path_delay)); + } + ++ clock_notify_event(c, NOTIFY_TIME_SYNC); ++ + return state; + } + +diff --git a/notification.h b/notification.h +index 47c9b56..115f864 100644 +--- a/notification.h ++++ b/notification.h +@@ -20,8 +20,30 @@ + #ifndef HAVE_NOTIFICATION_H + #define HAVE_NOTIFICATION_H + ++#include ++#include ++ ++static inline void event_bitmask_set(uint8_t *bitmask, unsigned int event, ++ bool value) ++{ ++ unsigned int event_pos = event / 8; ++ uint8_t event_bit = 1 << (event % 8); ++ ++ if (value) { ++ bitmask[event_pos] |= event_bit; ++ } else { ++ bitmask[event_pos] &= ~(event_bit); ++ } ++} ++ ++static inline bool event_bitmask_get(uint8_t *bitmask, unsigned int event) ++{ ++ return (bitmask[event / 8] & (1 << (event % 8))) ? true : false; ++} ++ + enum notification { + NOTIFY_PORT_STATE, ++ NOTIFY_TIME_SYNC, + }; + + #endif +diff --git a/pmc.c b/pmc.c +index 65d1d61..3678800 100644 +--- a/pmc.c ++++ b/pmc.c +@@ -387,9 +387,11 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + sen = (struct subscribe_events_np *) mgt->data; + fprintf(fp, "SUBSCRIBE_EVENTS_NP " + IFMT "duration %hu" +- IFMT "NOTIFY_PORT_STATE %s", ++ IFMT "NOTIFY_PORT_STATE %s" ++ IFMT "NOTIFY_TIME_SYNC %s", + sen->duration, +- (sen->bitmask[0] & 1 << NOTIFY_PORT_STATE) ? "on" : "off"); ++ event_bitmask_get(sen->bitmask, NOTIFY_PORT_STATE) ? "on" : "off", ++ event_bitmask_get(sen->bitmask, NOTIFY_TIME_SYNC) ? "on" : "off"); + break; + case TLV_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) mgt->data; +diff --git a/pmc_agent.c b/pmc_agent.c +index 623f300..37910b3 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -57,7 +57,7 @@ static void send_subscription(struct pmc_agent *node) + + memset(&sen, 0, sizeof(sen)); + sen.duration = PMC_SUBSCRIBE_DURATION; +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ event_bitmask_set(sen.bitmask, NOTIFY_PORT_STATE, TRUE); + pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + +diff --git a/pmc_common.c b/pmc_common.c +index a117904..c5cd992 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -149,7 +149,8 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + struct management_tlv_datum mtd; + struct subscribe_events_np sen; + struct port_ds_np pnp; +- char onoff[4] = {0}; ++ char onoff_port_state[4] = "off"; ++ char onoff_time_status[4] = "off"; + + switch (action) { + case GET: +@@ -223,16 +224,22 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + case TLV_SUBSCRIBE_EVENTS_NP: + memset(&sen, 0, sizeof(sen)); + cnt = sscanf(str, " %*s %*s " +- "duration %hu " +- "NOTIFY_PORT_STATE %3s ", +- &sen.duration, onoff); +- if (cnt != 2) { +- fprintf(stderr, "%s SET needs 2 values\n", ++ "duration %hu " ++ "NOTIFY_PORT_STATE %3s " ++ "NOTIFY_TIME_SYNC %3s ", ++ &sen.duration, ++ onoff_port_state, ++ onoff_time_status); ++ if (cnt != 3) { ++ fprintf(stderr, "%s SET needs 3 values\n", + idtab[index].name); + break; + } +- if (!strcasecmp(onoff, "on")) { +- sen.bitmask[0] = 1 << NOTIFY_PORT_STATE; ++ if (!strcasecmp(onoff_port_state, "on")) { ++ event_bitmask_set(sen.bitmask, NOTIFY_PORT_STATE, TRUE); ++ } ++ if (!strcasecmp(onoff_time_status, "on")) { ++ event_bitmask_set(sen.bitmask, NOTIFY_TIME_SYNC, TRUE); + } + pmc_send_set_action(pmc, code, &sen, sizeof(sen)); + break; +-- +2.25.1 + +From babbe47ab091071e16fcd527bf1aad06e5aec377 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 18:16:31 -0300 +Subject: [PATCH 34/47] clock: Rename UDS variables to read-write. + +In preparation for a new read-only UDS port, rename variables of the +current UDS port to make it clear it is read-write, as opposed to +read-only. + +Signed-off-by: Miroslav Lichvar + +[commit 1b781a5a086571859b0cfba687706d8fdc764d7f upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 52 +++++++++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 23 deletions(-) + +diff --git a/clock.c b/clock.c +index f048771..d653c33 100644 +--- a/clock.c ++++ b/clock.c +@@ -95,7 +95,7 @@ struct clock { + struct foreign_clock *best; + struct ClockIdentity best_id; + LIST_HEAD(ports_head, port) ports; +- struct port *uds_port; ++ struct port *uds_rw_port; + struct pollfd *pollfd; + int pollfd_valid; + int nports; /* does not include the UDS port */ +@@ -129,7 +129,7 @@ struct clock { + struct clock_stats stats; + int stats_interval; + struct clockcheck *sanity_check; +- struct interface *udsif; ++ struct interface *uds_rw_if; + LIST_HEAD(clock_subscribers_head, clock_subscriber) subscribers; + struct monitor *slave_event_monitor; + }; +@@ -243,7 +243,7 @@ static void clock_prune_subscriptions(struct clock *c) + void clock_send_notification(struct clock *c, struct ptp_message *msg, + enum notification event) + { +- struct port *uds = c->uds_port; ++ struct port *uds = c->uds_rw_port; + struct clock_subscriber *s; + + LIST_FOREACH(s, &c->subscribers, list) { +@@ -265,13 +265,13 @@ void clock_destroy(struct clock *c) + { + struct port *p, *tmp; + +- interface_destroy(c->udsif); ++ interface_destroy(c->uds_rw_if); + clock_flush_subscriptions(c); + LIST_FOREACH_SAFE(p, &c->ports, list, tmp) { + clock_remove_port(c, p); + } + monitor_destroy(c->slave_event_monitor); +- port_close(c->uds_port); ++ port_close(c->uds_rw_port); + free(c->pollfd); + if (c->clkid != CLOCK_REALTIME) { + phc_close(c->clkid); +@@ -440,7 +440,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + datalen = sizeof(*gsn); + break; + case TLV_SUBSCRIBE_EVENTS_NP: +- if (p != c->uds_port) { ++ if (p != c->uds_rw_port) { + /* Only the UDS port allowed. */ + break; + } +@@ -782,7 +782,7 @@ static int forwarding(struct clock *c, struct port *p) + default: + break; + } +- if (p == c->uds_port && ps != PS_FAULTY) { ++ if (p == c->uds_rw_port && ps != PS_FAULTY) { + return 1; + } + return 0; +@@ -1042,20 +1042,20 @@ struct clock *clock_create(enum clock_type type, struct config *config, + + /* Configure the UDS. */ + uds_ifname = config_get_string(config, NULL, "uds_address"); +- c->udsif = interface_create(uds_ifname); +- if (config_set_section_int(config, interface_name(c->udsif), ++ c->uds_rw_if = interface_create(uds_ifname); ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "announceReceiptTimeout", 0)) { + return NULL; + } +- if (config_set_section_int(config, interface_name(c->udsif), ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "delay_mechanism", DM_AUTO)) { + return NULL; + } +- if (config_set_section_int(config, interface_name(c->udsif), ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "network_transport", TRANS_UDS)) { + return NULL; + } +- if (config_set_section_int(config, interface_name(c->udsif), ++ if (config_set_section_int(config, interface_name(c->uds_rw_if), + "delay_filter_length", 1)) { + return NULL; + } +@@ -1178,14 +1178,15 @@ struct clock *clock_create(enum clock_type type, struct config *config, + } + + /* Create the UDS interface. */ +- c->uds_port = port_open(phc_device, phc_index, timestamping, 0, c->udsif, c); +- if (!c->uds_port) { ++ c->uds_rw_port = port_open(phc_device, phc_index, timestamping, 0, ++ c->uds_rw_if, c); ++ if (!c->uds_rw_port) { + pr_err("failed to open the UDS port"); + return NULL; + } + clock_fda_changed(c); + +- c->slave_event_monitor = monitor_create(config, c->uds_port); ++ c->slave_event_monitor = monitor_create(config, c->uds_rw_port); + if (!c->slave_event_monitor) { + pr_err("failed to create slave event monitor"); + return NULL; +@@ -1204,7 +1205,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + LIST_FOREACH(p, &c->ports, list) { + port_dispatch(p, EV_INITIALIZE, 0); + } +- port_dispatch(c->uds_port, EV_INITIALIZE, 0); ++ port_dispatch(c->uds_rw_port, EV_INITIALIZE, 0); + + return c; + } +@@ -1312,7 +1313,7 @@ static void clock_check_pollfd(struct clock *c) + clock_fill_pollfd(dest, p); + dest += N_CLOCK_PFD; + } +- clock_fill_pollfd(dest, c->uds_port); ++ clock_fill_pollfd(dest, c->uds_rw_port); + c->pollfd_valid = 1; + } + +@@ -1329,7 +1330,7 @@ static int clock_do_forward_mgmt(struct clock *c, + return 0; + + /* Don't forward any requests to the UDS port. */ +- if (out == c->uds_port) { ++ if (out == c->uds_rw_port) { + switch (management_action(msg)) { + case GET: + case SET: +@@ -1360,7 +1361,7 @@ static void clock_forward_mgmt_msg(struct clock *c, struct port *p, struct ptp_m + pr_err("port %d: management forward failed", + port_number(piter)); + } +- if (clock_do_forward_mgmt(c, p, c->uds_port, msg, &msg_ready)) ++ if (clock_do_forward_mgmt(c, p, c->uds_rw_port, msg, &msg_ready)) + pr_err("uds port: management forward failed"); + if (msg_ready) { + msg_post_recv(msg, pdulen); +@@ -1412,7 +1413,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + clock_management_send_error(p, msg, TLV_WRONG_LENGTH); + return changed; + } +- if (p != c->uds_port) { ++ if (p != c->uds_rw_port) { + /* Sorry, only allowed on the UDS port. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return changed; +@@ -1421,6 +1422,11 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + return changed; + break; + case COMMAND: ++ if (p != c->uds_rw_port) { ++ /* Sorry, only allowed on the UDS port. */ ++ clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ return changed; ++ } + break; + default: + return changed; +@@ -1428,7 +1434,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + + switch (mgt->id) { + case TLV_PORT_PROPERTIES_NP: +- if (p != c->uds_port) { ++ if (p != c->uds_rw_port) { + /* Only the UDS port allowed. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return 0; +@@ -1493,7 +1499,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + + void clock_notify_event(struct clock *c, enum notification event) + { +- struct port *uds = c->uds_port; ++ struct port *uds = c->uds_rw_port; + struct PortIdentity pid = port_identity(uds); + struct ptp_message *msg; + int id; +@@ -1599,7 +1605,7 @@ int clock_poll(struct clock *c) + /* Check the UDS port. */ + for (i = 0; i < N_POLLFD; i++) { + if (cur[i].revents & (POLLIN|POLLPRI)) { +- event = port_event(c->uds_port, i); ++ event = port_event(c->uds_rw_port, i); + if (EV_STATE_DECISION_EVENT == event) { + c->sde = 1; + } +-- +2.25.1 + +From 4af24949b94eda84b4b74d77b9164cf3fe0eccf9 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 18:18:29 -0300 +Subject: [PATCH 35/47] clock: Add read-only UDS port for monitoring. + +Add a second UDS port to allow untrusted applications to monitor ptp4l. +On this "read-only" UDS port disable non-GET actions and forwarding. +The path can be configured with the uds_ro_address option (default is +/var/run/ptp4lro). + +Forwarding is disabled to limit the access to the local ptp4l instance. + +Subscriptions are not enabled to prevent the applications from making a +large number of subscriptions or interfere with applications that have +access to the read-write UDS port. + +Signed-off-by: Miroslav Lichvar + +[commit 6823e077b2466dcc3c7cbce8ab384b0ef9a62811 upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 72 +++++++++++++++++++++++++++++++++++++-------- + config.c | 1 + + configs/default.cfg | 1 + + ptp4l.8 | 6 ++++ + 4 files changed, 67 insertions(+), 13 deletions(-) + +diff --git a/clock.c b/clock.c +index d653c33..869e35d 100644 +--- a/clock.c ++++ b/clock.c +@@ -96,9 +96,10 @@ struct clock { + struct ClockIdentity best_id; + LIST_HEAD(ports_head, port) ports; + struct port *uds_rw_port; ++ struct port *uds_ro_port; + struct pollfd *pollfd; + int pollfd_valid; +- int nports; /* does not include the UDS port */ ++ int nports; /* does not include the two UDS ports */ + int last_port_number; + int sde; + int free_running; +@@ -130,6 +131,7 @@ struct clock { + int stats_interval; + struct clockcheck *sanity_check; + struct interface *uds_rw_if; ++ struct interface *uds_ro_if; + LIST_HEAD(clock_subscribers_head, clock_subscriber) subscribers; + struct monitor *slave_event_monitor; + }; +@@ -266,12 +268,14 @@ void clock_destroy(struct clock *c) + struct port *p, *tmp; + + interface_destroy(c->uds_rw_if); ++ interface_destroy(c->uds_ro_if); + clock_flush_subscriptions(c); + LIST_FOREACH_SAFE(p, &c->ports, list, tmp) { + clock_remove_port(c, p); + } + monitor_destroy(c->slave_event_monitor); + port_close(c->uds_rw_port); ++ port_close(c->uds_ro_port); + free(c->pollfd); + if (c->clkid != CLOCK_REALTIME) { + phc_close(c->clkid); +@@ -441,7 +445,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + break; + case TLV_SUBSCRIBE_EVENTS_NP: + if (p != c->uds_rw_port) { +- /* Only the UDS port allowed. */ ++ /* Only the UDS-RW port allowed. */ + break; + } + sen = (struct subscribe_events_np *)tlv->data; +@@ -772,6 +776,10 @@ static int clock_utc_correct(struct clock *c, tmv_t ingress) + static int forwarding(struct clock *c, struct port *p) + { + enum port_state ps = port_state(p); ++ ++ if (p == c->uds_ro_port) ++ return 0; ++ + switch (ps) { + case PS_MASTER: + case PS_GRAND_MASTER: +@@ -816,7 +824,7 @@ static int clock_add_port(struct clock *c, const char *phc_device, + { + struct port *p, *piter, *lastp = NULL; + +- if (clock_resize_pollfd(c, c->nports + 1)) { ++ if (clock_resize_pollfd(c, c->nports + 2)) { + return -1; + } + p = port_open(phc_device, phc_index, timestamping, +@@ -1041,6 +1049,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + } + + /* Configure the UDS. */ ++ + uds_ifname = config_get_string(config, NULL, "uds_address"); + c->uds_rw_if = interface_create(uds_ifname); + if (config_set_section_int(config, interface_name(c->uds_rw_if), +@@ -1060,6 +1069,25 @@ struct clock *clock_create(enum clock_type type, struct config *config, + return NULL; + } + ++ uds_ifname = config_get_string(config, NULL, "uds_ro_address"); ++ c->uds_ro_if = interface_create(uds_ifname); ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "announceReceiptTimeout", 0)) { ++ return NULL; ++ } ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "delay_mechanism", DM_AUTO)) { ++ return NULL; ++ } ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "network_transport", TRANS_UDS)) { ++ return NULL; ++ } ++ if (config_set_section_int(config, interface_name(c->uds_ro_if), ++ "delay_filter_length", 1)) { ++ return NULL; ++ } ++ + c->config = config; + c->free_running = config_get_int(config, NULL, "free_running"); + c->freq_est_interval = config_get_int(config, NULL, "freq_est_interval"); +@@ -1177,11 +1205,18 @@ struct clock *clock_create(enum clock_type type, struct config *config, + return NULL; + } + +- /* Create the UDS interface. */ ++ /* Create the UDS interfaces. */ ++ + c->uds_rw_port = port_open(phc_device, phc_index, timestamping, 0, + c->uds_rw_if, c); + if (!c->uds_rw_port) { +- pr_err("failed to open the UDS port"); ++ pr_err("failed to open the UDS-RW port"); ++ return NULL; ++ } ++ c->uds_ro_port = port_open(phc_device, phc_index, timestamping, 0, ++ c->uds_ro_if, c); ++ if (!c->uds_ro_port) { ++ pr_err("failed to open the UDS-RO port"); + return NULL; + } + clock_fda_changed(c); +@@ -1206,6 +1241,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, + port_dispatch(p, EV_INITIALIZE, 0); + } + port_dispatch(c->uds_rw_port, EV_INITIALIZE, 0); ++ port_dispatch(c->uds_ro_port, EV_INITIALIZE, 0); + + return c; + } +@@ -1276,9 +1312,9 @@ static int clock_resize_pollfd(struct clock *c, int new_nports) + { + struct pollfd *new_pollfd; + +- /* Need to allocate one whole extra block of fds for UDS. */ ++ /* Need to allocate two whole extra blocks of fds for UDS ports. */ + new_pollfd = realloc(c->pollfd, +- (new_nports + 1) * N_CLOCK_PFD * ++ (new_nports + 2) * N_CLOCK_PFD * + sizeof(struct pollfd)); + if (!new_pollfd) { + return -1; +@@ -1314,6 +1350,8 @@ static void clock_check_pollfd(struct clock *c) + dest += N_CLOCK_PFD; + } + clock_fill_pollfd(dest, c->uds_rw_port); ++ dest += N_CLOCK_PFD; ++ clock_fill_pollfd(dest, c->uds_ro_port); + c->pollfd_valid = 1; + } + +@@ -1329,7 +1367,8 @@ static int clock_do_forward_mgmt(struct clock *c, + if (in == out || !forwarding(c, out)) + return 0; + +- /* Don't forward any requests to the UDS port. */ ++ /* Don't forward any requests to the UDS-RW port ++ (the UDS-RO port doesn't allow any forwarding). */ + if (out == c->uds_rw_port) { + switch (management_action(msg)) { + case GET: +@@ -1414,7 +1453,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + return changed; + } + if (p != c->uds_rw_port) { +- /* Sorry, only allowed on the UDS port. */ ++ /* Sorry, only allowed on the UDS-RW port. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return changed; + } +@@ -1423,7 +1462,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + break; + case COMMAND: + if (p != c->uds_rw_port) { +- /* Sorry, only allowed on the UDS port. */ ++ /* Sorry, only allowed on the UDS-RW port. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return changed; + } +@@ -1435,7 +1474,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + switch (mgt->id) { + case TLV_PORT_PROPERTIES_NP: + if (p != c->uds_rw_port) { +- /* Only the UDS port allowed. */ ++ /* Only the UDS-RW port allowed. */ + clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); + return 0; + } +@@ -1548,7 +1587,7 @@ int clock_poll(struct clock *c) + struct port *p; + + clock_check_pollfd(c); +- cnt = poll(c->pollfd, (c->nports + 1) * N_CLOCK_PFD, -1); ++ cnt = poll(c->pollfd, (c->nports + 2) * N_CLOCK_PFD, -1); + if (cnt < 0) { + if (EINTR == errno) { + return 0; +@@ -1602,7 +1641,7 @@ int clock_poll(struct clock *c) + cur += N_CLOCK_PFD; + } + +- /* Check the UDS port. */ ++ /* Check the UDS ports. */ + for (i = 0; i < N_POLLFD; i++) { + if (cur[i].revents & (POLLIN|POLLPRI)) { + event = port_event(c->uds_rw_port, i); +@@ -1611,6 +1650,13 @@ int clock_poll(struct clock *c) + } + } + } ++ cur += N_CLOCK_PFD; ++ for (i = 0; i < N_POLLFD; i++) { ++ if (cur[i].revents & (POLLIN|POLLPRI)) { ++ event = port_event(c->uds_ro_port, i); ++ /* sde is not expected on the UDS-RO port */ ++ } ++ } + + if (c->sde) { + handle_state_decision_event(c); +diff --git a/config.c b/config.c +index fea7f67..d45e948 100644 +--- a/config.c ++++ b/config.c +@@ -323,6 +323,7 @@ struct config_item config_tab[] = { + PORT_ITEM_INT("udp_ttl", 1, 1, 255), + PORT_ITEM_INT("udp6_scope", 0x0E, 0x00, 0x0F), + GLOB_ITEM_STR("uds_address", "/var/run/ptp4l"), ++ GLOB_ITEM_STR("uds_ro_address", "/var/run/ptp4lro"), + PORT_ITEM_INT("unicast_listen", 0, 0, 1), + PORT_ITEM_INT("unicast_master_table", 0, 0, INT_MAX), + PORT_ITEM_INT("unicast_req_duration", 3600, 10, INT_MAX), +diff --git a/configs/default.cfg b/configs/default.cfg +index 8c19129..d5bab7d 100644 +--- a/configs/default.cfg ++++ b/configs/default.cfg +@@ -90,6 +90,7 @@ p2p_dst_mac 01:80:C2:00:00:0E + udp_ttl 1 + udp6_scope 0x0E + uds_address /var/run/ptp4l ++uds_ro_address /var/run/ptp4lro + # + # Default interface options + # +diff --git a/ptp4l.8 b/ptp4l.8 +index b179b81..f9bd228 100644 +--- a/ptp4l.8 ++++ b/ptp4l.8 +@@ -615,6 +615,12 @@ is only relevant with IPv6 transport. See RFC 4291. The default is + Specifies the address of the UNIX domain socket for receiving local + management messages. The default is /var/run/ptp4l. + .TP ++.B uds_ro_address ++Specifies the address of the second UNIX domain socket for receiving local ++management messages, which is restricted to GET actions and does not forward ++messages to other ports. Access to this socket can be given to untrusted ++applications for monitoring purposes. The default is /var/run/ptp4lro. ++.TP + .B dscp_event + Defines the Differentiated Services Codepoint (DSCP) to be used for PTP + event messages. Must be a value between 0 and 63. There are several media +-- +2.25.1 + +From 019f50868bc4300c591025d364898035ea9817b9 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 12 Jun 2023 18:20:50 -0300 +Subject: [PATCH 36/47] Rename management ID macros. + +The management ID macros are prefixed with TLV. This is confusing, +because the true TLV tags have the same prefix. Make the code more +readable by using an appropriate prefix the for management IDs. + +Signed-off-by: Richard Cochran + +[commit d86eaa157a0af7d807fc44ce6d91c34084e6902f upstream] +Signed-off-by: Andre Mauricio Zelak +--- + clock.c | 130 +++++++++++++++++++------------------- + phc2sys.c | 2 +- + pmc.c | 61 +++++++++--------- + pmc_agent.c | 14 ++--- + pmc_common.c | 172 ++++++++++++++++++++++++++------------------------- + port.c | 72 +++++++++++---------- + tlv.c | 62 +++++++++---------- + tlv.h | 132 +++++++++++++++++++-------------------- + 8 files changed, 329 insertions(+), 316 deletions(-) + +diff --git a/clock.c b/clock.c +index 869e35d..534b7e1 100644 +--- a/clock.c ++++ b/clock.c +@@ -361,64 +361,64 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + tlv->id = id; + + switch (id) { +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + text = (struct PTPText *) tlv->data; + text->length = c->desc.userDescription.length; + memcpy(text->text, c->desc.userDescription.text, text->length); + datalen = 1 + text->length; + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + memcpy(tlv->data, &c->dds, sizeof(c->dds)); + datalen = sizeof(c->dds); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + memcpy(tlv->data, &c->cur, sizeof(c->cur)); + datalen = sizeof(c->cur); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + memcpy(tlv->data, &c->dad.pds, sizeof(c->dad.pds)); + datalen = sizeof(c->dad.pds); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + memcpy(tlv->data, &c->tds, sizeof(c->tds)); + datalen = sizeof(c->tds); + break; +- case TLV_PRIORITY1: ++ case MID_PRIORITY1: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.priority1; + datalen = sizeof(*mtd); + break; +- case TLV_PRIORITY2: ++ case MID_PRIORITY2: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.priority2; + datalen = sizeof(*mtd); + break; +- case TLV_DOMAIN: ++ case MID_DOMAIN: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.domainNumber; + datalen = sizeof(*mtd); + break; +- case TLV_SLAVE_ONLY: ++ case MID_SLAVE_ONLY: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.flags & DDS_SLAVE_ONLY; + datalen = sizeof(*mtd); + break; +- case TLV_CLOCK_ACCURACY: ++ case MID_CLOCK_ACCURACY: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->dds.clockQuality.clockAccuracy; + datalen = sizeof(*mtd); + break; +- case TLV_TRACEABILITY_PROPERTIES: ++ case MID_TRACEABILITY_PROPERTIES: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->tds.flags & (TIME_TRACEABLE|FREQ_TRACEABLE); + datalen = sizeof(*mtd); + break; +- case TLV_TIMESCALE_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->tds.flags & PTP_TIMESCALE; + datalen = sizeof(*mtd); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + tsn = (struct time_status_np *) tlv->data; + tsn->master_offset = tmv_to_nanoseconds(c->master_offset); + tsn->ingress_time = tmv_to_nanoseconds(c->ingress_ts); +@@ -435,7 +435,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + tsn->gmIdentity = c->dad.pds.grandmasterIdentity; + datalen = sizeof(*tsn); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) tlv->data; + gsn->clockQuality = c->dds.clockQuality; + gsn->utc_offset = c->utc_offset; +@@ -443,7 +443,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + gsn->time_source = c->time_source; + datalen = sizeof(*gsn); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + if (p != c->uds_rw_port) { + /* Only the UDS-RW port allowed. */ + break; +@@ -452,7 +452,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + clock_get_subscription(c, req, sen->bitmask, &sen->duration); + datalen = sizeof(*sen); + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = c->local_sync_uncertain; + datalen = sizeof(*mtd); +@@ -504,19 +504,19 @@ static int clock_management_set(struct clock *c, struct port *p, + tlv = (struct management_tlv *) req->management.suffix; + + switch (id) { +- case TLV_PRIORITY1: ++ case MID_PRIORITY1: + mtd = (struct management_tlv_datum *) tlv->data; + c->dds.priority1 = mtd->val; + *changed = 1; + respond = 1; + break; +- case TLV_PRIORITY2: ++ case MID_PRIORITY2: + mtd = (struct management_tlv_datum *) tlv->data; + c->dds.priority2 = mtd->val; + *changed = 1; + respond = 1; + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) tlv->data; + c->dds.clockQuality = gsn->clockQuality; + c->utc_offset = gsn->utc_offset; +@@ -525,12 +525,12 @@ static int clock_management_set(struct clock *c, struct port *p, + *changed = 1; + respond = 1; + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + sen = (struct subscribe_events_np *)tlv->data; + clock_update_subscription(c, req, sen->bitmask, sen->duration); + respond = 1; + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) tlv->data; + switch (mtd->val) { + case SYNC_UNCERTAIN_DONTCARE: +@@ -1448,13 +1448,13 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + return changed; + break; + case SET: +- if (mgt->length == 2 && mgt->id != TLV_NULL_MANAGEMENT) { +- clock_management_send_error(p, msg, TLV_WRONG_LENGTH); ++ if (mgt->length == 2 && mgt->id != MID_NULL_MANAGEMENT) { ++ clock_management_send_error(p, msg, MID_WRONG_LENGTH); + return changed; + } + if (p != c->uds_rw_port) { + /* Sorry, only allowed on the UDS-RW port. */ +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + return changed; + } + if (clock_management_set(c, p, mgt->id, msg, &changed)) +@@ -1463,7 +1463,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + case COMMAND: + if (p != c->uds_rw_port) { + /* Sorry, only allowed on the UDS-RW port. */ +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + return changed; + } + break; +@@ -1472,50 +1472,50 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + } + + switch (mgt->id) { +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + if (p != c->uds_rw_port) { + /* Only the UDS-RW port allowed. */ +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + return 0; + } + } + + switch (mgt->id) { +- case TLV_USER_DESCRIPTION: +- case TLV_SAVE_IN_NON_VOLATILE_STORAGE: +- case TLV_RESET_NON_VOLATILE_STORAGE: +- case TLV_INITIALIZE: +- case TLV_FAULT_LOG: +- case TLV_FAULT_LOG_RESET: +- case TLV_DEFAULT_DATA_SET: +- case TLV_CURRENT_DATA_SET: +- case TLV_PARENT_DATA_SET: +- case TLV_TIME_PROPERTIES_DATA_SET: +- case TLV_PRIORITY1: +- case TLV_PRIORITY2: +- case TLV_DOMAIN: +- case TLV_SLAVE_ONLY: +- case TLV_TIME: +- case TLV_CLOCK_ACCURACY: +- case TLV_UTC_PROPERTIES: +- case TLV_TRACEABILITY_PROPERTIES: +- case TLV_TIMESCALE_PROPERTIES: +- case TLV_PATH_TRACE_LIST: +- case TLV_PATH_TRACE_ENABLE: +- case TLV_GRANDMASTER_CLUSTER_TABLE: +- case TLV_ACCEPTABLE_MASTER_TABLE: +- case TLV_ACCEPTABLE_MASTER_MAX_TABLE_SIZE: +- case TLV_ALTERNATE_TIME_OFFSET_ENABLE: +- case TLV_ALTERNATE_TIME_OFFSET_NAME: +- case TLV_ALTERNATE_TIME_OFFSET_MAX_KEY: +- case TLV_ALTERNATE_TIME_OFFSET_PROPERTIES: +- case TLV_TRANSPARENT_CLOCK_DEFAULT_DATA_SET: +- case TLV_PRIMARY_DOMAIN: +- case TLV_TIME_STATUS_NP: +- case TLV_GRANDMASTER_SETTINGS_NP: +- case TLV_SUBSCRIBE_EVENTS_NP: +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: +- clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); ++ case MID_USER_DESCRIPTION: ++ case MID_SAVE_IN_NON_VOLATILE_STORAGE: ++ case MID_RESET_NON_VOLATILE_STORAGE: ++ case MID_INITIALIZE: ++ case MID_FAULT_LOG: ++ case MID_FAULT_LOG_RESET: ++ case MID_DEFAULT_DATA_SET: ++ case MID_CURRENT_DATA_SET: ++ case MID_PARENT_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: ++ case MID_PRIORITY1: ++ case MID_PRIORITY2: ++ case MID_DOMAIN: ++ case MID_SLAVE_ONLY: ++ case MID_TIME: ++ case MID_CLOCK_ACCURACY: ++ case MID_UTC_PROPERTIES: ++ case MID_TRACEABILITY_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: ++ case MID_PATH_TRACE_LIST: ++ case MID_PATH_TRACE_ENABLE: ++ case MID_GRANDMASTER_CLUSTER_TABLE: ++ case MID_ACCEPTABLE_MASTER_TABLE: ++ case MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE: ++ case MID_ALTERNATE_TIME_OFFSET_ENABLE: ++ case MID_ALTERNATE_TIME_OFFSET_NAME: ++ case MID_ALTERNATE_TIME_OFFSET_MAX_KEY: ++ case MID_ALTERNATE_TIME_OFFSET_PROPERTIES: ++ case MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET: ++ case MID_PRIMARY_DOMAIN: ++ case MID_TIME_STATUS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: ++ clock_management_send_error(p, msg, MID_NOT_SUPPORTED); + break; + default: + answers = 0; +@@ -1528,8 +1528,8 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) + } + if (!answers) { + /* IEEE 1588 Interpretation #21 suggests to use +- * TLV_WRONG_VALUE for ports that do not exist */ +- clock_management_send_error(p, msg, TLV_WRONG_VALUE); ++ * MID_WRONG_VALUE for ports that do not exist */ ++ clock_management_send_error(p, msg, MID_WRONG_VALUE); + } + break; + } +@@ -1545,7 +1545,7 @@ void clock_notify_event(struct clock *c, enum notification event) + + switch (event) { + case NOTIFY_TIME_SYNC: +- id = TLV_TIME_STATUS_NP; ++ id = MID_TIME_STATUS_NP; + break; + default: + return; +diff --git a/phc2sys.c b/phc2sys.c +index 569544e..c9fabd7 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -827,7 +827,7 @@ static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, + if (mgt_id == excluded) + return 0; + switch (mgt_id) { +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + pds = management_tlv_data(msg); + port = port_get(priv, pds->portIdentity.portNumber); + if (!port) { +diff --git a/pmc.c b/pmc.c +index 3678800..0881178 100644 +--- a/pmc.c ++++ b/pmc.c +@@ -186,12 +186,12 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + goto out; + } + mgt = (struct management_tlv *) msg->management.suffix; +- if (mgt->length == 2 && mgt->id != TLV_NULL_MANAGEMENT) { ++ if (mgt->length == 2 && mgt->id != MID_NULL_MANAGEMENT) { + fprintf(fp, "empty-tlv "); + goto out; + } + switch (mgt->id) { +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + cd = &extra->cd; + fprintf(fp, "CLOCK_DESCRIPTION " + IFMT "clockType 0x%hx" +@@ -215,12 +215,12 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + text2str(cd->userDescription), + bin2str(cd->profileIdentity, PROFILE_ID_LEN)); + break; +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + fprintf(fp, "USER_DESCRIPTION " + IFMT "userDescription %s", + text2str(extra->cd.userDescription)); + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + dds = (struct defaultDS *) mgt->data; + fprintf(fp, "DEFAULT_DATA_SET " + IFMT "twoStepFlag %d" +@@ -244,7 +244,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + cid2str(&dds->clockIdentity), + dds->domainNumber); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + cds = (struct currentDS *) mgt->data; + fprintf(fp, "CURRENT_DATA_SET " + IFMT "stepsRemoved %hd" +@@ -253,7 +253,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + cds->stepsRemoved, cds->offsetFromMaster / 65536.0, + cds->meanPathDelay / 65536.0); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + pds = (struct parentDS *) mgt->data; + fprintf(fp, "PARENT_DATA_SET " + IFMT "parentPortIdentity %s" +@@ -277,7 +277,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + pds->grandmasterPriority2, + cid2str(&pds->grandmasterIdentity)); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + tp = (struct timePropertiesDS *) mgt->data; + fprintf(fp, "TIME_PROPERTIES_DATA_SET " + IFMT "currentUtcOffset %hd" +@@ -297,32 +297,32 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + tp->flags & FREQ_TRACEABLE ? 1 : 0, + tp->timeSource); + break; +- case TLV_PRIORITY1: ++ case MID_PRIORITY1: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "PRIORITY1 " + IFMT "priority1 %hhu", mtd->val); + break; +- case TLV_PRIORITY2: ++ case MID_PRIORITY2: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "PRIORITY2 " + IFMT "priority2 %hhu", mtd->val); + break; +- case TLV_DOMAIN: ++ case MID_DOMAIN: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "DOMAIN " + IFMT "domainNumber %hhu", mtd->val); + break; +- case TLV_SLAVE_ONLY: ++ case MID_SLAVE_ONLY: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "SLAVE_ONLY " + IFMT "slaveOnly %d", mtd->val & DDS_SLAVE_ONLY ? 1 : 0); + break; +- case TLV_CLOCK_ACCURACY: ++ case MID_CLOCK_ACCURACY: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "CLOCK_ACCURACY " + IFMT "clockAccuracy 0x%02hhx", mtd->val); + break; +- case TLV_TRACEABILITY_PROPERTIES: ++ case MID_TRACEABILITY_PROPERTIES: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "TRACEABILITY_PROPERTIES " + IFMT "timeTraceable %d" +@@ -330,12 +330,17 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + mtd->val & TIME_TRACEABLE ? 1 : 0, + mtd->val & FREQ_TRACEABLE ? 1 : 0); + break; +- case TLV_TIMESCALE_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "TIMESCALE_PROPERTIES " + IFMT "ptpTimescale %d", mtd->val & PTP_TIMESCALE ? 1 : 0); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_MASTER_ONLY: ++ mtd = (struct management_tlv_datum *) mgt->data; ++ fprintf(fp, "MASTER_ONLY " ++ IFMT "masterOnly %d", mtd->val); ++ break; ++ case MID_TIME_STATUS_NP: + tsn = (struct time_status_np *) mgt->data; + fprintf(fp, "TIME_STATUS_NP " + IFMT "master_offset %" PRId64 +@@ -357,7 +362,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + tsn->gmPresent ? "true" : "false", + cid2str(&tsn->gmIdentity)); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) mgt->data; + fprintf(fp, "GRANDMASTER_SETTINGS_NP " + IFMT "clockClass %hhu" +@@ -383,7 +388,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + gsn->time_flags & FREQ_TRACEABLE ? 1 : 0, + gsn->time_source); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + sen = (struct subscribe_events_np *) mgt->data; + fprintf(fp, "SUBSCRIBE_EVENTS_NP " + IFMT "duration %hu" +@@ -393,12 +398,12 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + event_bitmask_get(sen->bitmask, NOTIFY_PORT_STATE) ? "on" : "off", + event_bitmask_get(sen->bitmask, NOTIFY_TIME_SYNC) ? "on" : "off"); + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "SYNCHRONIZATION_UNCERTAIN_NP " + IFMT "uncertain %hhu", mtd->val); + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + p = (struct portDS *) mgt->data; + if (p->portState > PS_SLAVE) { + p->portState = 0; +@@ -420,7 +425,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + p->logSyncInterval, p->delayMechanism, + p->logMinPdelayReqInterval, p->versionNumber); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pnp = (struct port_ds_np *) mgt->data; + fprintf(fp, "PORT_DATA_SET_NP " + IFMT "neighborPropDelayThresh %u" +@@ -428,7 +433,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + pnp->neighborPropDelayThresh, + pnp->asCapable ? 1 : 0); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + ppn = (struct port_properties_np *) mgt->data; + if (ppn->port_state > PS_SLAVE) { + ppn->port_state = 0; +@@ -443,7 +448,7 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + ts_str(ppn->timestamping), + text2str(&ppn->interface)); + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + pcp = (struct port_stats_np *) mgt->data; + fprintf(fp, "PORT_STATS_NP " + IFMT "portIdentity %s" +@@ -489,32 +494,32 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + pcp->stats.txMsgType[SIGNALING], + pcp->stats.txMsgType[MANAGEMENT]); + break; +- case TLV_LOG_ANNOUNCE_INTERVAL: ++ case MID_LOG_ANNOUNCE_INTERVAL: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "LOG_ANNOUNCE_INTERVAL " + IFMT "logAnnounceInterval %hhd", mtd->val); + break; +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "ANNOUNCE_RECEIPT_TIMEOUT " + IFMT "announceReceiptTimeout %hhu", mtd->val); + break; +- case TLV_LOG_SYNC_INTERVAL: ++ case MID_LOG_SYNC_INTERVAL: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "LOG_SYNC_INTERVAL " + IFMT "logSyncInterval %hhd", mtd->val); + break; +- case TLV_VERSION_NUMBER: ++ case MID_VERSION_NUMBER: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "VERSION_NUMBER " + IFMT "versionNumber %hhu", mtd->val); + break; +- case TLV_DELAY_MECHANISM: ++ case MID_DELAY_MECHANISM: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "DELAY_MECHANISM " + IFMT "delayMechanism %hhu", mtd->val); + break; +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: + mtd = (struct management_tlv_datum *) mgt->data; + fprintf(fp, "LOG_MIN_PDELAY_REQ_INTERVAL " + IFMT "logMinPdelayReqInterval %hhd", mtd->val); +diff --git a/pmc_agent.c b/pmc_agent.c +index 37910b3..3034f65 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -58,7 +58,7 @@ static void send_subscription(struct pmc_agent *node) + memset(&sen, 0, sizeof(sen)); + sen.duration = PMC_SUBSCRIBE_DURATION; + event_bitmask_set(sen.bitmask, NOTIFY_PORT_STATE, TRUE); +- pmc_send_set_action(node->pmc, TLV_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); ++ pmc_send_set_action(node->pmc, MID_SUBSCRIBE_EVENTS_NP, &sen, sizeof(sen)); + } + + static int check_clock_identity(struct pmc_agent *node, struct ptp_message *msg) +@@ -149,7 +149,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + if ((pollfd[0].revents & POLLOUT) && + !(pollfd[0].revents & (POLLIN|POLLPRI))) { + switch (ds_id) { +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + send_subscription(node); + break; + default: +@@ -195,7 +195,7 @@ static int renew_subscription(struct pmc_agent *node, int timeout) + struct ptp_message *msg; + int res; + +- res = run_pmc(node, timeout, TLV_SUBSCRIBE_EVENTS_NP, &msg); ++ res = run_pmc(node, timeout, MID_SUBSCRIBE_EVENTS_NP, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } +@@ -211,7 +211,7 @@ int run_pmc_wait_sync(struct pmc_agent *node, int timeout) + int res; + + while (1) { +- res = run_pmc(node, timeout, TLV_PORT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, MID_PORT_DATA_SET, &msg); + if (res <= 0) + return res; + +@@ -298,7 +298,7 @@ int pmc_agent_query_dds(struct pmc_agent *node, int timeout) + struct defaultDS *dds; + int res; + +- res = run_pmc(node, timeout, TLV_DEFAULT_DATA_SET, &msg); ++ res = run_pmc(node, timeout, MID_DEFAULT_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } +@@ -319,7 +319,7 @@ int pmc_agent_query_port_properties(struct pmc_agent *node, int timeout, + + pmc_target_port(node->pmc, port); + while (1) { +- res = run_pmc(node, timeout, TLV_PORT_PROPERTIES_NP, &msg); ++ res = run_pmc(node, timeout, MID_PORT_PROPERTIES_NP, &msg); + if (is_run_pmc_error(res)) { + goto out; + } +@@ -352,7 +352,7 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + struct ptp_message *msg; + int res; + +- res = run_pmc(node, timeout, TLV_TIME_PROPERTIES_DATA_SET, &msg); ++ res = run_pmc(node, timeout, MID_TIME_PROPERTIES_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } +diff --git a/pmc_common.c b/pmc_common.c +index c5cd992..7a1dbb4 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -76,61 +76,62 @@ struct management_id { + + struct management_id idtab[] = { + /* Clock management ID values */ +- { "USER_DESCRIPTION", TLV_USER_DESCRIPTION, do_get_action }, +- { "SAVE_IN_NON_VOLATILE_STORAGE", TLV_SAVE_IN_NON_VOLATILE_STORAGE, not_supported }, +- { "RESET_NON_VOLATILE_STORAGE", TLV_RESET_NON_VOLATILE_STORAGE, not_supported }, +- { "INITIALIZE", TLV_INITIALIZE, not_supported }, +- { "FAULT_LOG", TLV_FAULT_LOG, not_supported }, +- { "FAULT_LOG_RESET", TLV_FAULT_LOG_RESET, not_supported }, +- { "DEFAULT_DATA_SET", TLV_DEFAULT_DATA_SET, do_get_action }, +- { "CURRENT_DATA_SET", TLV_CURRENT_DATA_SET, do_get_action }, +- { "PARENT_DATA_SET", TLV_PARENT_DATA_SET, do_get_action }, +- { "TIME_PROPERTIES_DATA_SET", TLV_TIME_PROPERTIES_DATA_SET, do_get_action }, +- { "PRIORITY1", TLV_PRIORITY1, do_set_action }, +- { "PRIORITY2", TLV_PRIORITY2, do_set_action }, +- { "DOMAIN", TLV_DOMAIN, do_get_action }, +- { "SLAVE_ONLY", TLV_SLAVE_ONLY, do_get_action }, +- { "TIME", TLV_TIME, not_supported }, +- { "CLOCK_ACCURACY", TLV_CLOCK_ACCURACY, do_get_action }, +- { "UTC_PROPERTIES", TLV_UTC_PROPERTIES, not_supported }, +- { "TRACEABILITY_PROPERTIES", TLV_TRACEABILITY_PROPERTIES, do_get_action }, +- { "TIMESCALE_PROPERTIES", TLV_TIMESCALE_PROPERTIES, do_get_action }, +- { "PATH_TRACE_LIST", TLV_PATH_TRACE_LIST, not_supported }, +- { "PATH_TRACE_ENABLE", TLV_PATH_TRACE_ENABLE, not_supported }, +- { "GRANDMASTER_CLUSTER_TABLE", TLV_GRANDMASTER_CLUSTER_TABLE, not_supported }, +- { "ACCEPTABLE_MASTER_TABLE", TLV_ACCEPTABLE_MASTER_TABLE, not_supported }, +- { "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", TLV_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported }, +- { "ALTERNATE_TIME_OFFSET_ENABLE", TLV_ALTERNATE_TIME_OFFSET_ENABLE, not_supported }, +- { "ALTERNATE_TIME_OFFSET_NAME", TLV_ALTERNATE_TIME_OFFSET_NAME, not_supported }, +- { "ALTERNATE_TIME_OFFSET_MAX_KEY", TLV_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported }, +- { "ALTERNATE_TIME_OFFSET_PROPERTIES", TLV_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported }, +- { "TRANSPARENT_CLOCK_DEFAULT_DATA_SET", TLV_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported }, +- { "PRIMARY_DOMAIN", TLV_PRIMARY_DOMAIN, not_supported }, +- { "TIME_STATUS_NP", TLV_TIME_STATUS_NP, do_get_action }, +- { "GRANDMASTER_SETTINGS_NP", TLV_GRANDMASTER_SETTINGS_NP, do_set_action }, +- { "SUBSCRIBE_EVENTS_NP", TLV_SUBSCRIBE_EVENTS_NP, do_set_action }, +- { "SYNCHRONIZATION_UNCERTAIN_NP", TLV_SYNCHRONIZATION_UNCERTAIN_NP, do_set_action }, ++ { "USER_DESCRIPTION", MID_USER_DESCRIPTION, do_get_action }, ++ { "SAVE_IN_NON_VOLATILE_STORAGE", MID_SAVE_IN_NON_VOLATILE_STORAGE, not_supported }, ++ { "RESET_NON_VOLATILE_STORAGE", MID_RESET_NON_VOLATILE_STORAGE, not_supported }, ++ { "INITIALIZE", MID_INITIALIZE, not_supported }, ++ { "FAULT_LOG", MID_FAULT_LOG, not_supported }, ++ { "FAULT_LOG_RESET", MID_FAULT_LOG_RESET, not_supported }, ++ { "DEFAULT_DATA_SET", MID_DEFAULT_DATA_SET, do_get_action }, ++ { "CURRENT_DATA_SET", MID_CURRENT_DATA_SET, do_get_action }, ++ { "PARENT_DATA_SET", MID_PARENT_DATA_SET, do_get_action }, ++ { "TIME_PROPERTIES_DATA_SET", MID_TIME_PROPERTIES_DATA_SET, do_get_action }, ++ { "PRIORITY1", MID_PRIORITY1, do_set_action }, ++ { "PRIORITY2", MID_PRIORITY2, do_set_action }, ++ { "DOMAIN", MID_DOMAIN, do_get_action }, ++ { "SLAVE_ONLY", MID_SLAVE_ONLY, do_get_action }, ++ { "TIME", MID_TIME, not_supported }, ++ { "CLOCK_ACCURACY", MID_CLOCK_ACCURACY, do_get_action }, ++ { "UTC_PROPERTIES", MID_UTC_PROPERTIES, not_supported }, ++ { "TRACEABILITY_PROPERTIES", MID_TRACEABILITY_PROPERTIES, do_get_action }, ++ { "TIMESCALE_PROPERTIES", MID_TIMESCALE_PROPERTIES, do_get_action }, ++ { "PATH_TRACE_LIST", MID_PATH_TRACE_LIST, not_supported }, ++ { "PATH_TRACE_ENABLE", MID_PATH_TRACE_ENABLE, not_supported }, ++ { "GRANDMASTER_CLUSTER_TABLE", MID_GRANDMASTER_CLUSTER_TABLE, not_supported }, ++ { "ACCEPTABLE_MASTER_TABLE", MID_ACCEPTABLE_MASTER_TABLE, not_supported }, ++ { "ACCEPTABLE_MASTER_MAX_TABLE_SIZE", MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_ENABLE", MID_ALTERNATE_TIME_OFFSET_ENABLE, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_NAME", MID_ALTERNATE_TIME_OFFSET_NAME, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_MAX_KEY", MID_ALTERNATE_TIME_OFFSET_MAX_KEY, not_supported }, ++ { "ALTERNATE_TIME_OFFSET_PROPERTIES", MID_ALTERNATE_TIME_OFFSET_PROPERTIES, not_supported }, ++ { "MASTER_ONLY", MID_MASTER_ONLY, do_get_action }, ++ { "TRANSPARENT_CLOCK_DEFAULT_DATA_SET", MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET, not_supported }, ++ { "PRIMARY_DOMAIN", MID_PRIMARY_DOMAIN, not_supported }, ++ { "TIME_STATUS_NP", MID_TIME_STATUS_NP, do_get_action }, ++ { "GRANDMASTER_SETTINGS_NP", MID_GRANDMASTER_SETTINGS_NP, do_set_action }, ++ { "SUBSCRIBE_EVENTS_NP", MID_SUBSCRIBE_EVENTS_NP, do_set_action }, ++ { "SYNCHRONIZATION_UNCERTAIN_NP", MID_SYNCHRONIZATION_UNCERTAIN_NP, do_set_action }, + /* Port management ID values */ +- { "NULL_MANAGEMENT", TLV_NULL_MANAGEMENT, null_management }, +- { "CLOCK_DESCRIPTION", TLV_CLOCK_DESCRIPTION, do_get_action }, +- { "PORT_DATA_SET", TLV_PORT_DATA_SET, do_get_action }, +- { "LOG_ANNOUNCE_INTERVAL", TLV_LOG_ANNOUNCE_INTERVAL, do_get_action }, +- { "ANNOUNCE_RECEIPT_TIMEOUT", TLV_ANNOUNCE_RECEIPT_TIMEOUT, do_get_action }, +- { "LOG_SYNC_INTERVAL", TLV_LOG_SYNC_INTERVAL, do_get_action }, +- { "VERSION_NUMBER", TLV_VERSION_NUMBER, do_get_action }, +- { "ENABLE_PORT", TLV_ENABLE_PORT, not_supported }, +- { "DISABLE_PORT", TLV_DISABLE_PORT, not_supported }, +- { "UNICAST_NEGOTIATION_ENABLE", TLV_UNICAST_NEGOTIATION_ENABLE, not_supported }, +- { "UNICAST_MASTER_TABLE", TLV_UNICAST_MASTER_TABLE, not_supported }, +- { "UNICAST_MASTER_MAX_TABLE_SIZE", TLV_UNICAST_MASTER_MAX_TABLE_SIZE, not_supported }, +- { "ACCEPTABLE_MASTER_TABLE_ENABLED", TLV_ACCEPTABLE_MASTER_TABLE_ENABLED, not_supported }, +- { "ALTERNATE_MASTER", TLV_ALTERNATE_MASTER, not_supported }, +- { "TRANSPARENT_CLOCK_PORT_DATA_SET", TLV_TRANSPARENT_CLOCK_PORT_DATA_SET, not_supported }, +- { "DELAY_MECHANISM", TLV_DELAY_MECHANISM, do_get_action }, +- { "LOG_MIN_PDELAY_REQ_INTERVAL", TLV_LOG_MIN_PDELAY_REQ_INTERVAL, do_get_action }, +- { "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 }, ++ { "NULL_MANAGEMENT", MID_NULL_MANAGEMENT, null_management }, ++ { "CLOCK_DESCRIPTION", MID_CLOCK_DESCRIPTION, do_get_action }, ++ { "PORT_DATA_SET", MID_PORT_DATA_SET, do_get_action }, ++ { "LOG_ANNOUNCE_INTERVAL", MID_LOG_ANNOUNCE_INTERVAL, do_get_action }, ++ { "ANNOUNCE_RECEIPT_TIMEOUT", MID_ANNOUNCE_RECEIPT_TIMEOUT, do_get_action }, ++ { "LOG_SYNC_INTERVAL", MID_LOG_SYNC_INTERVAL, do_get_action }, ++ { "VERSION_NUMBER", MID_VERSION_NUMBER, do_get_action }, ++ { "ENABLE_PORT", MID_ENABLE_PORT, not_supported }, ++ { "DISABLE_PORT", MID_DISABLE_PORT, not_supported }, ++ { "UNICAST_NEGOTIATION_ENABLE", MID_UNICAST_NEGOTIATION_ENABLE, not_supported }, ++ { "UNICAST_MASTER_TABLE", MID_UNICAST_MASTER_TABLE, not_supported }, ++ { "UNICAST_MASTER_MAX_TABLE_SIZE", MID_UNICAST_MASTER_MAX_TABLE_SIZE, not_supported }, ++ { "ACCEPTABLE_MASTER_TABLE_ENABLED", MID_ACCEPTABLE_MASTER_TABLE_ENABLED, not_supported }, ++ { "ALTERNATE_MASTER", MID_ALTERNATE_MASTER, not_supported }, ++ { "TRANSPARENT_CLOCK_PORT_DATA_SET", MID_TRANSPARENT_CLOCK_PORT_DATA_SET, not_supported }, ++ { "DELAY_MECHANISM", MID_DELAY_MECHANISM, do_get_action }, ++ { "LOG_MIN_PDELAY_REQ_INTERVAL", MID_LOG_MIN_PDELAY_REQ_INTERVAL, do_get_action }, ++ { "PORT_DATA_SET_NP", MID_PORT_DATA_SET_NP, do_set_action }, ++ { "PORT_STATS_NP", MID_PORT_STATS_NP, do_get_action }, ++ { "PORT_PROPERTIES_NP", MID_PORT_PROPERTIES_NP, do_get_action }, + }; + + static void do_get_action(struct pmc *pmc, int action, int index, char *str) +@@ -167,8 +168,8 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + return; + } + switch (code) { +- case TLV_PRIORITY1: +- case TLV_PRIORITY2: ++ case MID_PRIORITY1: ++ case MID_PRIORITY2: + cnt = sscanf(str, " %*s %*s %hhu", &mtd.val); + if (cnt != 1) { + fprintf(stderr, "%s SET needs 1 value\n", +@@ -177,7 +178,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + } + pmc_send_set_action(pmc, code, &mtd, sizeof(mtd)); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + cnt = sscanf(str, " %*s %*s " + "clockClass %hhu " + "clockAccuracy %hhx " +@@ -221,7 +222,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + gsn.time_flags |= FREQ_TRACEABLE; + pmc_send_set_action(pmc, code, &gsn, sizeof(gsn)); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + memset(&sen, 0, sizeof(sen)); + cnt = sscanf(str, " %*s %*s " + "duration %hu " +@@ -243,7 +244,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + } + pmc_send_set_action(pmc, code, &sen, sizeof(sen)); + break; +- case TLV_SYNCHRONIZATION_UNCERTAIN_NP: ++ case MID_SYNCHRONIZATION_UNCERTAIN_NP: + cnt = sscanf(str, " %*s %*s %hhu", &mtd.val); + if (cnt != 1) { + fprintf(stderr, "%s SET needs 1 value\n", +@@ -264,7 +265,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + SYNC_UNCERTAIN_DONTCARE); + } + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + cnt = sscanf(str, " %*s %*s " + "neighborPropDelayThresh %u " + "asCapable %d ", +@@ -488,53 +489,54 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id) + return len; + + switch (id) { +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + len += EMPTY_PTP_TEXT; + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + len += sizeof(struct defaultDS); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + len += sizeof(struct currentDS); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + len += sizeof(struct parentDS); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + len += sizeof(struct timePropertiesDS); + break; +- case TLV_PRIORITY1: +- case TLV_PRIORITY2: +- case TLV_DOMAIN: +- case TLV_SLAVE_ONLY: +- case TLV_CLOCK_ACCURACY: +- case TLV_TRACEABILITY_PROPERTIES: +- case TLV_TIMESCALE_PROPERTIES: ++ case MID_PRIORITY1: ++ case MID_PRIORITY2: ++ case MID_DOMAIN: ++ case MID_SLAVE_ONLY: ++ case MID_CLOCK_ACCURACY: ++ case MID_TRACEABILITY_PROPERTIES: ++ case MID_TIMESCALE_PROPERTIES: ++ case MID_MASTER_ONLY: + len += sizeof(struct management_tlv_datum); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + len += sizeof(struct time_status_np); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + len += sizeof(struct grandmaster_settings_np); + break; +- case TLV_NULL_MANAGEMENT: ++ case MID_NULL_MANAGEMENT: + break; +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + len += EMPTY_CLOCK_DESCRIPTION; + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + len += sizeof(struct portDS); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + len += sizeof(struct port_ds_np); + break; +- case TLV_LOG_ANNOUNCE_INTERVAL: +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: +- case TLV_LOG_SYNC_INTERVAL: +- case TLV_VERSION_NUMBER: +- case TLV_DELAY_MECHANISM: +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: ++ case MID_LOG_ANNOUNCE_INTERVAL: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_LOG_SYNC_INTERVAL: ++ case MID_VERSION_NUMBER: ++ case MID_DELAY_MECHANISM: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: + len += sizeof(struct management_tlv_datum); + break; + } +@@ -574,7 +576,7 @@ int pmc_send_get_action(struct pmc *pmc, int id) + extra->tlv = (struct TLV *) msg->management.suffix; + msg_tlv_attach(msg, extra); + +- if (id == TLV_CLOCK_DESCRIPTION && !pmc->zero_length_gets) { ++ if (id == MID_CLOCK_DESCRIPTION && !pmc->zero_length_gets) { + /* + * Make sure the tlv_extra pointers dereferenced in + * mgt_pre_send() do point to something. +diff --git a/port.c b/port.c +index f22bff4..b0e4ef8 100644 +--- a/port.c ++++ b/port.c +@@ -810,10 +810,10 @@ static int port_management_fill_response(struct port *target, + tlv->id = id; + + switch (id) { +- case TLV_NULL_MANAGEMENT: ++ case MID_NULL_MANAGEMENT: + datalen = 0; + break; +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + cd = &extra->cd; + buf = tlv->data; + cd->clockType = (UInteger16 *) buf; +@@ -873,7 +873,7 @@ static int port_management_fill_response(struct port *target, + buf += PROFILE_ID_LEN; + datalen = buf - tlv->data; + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + pds = (struct portDS *) tlv->data; + pds->portIdentity = target->portIdentity; + if (target->state == PS_GRAND_MASTER) { +@@ -895,27 +895,32 @@ static int port_management_fill_response(struct port *target, + pds->versionNumber = target->versionNumber; + datalen = sizeof(*pds); + break; +- case TLV_LOG_ANNOUNCE_INTERVAL: ++ case MID_LOG_ANNOUNCE_INTERVAL: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->logAnnounceInterval; + datalen = sizeof(*mtd); + break; +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->announceReceiptTimeout; + datalen = sizeof(*mtd); + break; +- case TLV_LOG_SYNC_INTERVAL: ++ case MID_LOG_SYNC_INTERVAL: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->logSyncInterval; + datalen = sizeof(*mtd); + break; +- case TLV_VERSION_NUMBER: ++ case MID_VERSION_NUMBER: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->versionNumber; + datalen = sizeof(*mtd); + break; +- case TLV_DELAY_MECHANISM: ++ case MID_MASTER_ONLY: ++ mtd = (struct management_tlv_datum *) tlv->data; ++ mtd->val = target->master_only; ++ datalen = sizeof(*mtd); ++ break; ++ case MID_DELAY_MECHANISM: + mtd = (struct management_tlv_datum *) tlv->data; + if (target->delayMechanism) + mtd->val = target->delayMechanism; +@@ -923,18 +928,18 @@ static int port_management_fill_response(struct port *target, + mtd->val = DM_E2E; + datalen = sizeof(*mtd); + break; +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: + mtd = (struct management_tlv_datum *) tlv->data; + mtd->val = target->logMinPdelayReqInterval; + datalen = sizeof(*mtd); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pdsnp = (struct port_ds_np *) tlv->data; + pdsnp->neighborPropDelayThresh = target->neighborPropDelayThresh; + pdsnp->asCapable = target->asCapable; + datalen = sizeof(*pdsnp); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + ppn = (struct port_properties_np *)tlv->data; + ppn->portIdentity = target->portIdentity; + if (target->state == PS_GRAND_MASTER) +@@ -946,7 +951,7 @@ static int port_management_fill_response(struct port *target, + ptp_text_set(&ppn->interface, ts_label); + datalen = sizeof(*ppn) + ppn->interface.length; + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + psn = (struct port_stats_np *)tlv->data; + psn->portIdentity = target->portIdentity; + psn->stats = target->stats; +@@ -1000,7 +1005,7 @@ static int port_management_set(struct port *target, + tlv = (struct management_tlv *) req->management.suffix; + + switch (id) { +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pdsnp = (struct port_ds_np *) tlv->data; + target->neighborPropDelayThresh = pdsnp->neighborPropDelayThresh; + respond = 1; +@@ -2858,27 +2863,28 @@ int port_manage(struct port *p, struct port *ingress, struct ptp_message *msg) + } + + switch (mgt->id) { +- case TLV_NULL_MANAGEMENT: +- case TLV_CLOCK_DESCRIPTION: +- case TLV_PORT_DATA_SET: +- case TLV_LOG_ANNOUNCE_INTERVAL: +- case TLV_ANNOUNCE_RECEIPT_TIMEOUT: +- case TLV_LOG_SYNC_INTERVAL: +- case TLV_VERSION_NUMBER: +- case TLV_ENABLE_PORT: +- case TLV_DISABLE_PORT: +- case TLV_UNICAST_NEGOTIATION_ENABLE: +- case TLV_UNICAST_MASTER_TABLE: +- case TLV_UNICAST_MASTER_MAX_TABLE_SIZE: +- case TLV_ACCEPTABLE_MASTER_TABLE_ENABLED: +- case TLV_ALTERNATE_MASTER: +- case TLV_TRANSPARENT_CLOCK_PORT_DATA_SET: +- case TLV_DELAY_MECHANISM: +- case TLV_LOG_MIN_PDELAY_REQ_INTERVAL: +- port_management_send_error(p, ingress, msg, TLV_NOT_SUPPORTED); ++ case MID_NULL_MANAGEMENT: ++ case MID_CLOCK_DESCRIPTION: ++ case MID_PORT_DATA_SET: ++ case MID_LOG_ANNOUNCE_INTERVAL: ++ case MID_ANNOUNCE_RECEIPT_TIMEOUT: ++ case MID_LOG_SYNC_INTERVAL: ++ case MID_VERSION_NUMBER: ++ case MID_ENABLE_PORT: ++ case MID_DISABLE_PORT: ++ case MID_UNICAST_NEGOTIATION_ENABLE: ++ case MID_UNICAST_MASTER_TABLE: ++ case MID_UNICAST_MASTER_MAX_TABLE_SIZE: ++ case MID_ACCEPTABLE_MASTER_TABLE_ENABLED: ++ case MID_ALTERNATE_MASTER: ++ case MID_MASTER_ONLY: ++ case MID_TRANSPARENT_CLOCK_PORT_DATA_SET: ++ case MID_DELAY_MECHANISM: ++ case MID_LOG_MIN_PDELAY_REQ_INTERVAL: ++ port_management_send_error(p, ingress, msg, MID_NOT_SUPPORTED); + break; + default: +- port_management_send_error(p, ingress, msg, TLV_NO_SUCH_ID); ++ port_management_send_error(p, ingress, msg, MID_NO_SUCH_ID); + return -1; + } + return 1; +@@ -2983,7 +2989,7 @@ void port_notify_event(struct port *p, enum notification event) + + switch (event) { + case NOTIFY_PORT_STATE: +- id = TLV_PORT_DATA_SET; ++ id = MID_PORT_DATA_SET; + break; + default: + return; +diff --git a/tlv.c b/tlv.c +index 738e404..2526394 100644 +--- a/tlv.c ++++ b/tlv.c +@@ -129,7 +129,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + uint8_t *buf; + uint16_t u16; + switch (m->id) { +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + cd = &extra->cd; + buf = m->data; + len = data_len; +@@ -228,14 +228,14 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + + extra_len = buf - m->data; + break; +- case TLV_USER_DESCRIPTION: ++ case MID_USER_DESCRIPTION: + if (data_len < sizeof(struct PTPText)) + goto bad_length; + extra->cd.userDescription = (struct PTPText *) m->data; + extra_len = sizeof(struct PTPText); + extra_len += extra->cd.userDescription->length; + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + if (data_len != sizeof(struct defaultDS)) + goto bad_length; + dds = (struct defaultDS *) m->data; +@@ -243,7 +243,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + dds->clockQuality.offsetScaledLogVariance = + ntohs(dds->clockQuality.offsetScaledLogVariance); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + if (data_len != sizeof(struct currentDS)) + goto bad_length; + cds = (struct currentDS *) m->data; +@@ -251,7 +251,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + cds->offsetFromMaster = net2host64(cds->offsetFromMaster); + cds->meanPathDelay = net2host64(cds->meanPathDelay); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + if (data_len != sizeof(struct parentDS)) + goto bad_length; + pds = (struct parentDS *) m->data; +@@ -264,20 +264,20 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + pds->grandmasterClockQuality.offsetScaledLogVariance = + ntohs(pds->grandmasterClockQuality.offsetScaledLogVariance); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + if (data_len != sizeof(struct timePropertiesDS)) + goto bad_length; + tp = (struct timePropertiesDS *) m->data; + tp->currentUtcOffset = ntohs(tp->currentUtcOffset); + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + if (data_len != sizeof(struct portDS)) + goto bad_length; + p = (struct portDS *) m->data; + p->portIdentity.portNumber = ntohs(p->portIdentity.portNumber); + p->peerMeanPathDelay = net2host64(p->peerMeanPathDelay); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + if (data_len != sizeof(struct time_status_np)) + goto bad_length; + tsn = (struct time_status_np *) m->data; +@@ -289,7 +289,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + scaled_ns_n2h(&tsn->lastGmPhaseChange); + tsn->gmPresent = ntohl(tsn->gmPresent); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + if (data_len != sizeof(struct grandmaster_settings_np)) + goto bad_length; + gsn = (struct grandmaster_settings_np *) m->data; +@@ -297,20 +297,20 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + ntohs(gsn->clockQuality.offsetScaledLogVariance); + gsn->utc_offset = ntohs(gsn->utc_offset); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + if (data_len != sizeof(struct port_ds_np)) + goto bad_length; + pdsnp = (struct port_ds_np *) m->data; + pdsnp->neighborPropDelayThresh = ntohl(pdsnp->neighborPropDelayThresh); + pdsnp->asCapable = ntohl(pdsnp->asCapable); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + if (data_len != sizeof(struct subscribe_events_np)) + goto bad_length; + sen = (struct subscribe_events_np *)m->data; + sen->duration = ntohs(sen->duration); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + if (data_len < sizeof(struct port_properties_np)) + goto bad_length; + ppn = (struct port_properties_np *)m->data; +@@ -318,7 +318,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + extra_len = sizeof(struct port_properties_np); + extra_len += ppn->interface.length; + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + if (data_len < sizeof(struct port_stats_np)) + goto bad_length; + psn = (struct port_stats_np *)m->data; +@@ -326,12 +326,12 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + ntohs(psn->portIdentity.portNumber); + extra_len = sizeof(struct port_stats_np); + break; +- case TLV_SAVE_IN_NON_VOLATILE_STORAGE: +- case TLV_RESET_NON_VOLATILE_STORAGE: +- case TLV_INITIALIZE: +- case TLV_FAULT_LOG_RESET: +- case TLV_ENABLE_PORT: +- case TLV_DISABLE_PORT: ++ case MID_SAVE_IN_NON_VOLATILE_STORAGE: ++ case MID_RESET_NON_VOLATILE_STORAGE: ++ case MID_INITIALIZE: ++ case MID_FAULT_LOG_RESET: ++ case MID_ENABLE_PORT: ++ case MID_DISABLE_PORT: + if (data_len != 0) + goto bad_length; + break; +@@ -362,7 +362,7 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + struct port_stats_np *psn; + struct mgmt_clock_description *cd; + switch (m->id) { +- case TLV_CLOCK_DESCRIPTION: ++ case MID_CLOCK_DESCRIPTION: + if (extra) { + cd = &extra->cd; + flip16(cd->clockType); +@@ -371,19 +371,19 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + flip16(&cd->protocolAddress->addressLength); + } + break; +- case TLV_DEFAULT_DATA_SET: ++ case MID_DEFAULT_DATA_SET: + dds = (struct defaultDS *) m->data; + dds->numberPorts = htons(dds->numberPorts); + dds->clockQuality.offsetScaledLogVariance = + htons(dds->clockQuality.offsetScaledLogVariance); + break; +- case TLV_CURRENT_DATA_SET: ++ case MID_CURRENT_DATA_SET: + cds = (struct currentDS *) m->data; + cds->stepsRemoved = htons(cds->stepsRemoved); + cds->offsetFromMaster = host2net64(cds->offsetFromMaster); + cds->meanPathDelay = host2net64(cds->meanPathDelay); + break; +- case TLV_PARENT_DATA_SET: ++ case MID_PARENT_DATA_SET: + pds = (struct parentDS *) m->data; + pds->parentPortIdentity.portNumber = + htons(pds->parentPortIdentity.portNumber); +@@ -394,16 +394,16 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + pds->grandmasterClockQuality.offsetScaledLogVariance = + htons(pds->grandmasterClockQuality.offsetScaledLogVariance); + break; +- case TLV_TIME_PROPERTIES_DATA_SET: ++ case MID_TIME_PROPERTIES_DATA_SET: + tp = (struct timePropertiesDS *) m->data; + tp->currentUtcOffset = htons(tp->currentUtcOffset); + break; +- case TLV_PORT_DATA_SET: ++ case MID_PORT_DATA_SET: + p = (struct portDS *) m->data; + p->portIdentity.portNumber = htons(p->portIdentity.portNumber); + p->peerMeanPathDelay = host2net64(p->peerMeanPathDelay); + break; +- case TLV_TIME_STATUS_NP: ++ case MID_TIME_STATUS_NP: + tsn = (struct time_status_np *) m->data; + tsn->master_offset = host2net64(tsn->master_offset); + tsn->ingress_time = host2net64(tsn->ingress_time); +@@ -413,26 +413,26 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + scaled_ns_h2n(&tsn->lastGmPhaseChange); + tsn->gmPresent = htonl(tsn->gmPresent); + break; +- case TLV_GRANDMASTER_SETTINGS_NP: ++ case MID_GRANDMASTER_SETTINGS_NP: + gsn = (struct grandmaster_settings_np *) m->data; + gsn->clockQuality.offsetScaledLogVariance = + htons(gsn->clockQuality.offsetScaledLogVariance); + gsn->utc_offset = htons(gsn->utc_offset); + break; +- case TLV_PORT_DATA_SET_NP: ++ case MID_PORT_DATA_SET_NP: + pdsnp = (struct port_ds_np *) m->data; + pdsnp->neighborPropDelayThresh = htonl(pdsnp->neighborPropDelayThresh); + pdsnp->asCapable = htonl(pdsnp->asCapable); + break; +- case TLV_SUBSCRIBE_EVENTS_NP: ++ case MID_SUBSCRIBE_EVENTS_NP: + sen = (struct subscribe_events_np *)m->data; + sen->duration = htons(sen->duration); + break; +- case TLV_PORT_PROPERTIES_NP: ++ case MID_PORT_PROPERTIES_NP: + ppn = (struct port_properties_np *)m->data; + ppn->portIdentity.portNumber = htons(ppn->portIdentity.portNumber); + break; +- case TLV_PORT_STATS_NP: ++ case MID_PORT_STATS_NP: + psn = (struct port_stats_np *)m->data; + psn->portIdentity.portNumber = + htons(psn->portIdentity.portNumber); +diff --git a/tlv.h b/tlv.h +index a205119..97615fd 100644 +--- a/tlv.h ++++ b/tlv.h +@@ -64,76 +64,76 @@ enum management_action { + }; + + /* Clock management ID values */ +-#define TLV_USER_DESCRIPTION 0x0002 +-#define TLV_SAVE_IN_NON_VOLATILE_STORAGE 0x0003 +-#define TLV_RESET_NON_VOLATILE_STORAGE 0x0004 +-#define TLV_INITIALIZE 0x0005 +-#define TLV_FAULT_LOG 0x0006 +-#define TLV_FAULT_LOG_RESET 0x0007 +-#define TLV_DEFAULT_DATA_SET 0x2000 +-#define TLV_CURRENT_DATA_SET 0x2001 +-#define TLV_PARENT_DATA_SET 0x2002 +-#define TLV_TIME_PROPERTIES_DATA_SET 0x2003 +-#define TLV_PRIORITY1 0x2005 +-#define TLV_PRIORITY2 0x2006 +-#define TLV_DOMAIN 0x2007 +-#define TLV_SLAVE_ONLY 0x2008 +-#define TLV_TIME 0x200F +-#define TLV_CLOCK_ACCURACY 0x2010 +-#define TLV_UTC_PROPERTIES 0x2011 +-#define TLV_TRACEABILITY_PROPERTIES 0x2012 +-#define TLV_TIMESCALE_PROPERTIES 0x2013 +-#define TLV_PATH_TRACE_LIST 0x2015 +-#define TLV_PATH_TRACE_ENABLE 0x2016 +-#define TLV_GRANDMASTER_CLUSTER_TABLE 0x2017 +-#define TLV_ACCEPTABLE_MASTER_TABLE 0x201A +-#define TLV_ACCEPTABLE_MASTER_MAX_TABLE_SIZE 0x201C +-#define TLV_ALTERNATE_TIME_OFFSET_ENABLE 0x201E +-#define TLV_ALTERNATE_TIME_OFFSET_NAME 0x201F +-#define TLV_ALTERNATE_TIME_OFFSET_MAX_KEY 0x2020 +-#define TLV_ALTERNATE_TIME_OFFSET_PROPERTIES 0x2021 +-#define TLV_EXTERNAL_PORT_CONFIGURATION_ENABLED 0x3000 +-#define TLV_HOLDOVER_UPGRADE_ENABLE 0x3002 +-#define TLV_TRANSPARENT_CLOCK_DEFAULT_DATA_SET 0x4000 +-#define TLV_PRIMARY_DOMAIN 0x4002 +-#define TLV_TIME_STATUS_NP 0xC000 +-#define TLV_GRANDMASTER_SETTINGS_NP 0xC001 +-#define TLV_SUBSCRIBE_EVENTS_NP 0xC003 +-#define TLV_SYNCHRONIZATION_UNCERTAIN_NP 0xC006 ++#define MID_USER_DESCRIPTION 0x0002 ++#define MID_SAVE_IN_NON_VOLATILE_STORAGE 0x0003 ++#define MID_RESET_NON_VOLATILE_STORAGE 0x0004 ++#define MID_INITIALIZE 0x0005 ++#define MID_FAULT_LOG 0x0006 ++#define MID_FAULT_LOG_RESET 0x0007 ++#define MID_DEFAULT_DATA_SET 0x2000 ++#define MID_CURRENT_DATA_SET 0x2001 ++#define MID_PARENT_DATA_SET 0x2002 ++#define MID_TIME_PROPERTIES_DATA_SET 0x2003 ++#define MID_PRIORITY1 0x2005 ++#define MID_PRIORITY2 0x2006 ++#define MID_DOMAIN 0x2007 ++#define MID_SLAVE_ONLY 0x2008 ++#define MID_TIME 0x200F ++#define MID_CLOCK_ACCURACY 0x2010 ++#define MID_UTC_PROPERTIES 0x2011 ++#define MID_TRACEABILITY_PROPERTIES 0x2012 ++#define MID_TIMESCALE_PROPERTIES 0x2013 ++#define MID_PATH_TRACE_LIST 0x2015 ++#define MID_PATH_TRACE_ENABLE 0x2016 ++#define MID_GRANDMASTER_CLUSTER_TABLE 0x2017 ++#define MID_ACCEPTABLE_MASTER_TABLE 0x201A ++#define MID_ACCEPTABLE_MASTER_MAX_TABLE_SIZE 0x201C ++#define MID_ALTERNATE_TIME_OFFSET_ENABLE 0x201E ++#define MID_ALTERNATE_TIME_OFFSET_NAME 0x201F ++#define MID_ALTERNATE_TIME_OFFSET_MAX_KEY 0x2020 ++#define MID_ALTERNATE_TIME_OFFSET_PROPERTIES 0x2021 ++#define MID_EXTERNAL_PORT_CONFIGURATION_ENABLED 0x3000 ++#define MID_HOLDOVER_UPGRADE_ENABLE 0x3002 ++#define MID_TRANSPARENT_CLOCK_DEFAULT_DATA_SET 0x4000 ++#define MID_PRIMARY_DOMAIN 0x4002 ++#define MID_TIME_STATUS_NP 0xC000 ++#define MID_GRANDMASTER_SETTINGS_NP 0xC001 ++#define MID_SUBSCRIBE_EVENTS_NP 0xC003 ++#define MID_SYNCHRONIZATION_UNCERTAIN_NP 0xC006 + + /* Port management ID values */ +-#define TLV_NULL_MANAGEMENT 0x0000 +-#define TLV_CLOCK_DESCRIPTION 0x0001 +-#define TLV_PORT_DATA_SET 0x2004 +-#define TLV_LOG_ANNOUNCE_INTERVAL 0x2009 +-#define TLV_ANNOUNCE_RECEIPT_TIMEOUT 0x200A +-#define TLV_LOG_SYNC_INTERVAL 0x200B +-#define TLV_VERSION_NUMBER 0x200C +-#define TLV_ENABLE_PORT 0x200D +-#define TLV_DISABLE_PORT 0x200E +-#define TLV_UNICAST_NEGOTIATION_ENABLE 0x2014 +-#define TLV_UNICAST_MASTER_TABLE 0x2018 +-#define TLV_UNICAST_MASTER_MAX_TABLE_SIZE 0x2019 +-#define TLV_ACCEPTABLE_MASTER_TABLE_ENABLED 0x201B +-#define TLV_ALTERNATE_MASTER 0x201D +-#define TLV_MASTER_ONLY 0x3001 +-#define TLV_EXT_PORT_CONFIG_PORT_DATA_SET 0x3003 +-#define TLV_SLAVE_EVENT_MONITORING 0x3004 // TODO - proposed value, missing in 1588 v2.1 +-#define TLV_TRANSPARENT_CLOCK_PORT_DATA_SET 0x4001 +-#define TLV_DELAY_MECHANISM 0x6000 +-#define TLV_LOG_MIN_PDELAY_REQ_INTERVAL 0x6001 +-#define TLV_PORT_DATA_SET_NP 0xC002 +-#define TLV_PORT_PROPERTIES_NP 0xC004 +-#define TLV_PORT_STATS_NP 0xC005 ++#define MID_NULL_MANAGEMENT 0x0000 ++#define MID_CLOCK_DESCRIPTION 0x0001 ++#define MID_PORT_DATA_SET 0x2004 ++#define MID_LOG_ANNOUNCE_INTERVAL 0x2009 ++#define MID_ANNOUNCE_RECEIPT_TIMEOUT 0x200A ++#define MID_LOG_SYNC_INTERVAL 0x200B ++#define MID_VERSION_NUMBER 0x200C ++#define MID_ENABLE_PORT 0x200D ++#define MID_DISABLE_PORT 0x200E ++#define MID_UNICAST_NEGOTIATION_ENABLE 0x2014 ++#define MID_UNICAST_MASTER_TABLE 0x2018 ++#define MID_UNICAST_MASTER_MAX_TABLE_SIZE 0x2019 ++#define MID_ACCEPTABLE_MASTER_TABLE_ENABLED 0x201B ++#define MID_ALTERNATE_MASTER 0x201D ++#define MID_MASTER_ONLY 0x3001 ++#define MID_EXT_PORT_CONFIG_PORT_DATA_SET 0x3003 ++#define MID_SLAVE_EVENT_MONITORING 0x3004 // TODO - proposed value, missing in 1588 v2.1 ++#define MID_TRANSPARENT_CLOCK_PORT_DATA_SET 0x4001 ++#define MID_DELAY_MECHANISM 0x6000 ++#define MID_LOG_MIN_PDELAY_REQ_INTERVAL 0x6001 ++#define MID_PORT_DATA_SET_NP 0xC002 ++#define MID_PORT_PROPERTIES_NP 0xC004 ++#define MID_PORT_STATS_NP 0xC005 + + /* Management error ID values */ +-#define TLV_RESPONSE_TOO_BIG 0x0001 +-#define TLV_NO_SUCH_ID 0x0002 +-#define TLV_WRONG_LENGTH 0x0003 +-#define TLV_WRONG_VALUE 0x0004 +-#define TLV_NOT_SETABLE 0x0005 +-#define TLV_NOT_SUPPORTED 0x0006 +-#define TLV_GENERAL_ERROR 0xFFFE ++#define MID_RESPONSE_TOO_BIG 0x0001 ++#define MID_NO_SUCH_ID 0x0002 ++#define MID_WRONG_LENGTH 0x0003 ++#define MID_WRONG_VALUE 0x0004 ++#define MID_NOT_SETABLE 0x0005 ++#define MID_NOT_SUPPORTED 0x0006 ++#define MID_GENERAL_ERROR 0xFFFE + + /* Values for the SYNCHRONIZATION_UNCERTAIN_NP management TLV */ + #define SYNC_UNCERTAIN_DONTCARE 0xff +-- +2.25.1 + +From 2a6ddfe1b9700ce8e0c62da8a7a4f2edcd4e1cad Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Sun, 18 Jun 2023 20:58:34 -0300 +Subject: [PATCH 37/47] Enhance phc2sys to accept multiple ptp4l inputs + +A new configuration option called ha_enabled was created. When it is set 1 +multiple ptp4l inputs are accepted and the high availability algorithms +are enabled. + +In addition to ha_enabled 1 a set of interfaces must also be provided. +Each interface is one-to-one mapped to a clock source, and must be +associated to an unique ptp4l instance using the ha_uds_address +configuration option. + +For example: + +ha_enabled 1 + +[ens1f1] +ha_uds_address /var/run/ptp4l-ptp-inst1 + +[ens1f2] +ha_uds_address /var/run/ptp4l-ptp-inst2 + +A maximum of 128 interfaces is supported. + +Regression: verify non HA phc2sys configuration +PASS: Verify auto configuration is still accepted. +PASS: Verify manual configuration with a single clock is still accepted. +PASS: Verify mix of manual and auto configuration is denied. +PASS: Verify manual configuration with zero clock sources is denied. + +Test Plan: verify HA configuration +PASS: Verify HA is disabled by default. +PASS: Verify HA configuration with 1 or more clock source is accepted. +PASS: Verify ha_uds_address default value. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek +Signed-off-by: Andre Mauricio Zelak + +[commit 705fe12b294216c7b5797f48d83ff97fcc076294 upstream] +[commit e730f006cb56ac55932220c1afff5470de875200 upstream] +[commit df8fa0492771f6babb75254619337edb6041daea upstream] +[commit 0201340fa5abc17634bfb4d0b2a386d218d3095b upstream] +[commit dd7400f4eb548dfb2acfb6ebaf53a6d77b9c5da2 upstream] +[commit 904fb44ecebd448f9b2952dd287ac2b5db8249db upstream] +[commit 56dcd671d5241b589dc44b776fec9b2752496477 upstream] +[commit 7e5617afe8837b77629cc04c9e3abb38ae64678c upstream] +[commit 5ea8af40b8b5e4680d8a8e1a19482c28f95ce6b3 upstream] +[commit 3d38367a3151845ec543ab9125e2d0a0aefa2f56 upstream] +[commit 17a4d6805597fd6ddb911b8246e7b131a42f9191 upstream] +[commit 1650d972f4fe9bb39807536df2594d1a85aabf9c upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 17 +++ + config.h | 2 + + phc2sys.c | 337 +++++++++++++++++++++++++++++++++++++--------------- + pmc_agent.c | 17 --- + pmc_agent.h | 21 +++- + uds.c | 19 ++- + 6 files changed, 294 insertions(+), 119 deletions(-) + +diff --git a/config.c b/config.c +index d45e948..b97e5d7 100644 +--- a/config.c ++++ b/config.c +@@ -250,6 +250,8 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("G.8275.defaultDS.localPriority", 128, 1, UINT8_MAX), + PORT_ITEM_INT("G.8275.portDS.localPriority", 128, 1, UINT8_MAX), + GLOB_ITEM_INT("gmCapable", 1, 0, 1), ++ GLOB_ITEM_INT("ha_enabled", 0, 0, 1), ++ PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), + GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu), + PORT_ITEM_INT("hybrid_e2e", 0, 0, 1), + PORT_ITEM_INT("ignore_source_id", 0, 0, 1), +@@ -996,6 +998,21 @@ char *config_get_string(struct config *cfg, const char *section, + return ci->val.s; + } + ++unsigned int config_get_interfaces(struct config *cfg, char *interfaces[], unsigned int max) ++{ ++ struct interface *iface = NULL; ++ unsigned int counter = 0; ++ ++ STAILQ_FOREACH(iface, &cfg->interfaces, list) { ++ if (counter == max) { ++ pr_err("bug: too many interfaces!"); ++ return (unsigned int)-1; ++ } ++ interfaces[counter++] = interface_name(iface); ++ } ++ return counter; ++} ++ + int config_harmonize_onestep(struct config *cfg) + { + enum timestamp_type tstype = config_get_int(cfg, NULL, "time_stamping"); +diff --git a/config.h b/config.h +index 14d2f64..645fb42 100644 +--- a/config.h ++++ b/config.h +@@ -64,6 +64,8 @@ int config_get_int(struct config *cfg, const char *section, + char *config_get_string(struct config *cfg, const char *section, + const char *option); + ++unsigned int config_get_interfaces(struct config *cfg, char *interfaces[], unsigned int max); ++ + int config_harmonize_onestep(struct config *cfg); + + static inline struct option *config_long_options(struct config *cfg) +diff --git a/phc2sys.c b/phc2sys.c +index c9fabd7..a4afe01 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -64,6 +64,12 @@ + + #define PHC_PPS_OFFSET_LIMIT 10000000 + ++#define MAX_SRC_CLOCKS 128 ++ ++#define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) || (((unsigned int) index) & 0xFF)) ++#define PORT_ID_TO_PORT(id) ((((unsigned int) id) >> 8) & 0xFF) ++#define PORT_ID_TO_INDEX(id) (((unsigned int) id) & 0xFF) ++ + struct clock { + LIST_ENTRY(clock) list; + LIST_ENTRY(clock) dst_list; +@@ -85,6 +91,7 @@ struct clock { + struct stats *freq_stats; + struct stats *delay_stats; + struct clockcheck *sanity_check; ++ struct pmc_agent *node; + }; + + struct port { +@@ -103,7 +110,7 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; +- struct pmc_agent *node; ++ LIST_HEAD(pmc_agent_head, pmc_agent) pmc_agents; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; +@@ -260,6 +267,18 @@ static struct port *port_get(struct phc2sys_private *priv, unsigned int number) + return NULL; + } + ++static struct port *port_get_by_clock(struct phc2sys_private *priv, struct clock * clock) ++{ ++ struct port *p, *port = NULL; ++ LIST_FOREACH(p, &priv->ports, list) { ++ if (p->clock == clock) { ++ port = p; ++ break; ++ } ++ } ++ return port; ++} ++ + static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + char *device) + { +@@ -293,6 +312,42 @@ static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + return p; + } + ++static struct pmc_agent *pmc_agent_get(struct phc2sys_private *priv, unsigned int index) ++{ ++ struct pmc_agent *node; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ if (node->index == index) { ++ break; ++ } ++ } ++ return node; ++} ++ ++static struct pmc_agent *pmc_agent_add(struct phc2sys_private *priv, unsigned int index) ++{ ++ struct pmc_agent *node = pmc_agent_get(priv, index); ++ if (node) ++ return node; ++ ++ node = pmc_agent_create(); ++ if (!node) { ++ pr_err("failed to allocate memory for a pmc agent"); ++ return NULL; ++ } ++ ++ node->index = index; ++ LIST_INSERT_HEAD(&priv->pmc_agents, node, list); ++ return node; ++} ++ ++static void pmc_agent_cleanup(struct phc2sys_private *priv) ++{ ++ struct pmc_agent *node, *tmp; ++ LIST_FOREACH_SAFE(node, &priv->pmc_agents, list, tmp) { ++ pmc_agent_destroy(node); ++ } ++} ++ + static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + int new_state) + { +@@ -302,12 +357,17 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + struct sk_ts_info ts_info; + char iface[IFNAMSIZ]; + clockid_t clkid = CLOCK_INVALID; ++ struct pmc_agent *node; ++ unsigned int pmc_index; + + LIST_FOREACH(p, &priv->ports, list) { + if (p->clock != clock) { + continue; + } +- err = pmc_agent_query_port_properties(priv->node, 1000, ++ ++ pmc_index = PORT_ID_TO_INDEX(p->number); ++ node = pmc_agent_get(priv, pmc_index); ++ err = pmc_agent_query_port_properties(node, 1000, + p->number, &state, + ×tamping, iface); + if (!err) { +@@ -638,12 +698,13 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + int64_t pps_offset, phc_offset, phc_delay; + uint64_t pps_ts, phc_ts; + clockid_t src = priv->master->clkid; ++ struct pmc_agent *node = LIST_FIRST(&priv->pmc_agents); + + priv->master->source_label = "pps"; + + if (src == CLOCK_INVALID) { + /* The sync offset can't be applied with PPS alone. */ +- pmc_agent_set_sync_offset(priv->node, 0); ++ pmc_agent_set_sync_offset(node, 0); + } else { + enable_pps_output(priv->master->clkid); + } +@@ -674,7 +735,7 @@ static int do_pps_loop(struct phc2sys_private *priv, struct clock *clock, + pps_offset = pps_ts - phc_ts; + } + +- if (pmc_agent_update(priv->node) < 0) ++ if (pmc_agent_update(node) < 0) + continue; + update_clock(priv, clock, pps_offset, pps_ts, -1); + } +@@ -706,6 +767,7 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + uint64_t ts; + int64_t offset, delay; + int err; ++ struct pmc_agent *node = NULL; + + interval.tv_sec = priv->phc_interval; + interval.tv_nsec = (priv->phc_interval - interval.tv_sec) * 1e9; +@@ -713,22 +775,28 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + while (is_running()) { + clock_nanosleep(CLOCK_MONOTONIC, 0, &interval, NULL); + +- if (pmc_agent_update(priv->node) < 0) { +- continue; +- } ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ if (pmc_agent_update(node) < 0) { ++ continue; ++ } + +- if (subscriptions) { +- run_pmc_events(priv->node); +- if (priv->state_changed) { +- /* force getting offset, as it may have +- * changed after the port state change */ +- if (pmc_agent_query_utc_offset(priv->node, 1000)) { +- pr_err("failed to get UTC offset"); +- continue; ++ if (subscriptions) { ++ run_pmc_events(node); ++ if (priv->state_changed) { ++ /* force getting offset, as it may have ++ * changed after the port state change */ ++ if (pmc_agent_query_utc_offset(node, 1000)) { ++ pr_err("failed to get UTC offset"); ++ continue; ++ } + } +- reconfigure(priv); + } + } ++ ++ if (subscriptions && priv->state_changed) { ++ reconfigure(priv); ++ } ++ + if (!priv->master) + continue; + +@@ -859,55 +927,65 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + struct clock *clock; + struct port *port; + unsigned int i; ++ struct pmc_agent *node = NULL; ++ unsigned int retries, port_number; + +- while (1) { +- if (!is_running()) { +- return -1; +- } +- err = pmc_agent_query_dds(priv->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- } else { +- return -1; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_dds(node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } + } +- } + +- number_ports = pmc_agent_get_number_ports(priv->node); +- if (number_ports <= 0) { +- pr_err("failed to get number of ports"); +- return -1; +- } +- +- err = pmc_agent_subscribe(priv->node, 1000); +- if (err) { +- pr_err("failed to subscribe"); +- return -1; +- } +- +- for (i = 1; i <= number_ports; i++) { +- err = pmc_agent_query_port_properties(priv->node, 1000, i, +- &state, ×tamping, +- iface); +- if (err == -ENODEV) { +- /* port does not exist, ignore the port */ ++ number_ports = pmc_agent_get_number_ports(node); ++ if (number_ports <= 0) { ++ pr_err("failed to get number of ports"); + continue; + } ++ ++ err = pmc_agent_subscribe(node, 1000); + if (err) { +- pr_err("failed to get port properties"); +- return -1; +- } +- if (timestamping == TS_SOFTWARE) { +- /* ignore ports with software time stamping */ ++ pr_err("failed to subscribe"); + continue; + } +- port = port_add(priv, i, iface); +- if (!port) +- return -1; +- port->state = normalize_state(state); ++ ++ for (i = 1; i <= number_ports; i++) { ++ err = pmc_agent_query_port_properties(node, 1000, i, ++ &state, ×tamping, ++ iface); ++ if (err == -ENODEV) { ++ /* port does not exist, ignore the port */ ++ continue; ++ } ++ if (err) { ++ pr_err("failed to get port properties"); ++ break; ++ } ++ if (timestamping == TS_SOFTWARE) { ++ /* ignore ports with software time stamping */ ++ continue; ++ } ++ port_number = PORT_INDEX_TO_PORT_ID(i, node->index); ++ port = port_add(priv, port_number, iface); ++ if (!port) ++ return -1; ++ port->state = normalize_state(state); ++ /* map clock to pmc agent node */ ++ port->clock->node = node; ++ } + } ++ + if (LIST_EMPTY(&priv->clocks)) { + pr_err("no suitable ports available"); + return -1; +@@ -926,9 +1004,11 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + } + + /* get initial offset */ +- if (pmc_agent_query_utc_offset(priv->node, 1000)) { +- pr_err("failed to get UTC offset"); +- return -1; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ if (pmc_agent_query_utc_offset(node, 1000)) { ++ pr_err("failed to get UTC offset"); ++ continue; ++ } + } + return 0; + } +@@ -937,9 +1017,12 @@ static int auto_init_ports(struct phc2sys_private *priv, int add_rt) + static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + int64_t offset, uint64_t ts) + { +- int clock_leap, node_leap = pmc_agent_get_leap(priv->node); ++ int clock_leap, node_leap; ++ struct pmc_agent *node = priv->master->node; ++ ++ node_leap = pmc_agent_get_leap(node); + +- clock->sync_offset = pmc_agent_get_sync_offset(priv->node); ++ clock->sync_offset = pmc_agent_get_sync_offset(node); + + if ((node_leap || clock->leap_set) && + clock->is_utc != priv->master->is_utc) { +@@ -980,7 +1063,7 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + } + } + +- if (pmc_agent_utc_offset_traceable(priv->node) && ++ if (pmc_agent_utc_offset_traceable(node) && + clock->utc_offset_set != clock->sync_offset) { + if (clock->clkid == CLOCK_REALTIME) + sysclk_set_tai_offset(clock->sync_offset); +@@ -1034,7 +1117,8 @@ static void usage(char *progname) + + int main(int argc, char *argv[]) + { +- char *config = NULL, *dst_name = NULL, *progname, *src_name = NULL; ++ char *config = NULL, *dst_name = NULL, *progname; ++ char *src_names[MAX_SRC_CLOCKS]; + char uds_local[MAX_IFNAME_SIZE + 1]; + int autocfg = 0, c, domain_number = 0, index, ntpshm_segment, offset; + int pps_fd = -1, print_level = LOG_INFO, r = -1, rt = 0; +@@ -1047,7 +1131,11 @@ int main(int argc, char *argv[]) + struct phc2sys_private priv = { + .phc_readings = 5, + .phc_interval = 1.0, ++ .master = NULL, + }; ++ struct pmc_agent *node = NULL; ++ unsigned int i, src_cnt = 0; ++ int ha_enabled = 0; + + handle_term_signals(); + +@@ -1055,8 +1143,8 @@ int main(int argc, char *argv[]) + if (!cfg) { + return -1; + } +- priv.node = pmc_agent_create(); +- if (!priv.node) { ++ node = pmc_agent_add(&priv, 0); ++ if (!node) { + return -1; + } + +@@ -1102,7 +1190,11 @@ int main(int argc, char *argv[]) + "'-i' has been deprecated. please use '-s' instead.\n"); + /* fallthrough */ + case 's': +- src_name = strdup(optarg); ++ if (src_cnt == MAX_SRC_CLOCKS) { ++ fprintf(stderr, "too many source clocks\n"); ++ goto bad_usage; ++ } ++ src_names[src_cnt++] = optarg; + break; + case 'E': + if (!strcasecmp(optarg, "pi")) { +@@ -1153,7 +1245,7 @@ int main(int argc, char *argv[]) + if (get_arg_val_i(c, optarg, &offset, INT_MIN, INT_MAX)) { + goto end; + } +- pmc_agent_set_sync_offset(priv.node, offset); ++ pmc_agent_set_sync_offset(node, offset); + priv.forced_sync_offset = -1; + break; + case 'L': +@@ -1241,12 +1333,22 @@ int main(int argc, char *argv[]) + return c; + } + +- if (autocfg && (src_name || dst_name || pps_fd >= 0 || wait_sync || priv.forced_sync_offset)) { ++ if (src_cnt == 0) { ++ /* get the source interface list from configuration file */ ++ src_cnt = config_get_interfaces(cfg, src_names, MAX_SRC_CLOCKS); ++ if (src_cnt == (unsigned int)-1) { ++ fprintf(stderr, "too many interfaces in configuration file\n"); ++ fprintf(stderr, "maximum number of interfaces is %u\n", MAX_SRC_CLOCKS); ++ goto bad_usage; ++ } ++ } ++ ++ if (autocfg && (src_cnt > 0 || dst_name || pps_fd >= 0 || wait_sync || priv.forced_sync_offset)) { + fprintf(stderr, + "autoconfiguration cannot be mixed with manual config options.\n"); + goto bad_usage; + } +- if (!autocfg && pps_fd < 0 && !src_name) { ++ if (!autocfg && pps_fd < 0 && src_cnt == 0) { + fprintf(stderr, + "autoconfiguration or valid source clock must be selected.\n"); + goto bad_usage; +@@ -1282,7 +1384,7 @@ int main(int argc, char *argv[]) + getpid()); + + if (autocfg) { +- if (init_pmc_node(cfg, priv.node, uds_local, ++ if (init_pmc_node(cfg, node, uds_local, + phc2sys_recv_subscribed, &priv)) + goto end; + if (auto_init_ports(&priv, rt) < 0) +@@ -1291,15 +1393,26 @@ int main(int argc, char *argv[]) + goto end; + } + +- src = clock_add(&priv, src_name); +- free(src_name); +- if (!src) { +- fprintf(stderr, +- "valid source clock must be selected.\n"); ++ ha_enabled = config_get_int(cfg, NULL, "ha_enabled"); ++ if (!ha_enabled && src_cnt > 1) { ++ fprintf(stderr, "too many source clocks\n"); ++ fprintf(stderr, "Use 'ha_enabled 1' to accept more than one source clocks\n"); + goto bad_usage; + } +- src->state = PS_SLAVE; +- priv.master = src; ++ ++ for (i = 0; i < src_cnt; ++i) { ++ src = clock_add(&priv, src_names[i]); ++ if (!src) { ++ fprintf(stderr, ++ "invalid source clock '%s'.\n", src_names[i]); ++ goto bad_usage; ++ } ++ src->state = PS_SLAVE; ++ /* select the first clock */ ++ if (priv.master == NULL) { ++ priv.master = src; ++ } ++ } + + dst = clock_add(&priv, dst_name ? dst_name : "CLOCK_REALTIME"); + free(dst_name); +@@ -1320,32 +1433,58 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- if (init_pmc_node(cfg, priv.node, uds_local, +- phc2sys_recv_subscribed, &priv)) +- goto end; ++ i = 0; ++ for (src = LIST_FIRST(&priv.clocks); ++ src != NULL; ++ src = LIST_NEXT(src, list)) { + +- while (is_running()) { +- r = run_pmc_wait_sync(priv.node, 1000); +- if (r < 0) +- goto end; +- if (r > 0) +- break; +- else +- pr_notice("Waiting for ptp4l..."); +- } ++ /* skip dst clock */ ++ if (src == dst) { ++ continue; ++ } + +- if (!priv.forced_sync_offset) { +- r = pmc_agent_query_utc_offset(priv.node, 1000); +- if (r) { +- pr_err("failed to get UTC offset"); ++ if (i > 0) { ++ node = pmc_agent_add(&priv, i); ++ if (!node) ++ goto end; ++ } ++ ++ /* uds local is formated '/var/run/phc2sys..' */ ++ snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d.%s", ++ getpid(), src->device); ++ ++ if (init_pmc_node(cfg, node, uds_local, ++ phc2sys_recv_subscribed, &priv)) + goto end; ++ ++ /* map clock to pmc agent node */ ++ src->node = node; ++ ++ while (is_running()) { ++ r = run_pmc_wait_sync(node, 1000); ++ if (r < 0) ++ goto end; ++ if (r > 0) ++ break; ++ else ++ pr_notice("Waiting for ptp4l..."); ++ } ++ ++ if (!priv.forced_sync_offset) { ++ r = pmc_agent_query_utc_offset(node, 1000); ++ if (r) { ++ pr_err("failed to get UTC offset"); ++ goto end; ++ } ++ } ++ ++ if (priv.forced_sync_offset || ++ (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || ++ src->clkid == CLOCK_INVALID) { ++ pmc_agent_disable(node); + } +- } + +- if (priv.forced_sync_offset || +- (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || +- src->clkid == CLOCK_INVALID) { +- pmc_agent_disable(priv.node); ++ ++i; + } + } + +@@ -1359,7 +1498,7 @@ int main(int argc, char *argv[]) + } + + end: +- pmc_agent_destroy(priv.node); ++ pmc_agent_cleanup(&priv); + clock_cleanup(&priv); + port_cleanup(&priv); + config_destroy(cfg); +diff --git a/pmc_agent.c b/pmc_agent.c +index 3034f65..d13f569 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -34,23 +34,6 @@ + * renewed. + */ + +-struct pmc_agent { +- struct pmc *pmc; +- uint64_t pmc_last_update; +- +- struct defaultDS dds; +- bool dds_valid; +- int leap; +- int pmc_ds_requested; +- bool stay_subscribed; +- int sync_offset; +- int utc_offset_traceable; +- +- /* Callback on message reception */ +- pmc_node_recv_subscribed_t *recv_subscribed; +- void *recv_context; +-}; +- + static void send_subscription(struct pmc_agent *node) + { + struct subscribe_events_np sen; +diff --git a/pmc_agent.h b/pmc_agent.h +index dd34d30..5f25984 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -26,11 +26,28 @@ + + #include "pmc_common.h" + +-struct pmc_agent; +- + typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, + int excluded); + ++struct pmc_agent { ++ LIST_ENTRY(pmc_agent) list; ++ struct pmc *pmc; ++ uint64_t pmc_last_update; ++ ++ struct defaultDS dds; ++ bool dds_valid; ++ int leap; ++ int pmc_ds_requested; ++ bool stay_subscribed; ++ int sync_offset; ++ int utc_offset_traceable; ++ unsigned int index; ++ ++ /* Callback on message reception */ ++ pmc_node_recv_subscribed_t *recv_subscribed; ++ void *recv_context; ++}; ++ + int init_pmc_node(struct config *cfg, struct pmc_agent *agent, const char *uds, + pmc_node_recv_subscribed_t *recv_subscribed, void *context); + int run_pmc_wait_sync(struct pmc_agent *agent, int timeout); +diff --git a/uds.c b/uds.c +index 641a672..57d4796 100644 +--- a/uds.c ++++ b/uds.c +@@ -55,11 +55,13 @@ static int uds_close(struct transport *t, struct fdarray *fda) + static int uds_open(struct transport *t, struct interface *iface, struct fdarray *fda, + enum timestamp_type tt) + { +- char *uds_path = config_get_string(t->cfg, NULL, "uds_address"); ++ char *uds_path = NULL; + struct uds *uds = container_of(t, struct uds, t); + const char *name = interface_name(iface); + struct sockaddr_un sa; + int fd, err; ++ char *point = NULL, *source = NULL; ++ int ha_enabled = config_get_int(t->cfg, NULL, "ha_enabled"); + + fd = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (fd < 0) { +@@ -82,6 +84,21 @@ static int uds_open(struct transport *t, struct interface *iface, struct fdarray + /* For client use, pre load the server path. */ + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_LOCAL; ++ ++ if (!ha_enabled) { ++ uds_path = config_get_string(t->cfg, NULL, "uds_address"); ++ } else { ++ /* The interface name is formated as '/var/run/phc2sys..'. ++ The last item is the source interface. */ ++ point = strtok(name, "."); ++ while(point != NULL) { ++ source = point; ++ point = strtok(NULL, "."); ++ } ++ ++ uds_path = config_get_string(t->cfg, source, "ha_uds_address"); ++ } ++ + strncpy(sa.sun_path, uds_path, sizeof(sa.sun_path) - 1); + uds->address.sun = sa; + uds->address.len = sizeof(sa); +-- +2.25.1 + +From 142b30b1f996a5bd48f0edc9b5fb0f51af0b97fd Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Tue, 4 Jul 2023 17:27:50 -0300 +Subject: [PATCH 38/47] Best source selection algorithm + +An algorithm to select the best available clock and use it +as clock source. + +A new set of configuration options was introduced to control +the clock requirements. The clock which fails to match the +requirements is discarded. + +If a single clock matches the requirements, it is selected +as source. + +If one or more clock match requirements, the clock with the highest +priority is selected. In case of tie, the 1st configured clock is +selected. + +And if no clock match requirements, the clock with the best local +clock class is selected. + +The ha_priority option is an interface setting used to configure +the clock priority. The lowest priority is 0 and the highest is 254, +and the default value is 0. + +The ha_min_local_clockClass option is a global setting for the minimal +local clock class requirement. It ranges from 6 to 255 and its default +is 135. + +The ha_min_clockAccuracy option is a global setting for the minimal +clock accuracy requirement. It ranges from 0x00 to 0xff and its default +is 0xfe. + +The ha_min_offsetScaledLogVariance is a global setting for the minimal +offset scaled log variance. It ranges from 0 to 65535 and its default +is 65535. + +The ha_timeTraceable is a global setting to enable or disable +the time traceable verification. When it's set the clock +which time is not traceable is discarded. + +The ha_frequencyTraceable is a global setting to enable or disable +the frequency traceable verification. When it's set the clock +which frequency is not traceable is discarded. + +The ha_min_gm_ClockClass is a global setting for the minimal +GM clock class requirement. It ranges from 6 to 255 and its +default is 135. + +Test Plan: clock selection algorithm +PASS: Verify clock is discarded when local clock class doesn't match +requirements. +PASS: Verify clock is discarded when local clock accuracy doesn't match +requirements. +PASS: Verify clock is discarded when local offset scaled log variance doesn't +match requirements. +PASS: Verify clock is discarded when time traceable verification is set 1 and +the clock hasn't time traceable flag set. +PASS: Verify clock is discarded when frequency traceable verification is set 1 +and the clock hasn't frequency traceable flag set. +PASS: Verify clock is discarded when GM clock class doesn't match requirements. +PASS: Verify clock is accepted when time traceable verification is set 0 even +when clock hasn't time traceable flag set. +PASS: Verify clock is accepted when frequency traceable verification is set 0 +even when clock hasn't frequency traceable flag set. +PASS: Verify the highest priority clock is selected when one or more clock +match the requirements. +PASS: Verify the 1st configured clock is selected when one or more clock of the +same priority match the requirements. +PASS: Verify the clock with highest local clock class is selected when no clock +match the requirements. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit 1c10dd42b32388c2e708ad249dd1f193e7208155 upstream] +[commit 373c4fd50aaf52540d3eeb8f38f3e07307dea3a3 upstream] +[commit 279d5b6e7f88876ce00f1e87faba65c7cd6a90b0 upstream] +[commit 00d9ad798b1f700faefa0b5d4074c46f8ae87ef4 upstream] +[commit 1407a51d8000ca7df18ba67d611a761abb6f77f8 upstream] +[commit e0c1c7b64f7af8002092c01e023f524bfcc39f8b upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 7 ++ + phc2sys.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + pmc_agent.c | 20 +++++ + pmc_agent.h | 13 +++ + 4 files changed, 271 insertions(+) + +diff --git a/config.c b/config.c +index b97e5d7..8ce5f6c 100644 +--- a/config.c ++++ b/config.c +@@ -250,7 +250,14 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("G.8275.defaultDS.localPriority", 128, 1, UINT8_MAX), + PORT_ITEM_INT("G.8275.portDS.localPriority", 128, 1, UINT8_MAX), + GLOB_ITEM_INT("gmCapable", 1, 0, 1), ++ GLOB_ITEM_INT("ha_frequencyTraceable", 0, 0, 1), + GLOB_ITEM_INT("ha_enabled", 0, 0, 1), ++ GLOB_ITEM_INT("ha_min_clockAccuracy", 0xfe, 0, 0xff), ++ GLOB_ITEM_INT("ha_min_gm_ClockClass", 135, 6, 255), ++ GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), ++ GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), ++ PORT_ITEM_INT("ha_priority", 0, 0, 255), ++ GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), + PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), + GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu), + PORT_ITEM_INT("hybrid_e2e", 0, 0, 1), +diff --git a/phc2sys.c b/phc2sys.c +index a4afe01..d148d62 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -73,6 +73,7 @@ + struct clock { + LIST_ENTRY(clock) list; + LIST_ENTRY(clock) dst_list; ++ LIST_ENTRY(clock) good_list; + clockid_t clkid; + int phc_index; + int sysoff_method; +@@ -1073,6 +1074,232 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + return 0; + } + ++static struct clock* startup_select_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ struct clock *clock = NULL, *best = NULL; ++ LIST_HEAD(head, clock) good_clocks; ++ int clock_priority, highest_priority; ++ int min_local_clock_class, min_gm_clock_class, clock_class, lowest_clock_class; ++ int err; ++ unsigned int min_clock_accuracy, min_offset_scaled_log_variance, retries; ++ bool check_time_traceable, check_freq_traceable; ++ ++ LIST_INIT(&good_clocks); ++ ++ /* get requirements */ ++ min_local_clock_class = config_get_int(cfg, NULL, "ha_min_local_clockClass"); ++ min_clock_accuracy = config_get_int(cfg, NULL, "ha_min_clockAccuracy"); ++ min_offset_scaled_log_variance = config_get_int(cfg, NULL, "ha_min_offsetScaledLogVariance"); ++ check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); ++ check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); ++ min_gm_clock_class = config_get_int(cfg, NULL, "ha_min_gm_ClockClass"); ++ ++ /* save a list of available source clocks that matches requirements */ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* check matching parameters */ ++ pr_debug("clock %s state %d", clock->device, clock->state); ++ ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ pr_debug("clock %s discarded because state is PS_MASTER", clock->device); ++ continue; ++ } ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s discarded because node is (null)", clock->device); ++ continue; ++ } ++ ++ /* get Default Data Set */ ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return NULL; ++ } ++ err = pmc_agent_query_dds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return NULL; ++ } ++ } ++ ++ if (!clock->node->dds_valid) { ++ pr_debug("clock %s discarded because dds is invalid", clock->device); ++ continue; ++ } ++ ++ /* min clockClass ++ as lower clock class is better, accept sources which clock class ++ is lower then or equal to min local clock class and discard ++ the sources which clock class is higher than min local clock class. ++ */ ++ clock_class = clock->node->dds.clockQuality.clockClass; ++ pr_debug("clock %s local clockClass %d", clock->device, clock_class); ++ if (clock_class > min_local_clock_class) { ++ pr_debug("clock %s discarded because local clock class %d > min clock class %d", ++ clock->device, clock_class, min_local_clock_class); ++ continue; ++ } ++ ++ /* min clockAccuracy (lower is better) */ ++ pr_debug("clock %s clockAccuracy 0x%x", clock->device, ++ clock->node->dds.clockQuality.clockAccuracy); ++ if (clock->node->dds.clockQuality.clockAccuracy > min_clock_accuracy) { ++ pr_debug("clock %s discarded because clock accuracy %d > min clock accuracy %d", ++ clock->device, clock->node->dds.clockQuality.clockAccuracy, ++ min_clock_accuracy); ++ continue; ++ } ++ ++ /* min offset scaled log variance */ ++ pr_debug("clock %s offsetScaledLogVariance 0x%x", clock->device, ++ clock->node->dds.clockQuality.offsetScaledLogVariance); ++ if (clock->node->dds.clockQuality.offsetScaledLogVariance > min_offset_scaled_log_variance) { ++ pr_debug("clock %s discarded because offset scaled log variance 0x%x > min offset 0x%x", ++ clock->device, clock->node->dds.clockQuality.offsetScaledLogVariance, ++ min_offset_scaled_log_variance); ++ continue; ++ } ++ ++ if (check_time_traceable || check_freq_traceable) { ++ /* get Time Properties Data Set */ ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return NULL; ++ } ++ err = pmc_agent_query_utc_offset(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return NULL; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because tds is invalid", clock->device); ++ continue; ++ } ++ } ++ ++ /* is time traceable */ ++ pr_debug("clock %s is time traceable %s", clock->device, ++ clock->node->utc_offset_traceable ? "yes" : "no"); ++ if (check_time_traceable && !clock->node->utc_offset_traceable) { ++ pr_debug("clock %s discarded because time is not traceable", clock->device); ++ continue; ++ } ++ ++ /* is frequency traceable */ ++ pr_debug("clock %s is frequency traceable %s", clock->device, ++ clock->node->freq_traceable ? "yes" : "no"); ++ if (check_freq_traceable && !clock->node->freq_traceable) { ++ pr_debug("clock %s discarded because frequency is not traceable", clock->device); ++ continue; ++ } ++ ++ retries = 0; ++ while (retries < 10) { ++ if (!is_running()) { ++ return NULL; ++ } ++ err = pmc_agent_query_pds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return NULL; ++ } ++ } ++ ++ if (!clock->node->pds_valid) { ++ pr_debug("clock %s discarded because pds is invalid", clock->device); ++ continue; ++ } ++ ++ /* min gm clock class - lower is better */ ++ clock_class = clock->node->pds.grandmasterClockQuality.clockClass; ++ pr_debug("clock %s GM clockClass %d", clock->device, clock_class); ++ if (clock_class > min_gm_clock_class) { ++ pr_debug("clock %s discarded because GM clock class %d > min clock class %d", ++ clock->device, clock_class, min_gm_clock_class); ++ continue; ++ } ++ ++ pr_notice("clock %s matched requirements", clock->device); ++ ++ clock->good_list.le_next = NULL; ++ clock->good_list.le_prev = NULL; ++ LIST_INSERT_HEAD(&good_clocks, clock, good_list); ++ } ++ ++ /* one or more sources match requirements, select highest priority */ ++ highest_priority = 0; ++ LIST_FOREACH(clock, &good_clocks, good_list) { ++ clock_priority = config_get_int(cfg, clock->device, "ha_priority"); ++ ++ /* select highest priority clock ++ more than one clock with same priority, select first ++ don't select clocks with ha_priority 0 */ ++ if (clock_priority > highest_priority) { ++ pr_notice("new highest ha priority clock %s ha_priority %d", ++ clock->device, clock_priority); ++ best = clock; ++ highest_priority = clock_priority; ++ } ++ } ++ ++ /* no sources match requirements, choose best available clockClass */ ++ if (!best) { ++ lowest_clock_class = 256; ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ continue; ++ } ++ ++ /* get clock class */ ++ clock_class = clock->node->dds.clockQuality.clockClass; ++ if (clock_class <= lowest_clock_class) { ++ pr_notice("new better clock class clock %s clock class %d", ++ clock->device, clock_class); ++ best = clock; ++ lowest_clock_class = clock_class; ++ } ++ } ++ } ++ ++ /* no clock selected, select first clock configured (last in list) */ ++ if (!best) { ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ continue; ++ } ++ ++ best = clock; ++ } ++ } ++ ++ if (best) ++ pr_notice("Best clock selected %s", best->device); ++ ++ return best; ++}; ++ + static void usage(char *progname) + { + fprintf(stderr, +@@ -1486,6 +1713,10 @@ int main(int argc, char *argv[]) + + ++i; + } ++ ++ if (ha_enabled) { ++ startup_select_clock(&priv, cfg); ++ } + } + + if (pps_fd >= 0) { +diff --git a/pmc_agent.c b/pmc_agent.c +index d13f569..534f483 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -351,15 +351,35 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + node->leap = 0; + node->utc_offset_traceable = tds->flags & UTC_OFF_VALID && + tds->flags & TIME_TRACEABLE; ++ node->freq_traceable = tds->flags & FREQ_TRACEABLE; + } else { + node->sync_offset = 0; + node->leap = 0; + node->utc_offset_traceable = 0; ++ node->freq_traceable = 0; + } + msg_put(msg); + return 0; + } + ++int pmc_agent_query_pds(struct pmc_agent *node, int timeout) ++{ ++ struct parentDS *pds; ++ struct ptp_message *msg; ++ int res; ++ ++ res = run_pmc(node, timeout, MID_PARENT_DATA_SET, &msg); ++ if (is_run_pmc_error(res)) { ++ return run_pmc_err2errno(res); ++ } ++ ++ pds = (struct parentDS *) management_tlv_data(msg); ++ memcpy(&node->pds, pds, sizeof(node->pds)); ++ node->pds_valid = true; ++ msg_put(msg); ++ return 0; ++} ++ + void pmc_agent_set_sync_offset(struct pmc_agent *agent, int offset) + { + agent->sync_offset = offset; +diff --git a/pmc_agent.h b/pmc_agent.h +index 5f25984..2bd7f02 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -41,7 +41,10 @@ struct pmc_agent { + bool stay_subscribed; + int sync_offset; + int utc_offset_traceable; ++ int freq_traceable; + unsigned int index; ++ struct parentDS pds; ++ bool pds_valid; + + /* Callback on message reception */ + pmc_node_recv_subscribed_t *recv_subscribed; +@@ -142,6 +145,16 @@ int pmc_agent_query_port_properties(struct pmc_agent *agent, int timeout, + */ + int pmc_agent_query_utc_offset(struct pmc_agent *agent, int timeout); + ++/** ++ * Queries the parent data set from the ptp4l service. ++ * The result of the query will be cached inside of the agent. ++ * ++ * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). ++ * @param timeout Transmit and receive timeout in milliseconds. ++ * @return Zero on success, negative error code otherwise. ++ */ ++int pmc_agent_query_pds(struct pmc_agent *agent, int timeout); ++ + /** + * Sets the TAI-UTC offset. + * @param agent Pointer to a PMC instance obtained via @ref pmc_agent_create(). +-- +2.25.1 + +From c61a91f5da1c07a783b0922e713c9f1d32adfa80 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Sat, 8 Jul 2023 19:02:50 -0300 +Subject: [PATCH 39/47] Select best source clock after state changes + +During operation, the clock states might change and require a new clock +to be selected. For example, the local clock class of the current active +clock has changed and it doesn't match requirements any more. + +Every 60 seconds the state of every configured clock is updated. The +clock state includes all the parameters used in the clock selection +algorithm. + +When the active clock degrades, the clock selection algorithm +is used to immediatialy promote a better source clock. + +When a higher priority clock recovers or starts to match the requirements, +a stability timer is started, and the active candidate clock is selected +when timer reaches threshold. If the stability timer is not configured +the switch is done immediatialy. + +The stability timer is retriggarable. It is started when a clock with higher +priority than the active becomes available, becaming the active candidate. +The timer is restarted when another clock becames the active candidate. + +The ha_stability_timer option is a global setting used to configure the +stability timer. Its value is expressed in seconds, and the value 0 +disables the timer. In other words, when ha_stability_timer is set +0 the clock change is done immediatialy on clock state change. + +When a clock with equal priority than the active becomes available, +the active clock must not be switched. Only if the active degrades +the other clock can be selected active. + +Test plan: equal priority clocks +PASS: Verify when the active clock state changes but still matches +requirements, the active clock doesn't change. +PASS: Verify when the active clock degrades and secondary clock becomes active. +PASS: Verify when the primary recovers the active clock doesn't change. +PASS: Verify when the secondary and active clock degrades, the primary becomes +active. + +Test plan: different priority clock +PASS: Verify the higher priority clock is selected active at startup. +PASS: Verify when the active and primary degrades the secondary is selected +active. +PASS: Verify when the primary recovers it is selected active. + +Test plan: stability timer +PASS: Verify when primary and active clock degrades the secondary becomes +active. +PASS: Verify when primary recovers the secondary is kept active until +stability timer has elapsed, then the primary becomes active. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit de9976fc57d3e8212f51f4c509da27c88d0a39d8 upstream] +[commit 44c06ab00d81245d4dfeb159c61f3c0dbf148d81 upstream] +[commit 44de5cf877cbe18dbec0341931b4bc745e61746e upstream] +[commit 61cf557b56805a1af9b7cbd0344f22c4acd9400c upstream] +[commit f7d915c89b949122d9cb9eef82588b73e6171619 upstream] +[commit 2aec3fc46e82375e9e48da9f5aa227ee3d885308 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 1 + + phc2sys.c | 672 ++++++++++++++++++++++++++++++++++------------------ + pmc_agent.c | 39 ++- + pmc_agent.h | 6 +- + 4 files changed, 481 insertions(+), 237 deletions(-) + +diff --git a/config.c b/config.c +index 8ce5f6c..1ad5157 100644 +--- a/config.c ++++ b/config.c +@@ -257,6 +257,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), + PORT_ITEM_INT("ha_priority", 0, 0, 255), ++ PORT_ITEM_INT("ha_stability_timer", 0, 0, INT_MAX), + GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), + PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), + GLOB_ITEM_ENU("hwts_filter", HWTS_FILTER_NORMAL, hwts_filter_enu), +diff --git a/phc2sys.c b/phc2sys.c +index d148d62..152e783 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -66,14 +66,14 @@ + + #define MAX_SRC_CLOCKS 128 + +-#define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) || (((unsigned int) index) & 0xFF)) ++#define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) | (((unsigned int) index) & 0xFF)) + #define PORT_ID_TO_PORT(id) ((((unsigned int) id) >> 8) & 0xFF) + #define PORT_ID_TO_INDEX(id) (((unsigned int) id) & 0xFF) + + struct clock { + LIST_ENTRY(clock) list; + LIST_ENTRY(clock) dst_list; +- LIST_ENTRY(clock) good_list; ++ LIST_ENTRY(clock) ha_list; + clockid_t clkid; + int phc_index; + int sysoff_method; +@@ -94,6 +94,7 @@ struct clock { + struct clockcheck *sanity_check; + struct pmc_agent *node; + }; ++typedef LIST_HEAD(head, clock) clock_list_head_t; + + struct port { + LIST_ENTRY(port) list; +@@ -111,11 +112,14 @@ struct phc2sys_private { + int forced_sync_offset; + int kernel_leap; + int state_changed; ++ int clock_state_changed; + LIST_HEAD(pmc_agent_head, pmc_agent) pmc_agents; + LIST_HEAD(port_head, port) ports; + LIST_HEAD(clock_head, clock) clocks; + LIST_HEAD(dst_clock_head, clock) dst_clocks; + struct clock *master; ++ struct clock *better; ++ struct timespec stability_timer; + int default_sync; + }; + +@@ -248,6 +252,235 @@ static void clock_cleanup(struct phc2sys_private *priv) + } + } + ++static struct clock *clock_get(struct phc2sys_private *priv, struct pmc_agent *node) ++{ ++ struct clock * clock = NULL; ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ if (clock->node == node) { ++ break; ++ } ++ } ++ return clock; ++} ++ ++static bool clock_match_ha_dds_requirements(struct clock *clock, struct config *cfg) ++{ ++ /* get requirements */ ++ int local_clock_class, min_local_clock_class = config_get_int(cfg, NULL, "ha_min_local_clockClass"); ++ unsigned int clock_accuracy, min_clock_accuracy = config_get_int(cfg, NULL, "ha_min_clockAccuracy"); ++ unsigned int offset, min_offset_scaled_log_variance = config_get_int(cfg, NULL, "ha_min_offsetScaledLogVariance"); ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s node is (null)", clock->device); ++ return false; ++ } ++ ++ if (!clock->node->dds_valid) { ++ pr_debug("clock %s dds is invalid", clock->device); ++ return false; ++ } ++ ++ /* min local clock class (lower is better) */ ++ local_clock_class = clock->node->dds.clockQuality.clockClass; ++ if (local_clock_class > min_local_clock_class) { ++ pr_debug("clock %s local clock class %d > min local clock class %d", ++ clock->device, local_clock_class, min_local_clock_class); ++ return false; ++ } ++ ++ /* min clock accuracy (lower is better) */ ++ clock_accuracy = clock->node->dds.clockQuality.clockAccuracy; ++ if (clock_accuracy > min_clock_accuracy) { ++ pr_debug("clock %s clock accuracy %d > min clock accuracy %d", ++ clock->device, clock_accuracy, min_clock_accuracy); ++ return false; ++ } ++ ++ /* min offset scaled log variance (lower is better) */ ++ offset = clock->node->dds.clockQuality.offsetScaledLogVariance; ++ if (offset > min_offset_scaled_log_variance) { ++ pr_debug("clock %s offset scaled log variance 0x%x > min offset 0x%x", ++ clock->device, offset, min_offset_scaled_log_variance); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool clock_match_ha_tpds_requirements(struct clock *clock, struct config *cfg) ++{ ++ /* get requirements */ ++ bool check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); ++ bool check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s node is (null)", clock->device); ++ return false; ++ } ++ ++ /* is time traceable */ ++ if (check_time_traceable && !clock->node->utc_offset_traceable) { ++ pr_debug("clock %s time is not traceable", clock->device); ++ return false; ++ } ++ ++ /* is frequency traceable */ ++ if (check_freq_traceable && !clock->node->freq_traceable) { ++ pr_debug("clock %s frequency is not traceable", clock->device); ++ return false; ++ } ++ ++ return true; ++} ++ ++static bool clock_match_ha_pds_requirements(struct clock *clock, struct config *cfg) ++{ ++ /* get requirements */ ++ int gm_clock_class, min_gm_clock_class = config_get_int(cfg, NULL, "ha_min_gm_ClockClass"); ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s node is (null)", clock->device); ++ return false; ++ } ++ ++ if (!clock->node->pds_valid) { ++ pr_debug("clock %s pds is invalid", clock->device); ++ return false; ++ } ++ ++ /* min gm clock class (lower is better) */ ++ gm_clock_class = clock->node->pds.grandmasterClockQuality.clockClass; ++ if (gm_clock_class > min_gm_clock_class) { ++ pr_debug("clock %s GM clock class %d > min clock class %d", ++ clock->device, gm_clock_class, min_gm_clock_class); ++ return false; ++ } ++ ++ return true; ++} ++ ++/* save a list of available source clocks that matches ha requirements */ ++static int clock_available_ha_src_clocks(struct phc2sys_private *priv, struct config *cfg, clock_list_head_t *available_clocks) ++{ ++ int err, retries; ++ struct clock *clock; ++ bool check_time_traceable, check_freq_traceable; ++ ++ LIST_INIT(available_clocks); ++ ++ check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); ++ check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ pr_debug("clock %s state %d", clock->device, clock->state); ++ ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ pr_debug("clock %s discarded because state is PS_MASTER", clock->device); ++ continue; ++ } ++ ++ /* sanity check */ ++ if (clock->node == NULL) { ++ pr_debug("clock %s discarded because node is (null)", clock->device); ++ continue; ++ } ++ ++ /* get Default Data Set */ ++ if (!clock->node->dds_valid) { ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_dds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because tds is invalid", clock->device); ++ continue; ++ } ++ } ++ ++ if (!clock_match_ha_dds_requirements(clock, cfg)) ++ continue; ++ ++ if (check_time_traceable || check_freq_traceable) { ++ /* get Time Properties Data Set */ ++ retries = 0; ++ while(retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_utc_offset(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because tds is invalid", clock->device); ++ continue; ++ } ++ ++ if (!clock_match_ha_tpds_requirements(clock, cfg)) ++ continue; ++ } ++ ++ /* get Parent Data Set */ ++ if (!clock->node->pds_valid) { ++ retries = 0; ++ while (retries < 10) { ++ if (!is_running()) { ++ return -1; ++ } ++ err = pmc_agent_query_pds(clock->node, 1000); ++ if (!err) { ++ break; ++ } ++ if (err == -ETIMEDOUT) { ++ pr_notice("Waiting for ptp4l..."); ++ retries++; ++ } else { ++ return -1; ++ } ++ } ++ ++ if (err != 0) { ++ pr_debug("clock %s discarded because pds is invalid", clock->device); ++ continue; ++ } ++ } ++ ++ if (!clock_match_ha_pds_requirements(clock, cfg)) ++ continue; ++ ++ clock->ha_list.le_next = NULL; ++ clock->ha_list.le_prev = NULL; ++ LIST_INSERT_HEAD(available_clocks, clock, ha_list); ++ } ++ ++ return 0; ++} ++ + static void port_cleanup(struct phc2sys_private *priv) + { + struct port *p, *tmp; +@@ -368,6 +601,10 @@ static void clock_reinit(struct phc2sys_private *priv, struct clock *clock, + + pmc_index = PORT_ID_TO_INDEX(p->number); + node = pmc_agent_get(priv, pmc_index); ++ if (!node) { ++ pr_warning("pmc node associated to port number %d not found", p->number); ++ continue; ++ } + err = pmc_agent_query_port_properties(node, 1000, + p->number, &state, + ×tamping, iface); +@@ -761,7 +998,159 @@ static int update_needed(struct clock *c) + return 0; + } + +-static int do_loop(struct phc2sys_private *priv, int subscriptions) ++static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ int clock_priority, highest_priority; ++ int clock_class, lowest_clock_class; ++ struct clock *clock = NULL, *best = NULL; ++ clock_list_head_t ha_available_clocks; ++ ++ /* save a list of available source clocks that matches requirements */ ++ if (clock_available_ha_src_clocks(priv, cfg, &ha_available_clocks) < 0) { ++ pr_err("failed to create ha available clock list"); ++ return NULL; ++ } ++ ++ /* one or more sources match requirements, select highest priority */ ++ highest_priority = 0; ++ LIST_FOREACH(clock, &ha_available_clocks, ha_list) { ++ clock_priority = config_get_int(cfg, clock->device, "ha_priority"); ++ ++ /* select highest priority clock ++ more than one clock with same priority, select first ++ don't select clocks with ha_priority 0 */ ++ if (clock_priority > highest_priority) { ++ pr_notice("new highest ha priority clock %s ha_priority %d", ++ clock->device, clock_priority); ++ best = clock; ++ highest_priority = clock_priority; ++ } ++ } ++ ++ /* no sources match requirements, choose best available clockClass */ ++ if (!best) { ++ lowest_clock_class = 256; ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (clock->node == NULL) ++ continue; ++ ++ /* get clock class */ ++ clock_class = clock->node->dds.clockQuality.clockClass; ++ if (clock_class <= lowest_clock_class) { ++ pr_notice("new better clock class clock %s clock class %d", ++ clock->device, clock_class); ++ best = clock; ++ lowest_clock_class = clock_class; ++ } ++ } ++ } ++ ++ /* no clock selected, select first clock configured (last in list) */ ++ if (!best) { ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (clock->node == NULL) ++ continue; ++ ++ best = clock; ++ } ++ } ++ ++ if (best) ++ pr_notice("Best clock selected %s", best->device); ++ ++ return best; ++} ++ ++static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ struct clock *active = priv->master, *candidate = NULL; ++ int stability_timer = 0; ++ struct timespec now; ++ int active_priority, candidate_priority; ++ int active_clock_class, candidate_clock_class; ++ ++ /* Active source degrades - re-run ha_select_clock algorithm */ ++ if ((active->node->new_dds && !clock_match_ha_dds_requirements(active, cfg)) || ++ (active->node->new_tpds && !clock_match_ha_tpds_requirements(active, cfg)) || ++ (active->node->new_pds && !clock_match_ha_pds_requirements(active, cfg))) { ++ ++ pr_notice("active clock %s has degraded", active->device); ++ ++ active->node->new_dds = false; ++ active->node->new_tpds = false; ++ active->node->new_pds = false; ++ ++ candidate = ha_select_clock(priv, cfg); ++ if (active != candidate) { ++ pr_notice("new source clock selected %s", candidate->device); ++ return candidate; ++ } ++ } ++ ++ /* Primary clock is active, secondary clock becomes better quality */ ++ /* Secondary clock is active, primary clock becomes better quality */ ++ ++ /* select best clock available */ ++ candidate = ha_select_clock(priv, cfg); ++ ++ if (active == candidate) { ++ /* active source still is or became the best clock available again */ ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } else { ++ /* new clock candidate */ ++ ++ /* candidate has equal priority and clockClass than active - don't change active */ ++ active_priority = config_get_int(cfg, active->device, "ha_priority"); ++ candidate_priority = config_get_int(cfg, candidate->device, "ha_priority"); ++ active_clock_class = active->node->dds.clockQuality.clockClass; ++ candidate_clock_class = candidate->node->dds.clockQuality.clockClass; ++ if ((active_priority == candidate_priority) && ++ (active_clock_class == candidate_clock_class)) { ++ return NULL; ++ } ++ ++ /* stability timer = 0 - change active */ ++ stability_timer = config_get_int(cfg, NULL, "ha_stability_timer"); ++ if (stability_timer == 0) { ++ pr_notice("new source clock selected %s", candidate->device); ++ return candidate; ++ } ++ ++ if (candidate != priv->better) { ++ priv->better = candidate; ++ /* start/restart stability timer */ ++ clock_gettime(CLOCK_REALTIME, &now); ++ priv->stability_timer.tv_sec = now.tv_sec + stability_timer; ++ priv->stability_timer.tv_nsec = now.tv_nsec; ++ } ++ } ++ ++ return NULL; ++} ++ ++static void reset_new_dataset_flags(struct phc2sys_private *priv) ++{ ++ struct pmc_agent *node; ++ LIST_FOREACH(node, &priv->pmc_agents, list) { ++ node->new_dds = false; ++ node->new_tpds = false; ++ node->new_pds = false; ++ } ++} ++ ++static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscriptions) + { + struct timespec interval; + struct clock *clock; +@@ -769,6 +1158,8 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + int64_t offset, delay; + int err; + struct pmc_agent *node = NULL; ++ int ha_enabled = config_get_int(cfg, NULL, "ha_enabled"); ++ struct timespec now; + + interval.tv_sec = priv->phc_interval; + interval.tv_nsec = (priv->phc_interval - interval.tv_sec) * 1e9; +@@ -781,6 +1172,10 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + continue; + } + ++ if (node->new_dds || node->new_tpds || node->new_pds) { ++ priv->clock_state_changed = 1; ++ } ++ + if (subscriptions) { + run_pmc_events(node); + if (priv->state_changed) { +@@ -798,6 +1193,35 @@ static int do_loop(struct phc2sys_private *priv, int subscriptions) + reconfigure(priv); + } + ++ if (ha_enabled) { ++ if (priv->clock_state_changed) { ++ clock = check_and_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } ++ ++ priv->clock_state_changed = 0; ++ reset_new_dataset_flags(priv); ++ } ++ ++ if (priv->better) { ++ /* has stability timer expired? */ ++ clock_gettime(CLOCK_REALTIME, &now); ++ if ((now.tv_sec > priv->stability_timer.tv_sec) || ++ (now.tv_sec == priv->stability_timer.tv_sec && ++ now.tv_nsec > priv->stability_timer.tv_nsec)) { ++ pr_notice("new source clock selected %s", priv->better->device); ++ priv->master = priv->better; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } ++ } ++ } ++ + if (!priv->master) + continue; + +@@ -883,21 +1307,25 @@ static int clock_compute_state(struct phc2sys_private *priv, + return state; + } + +-static int phc2sys_recv_subscribed(void *context, struct ptp_message *msg, ++static int phc2sys_recv_subscribed(struct pmc_agent *node, void *context, struct ptp_message *msg, + int excluded) + { + struct phc2sys_private *priv = (struct phc2sys_private *) context; + int mgt_id, state; + struct portDS *pds; ++ struct defaultDS *dds; ++ struct parentDS *parentds; ++ struct timePropertiesDS *tds; + struct port *port; + struct clock *clock; ++ int utc_offset_traceable, freq_traceable; + + mgt_id = management_tlv_id(msg); + if (mgt_id == excluded) + return 0; + switch (mgt_id) { + case MID_PORT_DATA_SET: +- pds = management_tlv_data(msg); ++ pds = (struct portDS *)management_tlv_data(msg); + port = port_get(priv, pds->portIdentity.portNumber); + if (!port) { + pr_info("received data for unknown port %s", +@@ -1074,232 +1502,6 @@ static int clock_handle_leap(struct phc2sys_private *priv, struct clock *clock, + return 0; + } + +-static struct clock* startup_select_clock(struct phc2sys_private *priv, struct config *cfg) +-{ +- struct clock *clock = NULL, *best = NULL; +- LIST_HEAD(head, clock) good_clocks; +- int clock_priority, highest_priority; +- int min_local_clock_class, min_gm_clock_class, clock_class, lowest_clock_class; +- int err; +- unsigned int min_clock_accuracy, min_offset_scaled_log_variance, retries; +- bool check_time_traceable, check_freq_traceable; +- +- LIST_INIT(&good_clocks); +- +- /* get requirements */ +- min_local_clock_class = config_get_int(cfg, NULL, "ha_min_local_clockClass"); +- min_clock_accuracy = config_get_int(cfg, NULL, "ha_min_clockAccuracy"); +- min_offset_scaled_log_variance = config_get_int(cfg, NULL, "ha_min_offsetScaledLogVariance"); +- check_time_traceable = config_get_int(cfg, NULL, "ha_timeTraceable"); +- check_freq_traceable = config_get_int(cfg, NULL, "ha_frequencyTraceable"); +- min_gm_clock_class = config_get_int(cfg, NULL, "ha_min_gm_ClockClass"); +- +- /* save a list of available source clocks that matches requirements */ +- LIST_FOREACH(clock, &priv->clocks, list) { +- /* check matching parameters */ +- pr_debug("clock %s state %d", clock->device, clock->state); +- +- /* ignore the dst clock */ +- if (clock->state == PS_MASTER) { +- pr_debug("clock %s discarded because state is PS_MASTER", clock->device); +- continue; +- } +- +- /* sanity check */ +- if (clock->node == NULL) { +- pr_debug("clock %s discarded because node is (null)", clock->device); +- continue; +- } +- +- /* get Default Data Set */ +- retries = 0; +- while(retries < 10) { +- if (!is_running()) { +- return NULL; +- } +- err = pmc_agent_query_dds(clock->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- retries++; +- } else { +- return NULL; +- } +- } +- +- if (!clock->node->dds_valid) { +- pr_debug("clock %s discarded because dds is invalid", clock->device); +- continue; +- } +- +- /* min clockClass +- as lower clock class is better, accept sources which clock class +- is lower then or equal to min local clock class and discard +- the sources which clock class is higher than min local clock class. +- */ +- clock_class = clock->node->dds.clockQuality.clockClass; +- pr_debug("clock %s local clockClass %d", clock->device, clock_class); +- if (clock_class > min_local_clock_class) { +- pr_debug("clock %s discarded because local clock class %d > min clock class %d", +- clock->device, clock_class, min_local_clock_class); +- continue; +- } +- +- /* min clockAccuracy (lower is better) */ +- pr_debug("clock %s clockAccuracy 0x%x", clock->device, +- clock->node->dds.clockQuality.clockAccuracy); +- if (clock->node->dds.clockQuality.clockAccuracy > min_clock_accuracy) { +- pr_debug("clock %s discarded because clock accuracy %d > min clock accuracy %d", +- clock->device, clock->node->dds.clockQuality.clockAccuracy, +- min_clock_accuracy); +- continue; +- } +- +- /* min offset scaled log variance */ +- pr_debug("clock %s offsetScaledLogVariance 0x%x", clock->device, +- clock->node->dds.clockQuality.offsetScaledLogVariance); +- if (clock->node->dds.clockQuality.offsetScaledLogVariance > min_offset_scaled_log_variance) { +- pr_debug("clock %s discarded because offset scaled log variance 0x%x > min offset 0x%x", +- clock->device, clock->node->dds.clockQuality.offsetScaledLogVariance, +- min_offset_scaled_log_variance); +- continue; +- } +- +- if (check_time_traceable || check_freq_traceable) { +- /* get Time Properties Data Set */ +- retries = 0; +- while(retries < 10) { +- if (!is_running()) { +- return NULL; +- } +- err = pmc_agent_query_utc_offset(clock->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- retries++; +- } else { +- return NULL; +- } +- } +- +- if (err != 0) { +- pr_debug("clock %s discarded because tds is invalid", clock->device); +- continue; +- } +- } +- +- /* is time traceable */ +- pr_debug("clock %s is time traceable %s", clock->device, +- clock->node->utc_offset_traceable ? "yes" : "no"); +- if (check_time_traceable && !clock->node->utc_offset_traceable) { +- pr_debug("clock %s discarded because time is not traceable", clock->device); +- continue; +- } +- +- /* is frequency traceable */ +- pr_debug("clock %s is frequency traceable %s", clock->device, +- clock->node->freq_traceable ? "yes" : "no"); +- if (check_freq_traceable && !clock->node->freq_traceable) { +- pr_debug("clock %s discarded because frequency is not traceable", clock->device); +- continue; +- } +- +- retries = 0; +- while (retries < 10) { +- if (!is_running()) { +- return NULL; +- } +- err = pmc_agent_query_pds(clock->node, 1000); +- if (!err) { +- break; +- } +- if (err == -ETIMEDOUT) { +- pr_notice("Waiting for ptp4l..."); +- retries++; +- } else { +- return NULL; +- } +- } +- +- if (!clock->node->pds_valid) { +- pr_debug("clock %s discarded because pds is invalid", clock->device); +- continue; +- } +- +- /* min gm clock class - lower is better */ +- clock_class = clock->node->pds.grandmasterClockQuality.clockClass; +- pr_debug("clock %s GM clockClass %d", clock->device, clock_class); +- if (clock_class > min_gm_clock_class) { +- pr_debug("clock %s discarded because GM clock class %d > min clock class %d", +- clock->device, clock_class, min_gm_clock_class); +- continue; +- } +- +- pr_notice("clock %s matched requirements", clock->device); +- +- clock->good_list.le_next = NULL; +- clock->good_list.le_prev = NULL; +- LIST_INSERT_HEAD(&good_clocks, clock, good_list); +- } +- +- /* one or more sources match requirements, select highest priority */ +- highest_priority = 0; +- LIST_FOREACH(clock, &good_clocks, good_list) { +- clock_priority = config_get_int(cfg, clock->device, "ha_priority"); +- +- /* select highest priority clock +- more than one clock with same priority, select first +- don't select clocks with ha_priority 0 */ +- if (clock_priority > highest_priority) { +- pr_notice("new highest ha priority clock %s ha_priority %d", +- clock->device, clock_priority); +- best = clock; +- highest_priority = clock_priority; +- } +- } +- +- /* no sources match requirements, choose best available clockClass */ +- if (!best) { +- lowest_clock_class = 256; +- LIST_FOREACH(clock, &priv->clocks, list) { +- /* ignore the dst clock */ +- if (clock->state == PS_MASTER) { +- continue; +- } +- +- /* get clock class */ +- clock_class = clock->node->dds.clockQuality.clockClass; +- if (clock_class <= lowest_clock_class) { +- pr_notice("new better clock class clock %s clock class %d", +- clock->device, clock_class); +- best = clock; +- lowest_clock_class = clock_class; +- } +- } +- } +- +- /* no clock selected, select first clock configured (last in list) */ +- if (!best) { +- LIST_FOREACH(clock, &priv->clocks, list) { +- /* ignore the dst clock */ +- if (clock->state == PS_MASTER) { +- continue; +- } +- +- best = clock; +- } +- } +- +- if (best) +- pr_notice("Best clock selected %s", best->device); +- +- return best; +-}; +- + static void usage(char *progname) + { + fprintf(stderr, +@@ -1359,6 +1561,8 @@ int main(int argc, char *argv[]) + .phc_readings = 5, + .phc_interval = 1.0, + .master = NULL, ++ .better = NULL, ++ .stability_timer.tv_sec = 0, + }; + struct pmc_agent *node = NULL; + unsigned int i, src_cnt = 0; +@@ -1616,7 +1820,7 @@ int main(int argc, char *argv[]) + goto end; + if (auto_init_ports(&priv, rt) < 0) + goto end; +- r = do_loop(&priv, 1); ++ r = do_loop(&priv, cfg, 1); + goto end; + } + +@@ -1715,7 +1919,7 @@ int main(int argc, char *argv[]) + } + + if (ha_enabled) { +- startup_select_clock(&priv, cfg); ++ priv.master = ha_select_clock(&priv, cfg); + } + } + +@@ -1725,7 +1929,7 @@ int main(int argc, char *argv[]) + servo_sync_interval(dst->servo, 1.0); + r = do_pps_loop(&priv, dst, pps_fd); + } else { +- r = do_loop(&priv, 0); ++ r = do_loop(&priv, cfg, 0); + } + + end: +diff --git a/pmc_agent.c b/pmc_agent.c +index 534f483..af15710 100644 +--- a/pmc_agent.c ++++ b/pmc_agent.c +@@ -162,7 +162,7 @@ static int run_pmc(struct pmc_agent *node, int timeout, int ds_id, + return RUN_PMC_NODEV; + } + if (res <= 0 || +- node->recv_subscribed(node->recv_context, *msg, ds_id) || ++ node->recv_subscribed(node, node->recv_context, *msg, ds_id) || + management_tlv_id(*msg) != ds_id) { + msg_put(*msg); + *msg = NULL; +@@ -280,12 +280,21 @@ int pmc_agent_query_dds(struct pmc_agent *node, int timeout) + struct ptp_message *msg; + struct defaultDS *dds; + int res; ++ struct ClockQuality *current, *new; + + res = run_pmc(node, timeout, MID_DEFAULT_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } + dds = (struct defaultDS *) management_tlv_data(msg); ++ current = &node->dds.clockQuality; ++ new = &dds->clockQuality; ++ ++ if ((current->clockClass != new->clockClass) || ++ (current->clockAccuracy != new->clockAccuracy) || ++ (current->offsetScaledLogVariance != new->offsetScaledLogVariance)) { ++ node->new_dds = true; ++ } + memcpy(&node->dds, dds, sizeof(node->dds)); + node->dds_valid = true; + msg_put(msg); +@@ -334,12 +343,19 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + struct timePropertiesDS *tds; + struct ptp_message *msg; + int res; ++ int sync_offset, leap, utc_offset_traceable, freq_traceable; + + res = run_pmc(node, timeout, MID_TIME_PROPERTIES_DATA_SET, &msg); + if (is_run_pmc_error(res)) { + return run_pmc_err2errno(res); + } + ++ /* save current state */ ++ sync_offset = node->sync_offset; ++ leap = node->leap; ++ utc_offset_traceable = node->utc_offset_traceable; ++ freq_traceable = node->freq_traceable; ++ + tds = (struct timePropertiesDS *) management_tlv_data(msg); + if (tds->flags & PTP_TIMESCALE) { + node->sync_offset = tds->currentUtcOffset; +@@ -358,6 +374,15 @@ int pmc_agent_query_utc_offset(struct pmc_agent *node, int timeout) + node->utc_offset_traceable = 0; + node->freq_traceable = 0; + } ++ ++ /* compare to new tpds */ ++ if ((sync_offset != node->sync_offset) || ++ (leap != node->leap) || ++ (utc_offset_traceable != node->utc_offset_traceable) || ++ (freq_traceable != node->freq_traceable)) { ++ node->new_tpds = true; ++ } ++ + msg_put(msg); + return 0; + } +@@ -367,6 +392,7 @@ int pmc_agent_query_pds(struct pmc_agent *node, int timeout) + struct parentDS *pds; + struct ptp_message *msg; + int res; ++ struct ClockQuality *current, *new; + + res = run_pmc(node, timeout, MID_PARENT_DATA_SET, &msg); + if (is_run_pmc_error(res)) { +@@ -374,6 +400,11 @@ int pmc_agent_query_pds(struct pmc_agent *node, int timeout) + } + + pds = (struct parentDS *) management_tlv_data(msg); ++ current = &node->pds.grandmasterClockQuality; ++ new = &pds->grandmasterClockQuality; ++ if (current->clockClass != new->clockClass) { ++ node->new_pds = true; ++ } + memcpy(&node->pds, pds, sizeof(node->pds)); + node->pds_valid = true; + msg_put(msg); +@@ -396,6 +427,7 @@ int pmc_agent_update(struct pmc_agent *node) + struct ptp_message *msg; + struct timespec tp; + uint64_t ts; ++ int r; + + if (!node->pmc) { + return 0; +@@ -410,7 +442,10 @@ int pmc_agent_update(struct pmc_agent *node) + if (node->stay_subscribed) { + renew_subscription(node, 0); + } +- if (!pmc_agent_query_utc_offset(node, 0)) { ++ r = pmc_agent_query_utc_offset(node, 0); ++ r += pmc_agent_query_dds(node, 0); ++ r += pmc_agent_query_pds(node, 0); ++ if (!r) { + node->pmc_last_update = ts; + } + } +diff --git a/pmc_agent.h b/pmc_agent.h +index 2bd7f02..8207c46 100644 +--- a/pmc_agent.h ++++ b/pmc_agent.h +@@ -26,7 +26,8 @@ + + #include "pmc_common.h" + +-typedef int pmc_node_recv_subscribed_t(void *context, struct ptp_message *msg, ++struct pmc_agent; ++typedef int pmc_node_recv_subscribed_t(struct pmc_agent* node, void *context, struct ptp_message *msg, + int excluded); + + struct pmc_agent { +@@ -36,15 +37,18 @@ struct pmc_agent { + + struct defaultDS dds; + bool dds_valid; ++ bool new_dds; + int leap; + int pmc_ds_requested; + bool stay_subscribed; + int sync_offset; + int utc_offset_traceable; + int freq_traceable; ++ bool new_tpds; + unsigned int index; + struct parentDS pds; + bool pds_valid; ++ bool new_pds; + + /* Callback on message reception */ + pmc_node_recv_subscribed_t *recv_subscribed; +-- +2.25.1 + +From 7d5061d971a8abc2ba8443edccde38e9a7a6f0ce Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Wed, 26 Jul 2023 15:08:15 -0300 +Subject: [PATCH 40/47] Forced lock a clock source in configuration + +To help on maintenance and debuging tasks was implemented a configuration +to forced lock to a single clock. It disables the automatic clock +selection algorithm and lock to a source interface. + +When an interface is configured with maximum ha_priority (254) +the source selection is locked to it, regardless of its clock +status. + +When more than one source clock is configured with ha_priority 254 +selects the 1st interface in the configuration file. + +Test plan: forced lock by configuration +PASS: Verify the clock source is forced lock to an interface, regardless +its state. +PASS: Verify the clock source remains locked event after change the clock +state. +PASS: Verify the 1st configured interface with priority 254 is selected +when multiple interfaces has the same priority. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit 9563a04ef76cda55f9f014150270dbd320ca4bc4 upstream] +[commit 655fe5e304386b4494d864638ca972c4bd892e52 upstream] +[commit 3200a16f4cbe2d125bf301827a24d3d01e7f1c70 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 2 +- + phc2sys.c | 105 ++++++++++++++++++++++++++++++++++++++---------------- + 2 files changed, 75 insertions(+), 32 deletions(-) + +diff --git a/config.c b/config.c +index 1ad5157..dba1eef 100644 +--- a/config.c ++++ b/config.c +@@ -256,7 +256,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("ha_min_gm_ClockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), +- PORT_ITEM_INT("ha_priority", 0, 0, 255), ++ PORT_ITEM_INT("ha_priority", 0, 0, 254), + PORT_ITEM_INT("ha_stability_timer", 0, 0, INT_MAX), + GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), + PORT_ITEM_STR("ha_uds_address", "/var/run/ptp4l"), +diff --git a/phc2sys.c b/phc2sys.c +index 152e783..0b3f724 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -64,6 +64,7 @@ + + #define PHC_PPS_OFFSET_LIMIT 10000000 + ++#define FORCED_SOURCE_CLOCK_PRIORITY 254 + #define MAX_SRC_CLOCKS 128 + + #define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) | (((unsigned int) index) & 0xFF)) +@@ -121,6 +122,7 @@ struct phc2sys_private { + struct clock *better; + struct timespec stability_timer; + int default_sync; ++ int forced_source_clock; + }; + + static struct config *phc2sys_config; +@@ -998,6 +1000,29 @@ static int update_needed(struct clock *c) + return 0; + } + ++/* check configuration if one of the source clocks is force locked to be active */ ++static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct config *cfg) ++{ ++ int clock_priority; ++ struct clock *clock = NULL, *best = NULL; ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) { ++ continue; ++ } ++ ++ clock_priority = config_get_int(cfg, clock->device, "ha_priority"); ++ if (FORCED_SOURCE_CLOCK_PRIORITY == clock_priority) { ++ pr_info("HA automatic source selection is disabled by configuration"); ++ priv->forced_source_clock = 1; ++ best = clock; ++ } ++ } ++ ++ return best; ++} ++ + static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config *cfg) + { + int clock_priority, highest_priority; +@@ -1066,7 +1091,7 @@ static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config + } + + if (best) +- pr_notice("Best clock selected %s", best->device); ++ pr_notice("best clock available %s", best->device); + + return best; + } +@@ -1121,7 +1146,7 @@ static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct + return NULL; + } + +- /* stability timer = 0 - change active */ ++ /* stability timer equal 0 - change active */ + stability_timer = config_get_int(cfg, NULL, "ha_stability_timer"); + if (stability_timer == 0) { + pr_notice("new source clock selected %s", candidate->device); +@@ -1173,6 +1198,10 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (node->new_dds || node->new_tpds || node->new_pds) { ++ pr_debug("pmc agent index %d clock state changed by %s%s%s", ++ node->index, node->new_dds ? "new dds " : "", ++ node->new_tpds ? "new tpds " : "", ++ node->new_pds ? "new pds " : ""); + priv->clock_state_changed = 1; + } + +@@ -1194,30 +1223,38 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (ha_enabled) { +- if (priv->clock_state_changed) { +- clock = check_and_select_clock(priv, cfg); +- if (clock && clock != priv->master) { +- priv->master = clock; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ if (priv->forced_source_clock) { ++ /* HA automatic clock selection is disabled */ ++ if (priv->clock_state_changed) { ++ priv->clock_state_changed = 0; ++ reset_new_dataset_flags(priv); + } ++ } else { ++ if (priv->clock_state_changed) { ++ clock = check_and_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } + +- priv->clock_state_changed = 0; +- reset_new_dataset_flags(priv); +- } ++ priv->clock_state_changed = 0; ++ reset_new_dataset_flags(priv); ++ } + +- if (priv->better) { +- /* has stability timer expired? */ +- clock_gettime(CLOCK_REALTIME, &now); +- if ((now.tv_sec > priv->stability_timer.tv_sec) || +- (now.tv_sec == priv->stability_timer.tv_sec && +- now.tv_nsec > priv->stability_timer.tv_nsec)) { +- pr_notice("new source clock selected %s", priv->better->device); +- priv->master = priv->better; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ if (priv->better) { ++ /* has stability timer expired? */ ++ clock_gettime(CLOCK_REALTIME, &now); ++ if ((now.tv_sec > priv->stability_timer.tv_sec) || ++ (now.tv_sec == priv->stability_timer.tv_sec && ++ now.tv_nsec > priv->stability_timer.tv_nsec)) { ++ pr_notice("new source clock selected %s", priv->better->device); ++ priv->master = priv->better; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ } + } + } + } +@@ -1313,12 +1350,8 @@ static int phc2sys_recv_subscribed(struct pmc_agent *node, void *context, struct + struct phc2sys_private *priv = (struct phc2sys_private *) context; + int mgt_id, state; + struct portDS *pds; +- struct defaultDS *dds; +- struct parentDS *parentds; +- struct timePropertiesDS *tds; + struct port *port; + struct clock *clock; +- int utc_offset_traceable, freq_traceable; + + mgt_id = management_tlv_id(msg); + if (mgt_id == excluded) +@@ -1563,6 +1596,7 @@ int main(int argc, char *argv[]) + .master = NULL, + .better = NULL, + .stability_timer.tv_sec = 0, ++ .forced_source_clock = 0, + }; + struct pmc_agent *node = NULL; + unsigned int i, src_cnt = 0; +@@ -1861,13 +1895,19 @@ int main(int argc, char *argv[]) + goto bad_usage; + } + ++ if (ha_enabled) { ++ src = ha_forced_source_clock(&priv, cfg); ++ if (src != NULL) { ++ pr_info("Only interface %s will be used as source clock", src->device); ++ priv.master = src; ++ } ++ } ++ + r = -1; + + if (wait_sync) { + i = 0; +- for (src = LIST_FIRST(&priv.clocks); +- src != NULL; +- src = LIST_NEXT(src, list)) { ++ LIST_FOREACH(src, &priv.clocks, list) { + + /* skip dst clock */ + if (src == dst) { +@@ -1890,6 +1930,8 @@ int main(int argc, char *argv[]) + + /* map clock to pmc agent node */ + src->node = node; ++ pr_debug("pmc node index %d source clock %s initialized", ++ node->index, src->device); + + while (is_running()) { + r = run_pmc_wait_sync(node, 1000); +@@ -1918,8 +1960,9 @@ int main(int argc, char *argv[]) + ++i; + } + +- if (ha_enabled) { ++ if (ha_enabled && !priv.forced_source_clock) { + priv.master = ha_select_clock(&priv, cfg); ++ pr_info("interface %s will be used as source clock", priv.master->device); + } + } + +-- +2.25.1 + +From fce993dd36e481aace337a62ff81331cd2411bec Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Thu, 27 Jul 2023 14:22:47 -0300 +Subject: [PATCH 41/47] HA phc2sys com socket + +A new communication path was created to retrieve status and to control +the high availability algorithm. + +The ha_phc2sys_com_socket option is a global setting to configure +the socket path. Its default value is /var/run/phc2sys. + +The command 'status' was created to retrieve the current HA clock status. +The answer is a table of configured clocks and its status. + +act interface priority clockClass clockAcc offset time freq gm. + + * ens2f1 200 248 0xfe 0xffff no no 6 + ens1f2 100 248 0xfe 0xffff no no 6 + +Source forced? no + +The * sign marks the active source clock. +The - sign marks the active candidate source clock, which will be set active +after the stability timer expiration. +The x sign marks the disabled interfaces (see 'disable source' command). + +The 'Source forced?' field shows if the active source is forced lock or not. + +The 'clock source' command can be used to retrive the active +clock source. It returns the interface name of the active +clock source or "None" when there is no one select. + +The 'forced lock' command can be used to retrieve if the active +clock source is forced lock, and the clock source selection +algorithm is disabled. It returns "True" when is forced lock +and "False" otherwise. + +Test plan: socket path configuration +PASS Verify the socket using the default path. +PASS Verify the socket using a given socket path. + +Test plan: status command +PASS: Verify the 'status' command after start up. +PASS: Verify the 'status' command while stability timer is running. +PASS: Verify the 'status' command with a forced lock interface by +configuring ha_priority 254. + +Test plan: clock source command +PASS: Verify the 'clock source' command response is the highest priority +interface after start up. +PASS: Verify the 'clock source' command response is the active interface +after the primary has degraded. +PASS: Verify the 'clock source' command response is the forced lock +interface, when ha_priority 254 is configured in one of them. + +Test plan: forced lock command +PASS: Verify the 'forced lock' command response is 'False' when no +interface is configured with ha_priority 254. +PASS: Verify the 'forced lock' command response is 'True' when one +interface is configured with ha_priority 254. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + +[commit 0cfcbb78485a83d324963130f9558fd0a1962a79 upstream] +[commit 73b9afa33a0d8dcfd9c4ebb7bceacee40af8eb2b upstream] +[commit 6e93059d34639a3c2aac6b56dcf94ddf1e48e9b4 upstream] +[commit 4f118cf954bc3543582765bc039c42aeac05caf5 upstream] +[commit 6387ddf644afcb880b67368be8416b8ce906e029 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + config.c | 1 + + phc2sys.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++---- + 2 files changed, 216 insertions(+), 16 deletions(-) + +diff --git a/config.c b/config.c +index dba1eef..6a1bfb4 100644 +--- a/config.c ++++ b/config.c +@@ -256,6 +256,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_INT("ha_min_gm_ClockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_local_clockClass", 135, 6, 255), + GLOB_ITEM_INT("ha_min_offsetScaledLogVariance", 65535, 0, 65535), ++ GLOB_ITEM_STR("ha_phc2sys_com_socket", "/var/run/phc2sys-phc-inst1"), + PORT_ITEM_INT("ha_priority", 0, 0, 254), + PORT_ITEM_INT("ha_stability_timer", 0, 0, INT_MAX), + GLOB_ITEM_INT("ha_timeTraceable", 0, 0, 1), +diff --git a/phc2sys.c b/phc2sys.c +index 0b3f724..0bc3709 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -66,6 +66,9 @@ + + #define FORCED_SOURCE_CLOCK_PRIORITY 254 + #define MAX_SRC_CLOCKS 128 ++#define HA_SCK_N_FD 1 ++#define HA_SCK_BUFFER_SIZE 1024 ++#define HA_SCK_FILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) /*0660*/ + + #define PORT_INDEX_TO_PORT_ID(port, index) (((((unsigned int) port) & 0xFF) << 8) | (((unsigned int) index) & 0xFF)) + #define PORT_ID_TO_PORT(id) ((((unsigned int) id) >> 8) & 0xFF) +@@ -94,6 +97,7 @@ struct clock { + struct stats *delay_stats; + struct clockcheck *sanity_check; + struct pmc_agent *node; ++ int ha_priority; + }; + typedef LIST_HEAD(head, clock) clock_list_head_t; + +@@ -123,6 +127,7 @@ struct phc2sys_private { + struct timespec stability_timer; + int default_sync; + int forced_source_clock; ++ int ha_socket_fd; + }; + + static struct config *phc2sys_config; +@@ -1003,7 +1008,6 @@ static int update_needed(struct clock *c) + /* check configuration if one of the source clocks is force locked to be active */ + static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct config *cfg) + { +- int clock_priority; + struct clock *clock = NULL, *best = NULL; + + LIST_FOREACH(clock, &priv->clocks, list) { +@@ -1012,8 +1016,7 @@ static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct + continue; + } + +- clock_priority = config_get_int(cfg, clock->device, "ha_priority"); +- if (FORCED_SOURCE_CLOCK_PRIORITY == clock_priority) { ++ if (FORCED_SOURCE_CLOCK_PRIORITY == clock->ha_priority) { + pr_info("HA automatic source selection is disabled by configuration"); + priv->forced_source_clock = 1; + best = clock; +@@ -1025,7 +1028,7 @@ static struct clock* ha_forced_source_clock(struct phc2sys_private *priv, struct + + static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config *cfg) + { +- int clock_priority, highest_priority; ++ int highest_priority; + int clock_class, lowest_clock_class; + struct clock *clock = NULL, *best = NULL; + clock_list_head_t ha_available_clocks; +@@ -1038,17 +1041,14 @@ static struct clock* ha_select_clock(struct phc2sys_private *priv, struct config + + /* one or more sources match requirements, select highest priority */ + highest_priority = 0; +- LIST_FOREACH(clock, &ha_available_clocks, ha_list) { +- clock_priority = config_get_int(cfg, clock->device, "ha_priority"); +- +- /* select highest priority clock ++ LIST_FOREACH(clock, &ha_available_clocks, ha_list) {/* select highest priority clock + more than one clock with same priority, select first + don't select clocks with ha_priority 0 */ +- if (clock_priority > highest_priority) { ++ if (clock->ha_priority > highest_priority) { + pr_notice("new highest ha priority clock %s ha_priority %d", +- clock->device, clock_priority); ++ clock->device, clock->ha_priority); + best = clock; +- highest_priority = clock_priority; ++ highest_priority = clock->ha_priority; + } + } + +@@ -1101,7 +1101,6 @@ static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct + struct clock *active = priv->master, *candidate = NULL; + int stability_timer = 0; + struct timespec now; +- int active_priority, candidate_priority; + int active_clock_class, candidate_clock_class; + + /* Active source degrades - re-run ha_select_clock algorithm */ +@@ -1137,11 +1136,9 @@ static struct clock* check_and_select_clock(struct phc2sys_private *priv, struct + /* new clock candidate */ + + /* candidate has equal priority and clockClass than active - don't change active */ +- active_priority = config_get_int(cfg, active->device, "ha_priority"); +- candidate_priority = config_get_int(cfg, candidate->device, "ha_priority"); + active_clock_class = active->node->dds.clockQuality.clockClass; + candidate_clock_class = candidate->node->dds.clockQuality.clockClass; +- if ((active_priority == candidate_priority) && ++ if ((active->ha_priority == candidate->ha_priority) && + (active_clock_class == candidate_clock_class)) { + return NULL; + } +@@ -1175,6 +1172,196 @@ static void reset_new_dataset_flags(struct phc2sys_private *priv) + } + } + ++static int ha_com_socket_close(int fd) ++{ ++ struct sockaddr_un sa; ++ socklen_t len = sizeof(sa); ++ ++ // if (fd < 0) ++ // return -1; ++ ++ if (!getsockname(fd, (struct sockaddr *) &sa, &len) && ++ sa.sun_family == AF_LOCAL) { ++ unlink(sa.sun_path); ++ } ++ ++ close(fd); ++ return 0; ++} ++ ++static int ha_com_socket_open(int *fd_out, struct config *cfg) ++{ ++ int fd, err; ++ struct sockaddr_un sa; ++ const char *name = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); ++ ++ fd = socket(AF_LOCAL, SOCK_DGRAM, 0); ++ if (fd < 0) { ++ pr_err("ha_com_socket: failed to create socket: %m"); ++ return -1; ++ } ++ ++ memset(&sa, 0, sizeof(sa)); ++ sa.sun_family = AF_LOCAL; ++ strncpy(sa.sun_path, name, sizeof(sa.sun_path) - 1); ++ ++ err = bind(fd, (struct sockaddr *) &sa, sizeof(sa)); ++ if (err < 0) { ++ pr_err("ha_com_socket: bind failed: %m"); ++ close(fd); ++ return -1; ++ } ++ ++ *fd_out = fd; ++ chmod(name, HA_SCK_FILEMODE); ++ ++ return 0; ++} ++ ++static int ha_com_socket_recv(int fd, void *buf, size_t buflen, ++ struct address *addr) ++{ ++ int cnt; ++ ++ addr->len = sizeof(addr->sun); ++ cnt = recvfrom(fd, buf, buflen, 0, &addr->sa, &addr->len); ++ if (cnt <= 0) { ++ pr_err("ha_com_socket: recvfrom failed: %m"); ++ return cnt; ++ } ++ ++ ((char*)buf)[cnt] = '\0'; ++ ++ return 0; ++} ++ ++static int ha_com_socket_send(int fd, struct address *addr, void *buf, ++ size_t buflen) ++{ ++ int cnt; ++ ++ cnt = sendto(fd, buf, buflen, 0, &addr->sa, addr->len); ++ if (cnt < 1) { ++ return -errno; ++ } ++ return cnt; ++} ++ ++static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, ++ size_t resplen) ++{ ++ struct clock *clock; ++ size_t curlen = 0; ++ ++ /* header */ ++ curlen = snprintf(response, resplen, ++ "act interface priority clockClass clockAcc offset time freq " ++ "gm.clockClass\n\n"); ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (clock->node == NULL) ++ continue; ++ ++ curlen += snprintf(response + curlen, resplen - curlen, ++ " %c %9s %8d %10d 0x%2x 0x%4x %s %s %d\n", ++ (priv->master == clock) ? '*' : ++ (priv->better == clock) ? '-' : ' ', ++ clock->device, clock->ha_priority, ++ clock->node->dds.clockQuality.clockClass, ++ clock->node->dds.clockQuality.clockAccuracy, ++ clock->node->dds.clockQuality.offsetScaledLogVariance, ++ clock->node->utc_offset_traceable ? "yes" : "no ", ++ clock->node->freq_traceable ? "yes" : "no ", ++ clock->node->pds.grandmasterClockQuality.clockClass); ++ } ++ ++ curlen += snprintf(response + curlen, resplen - curlen, ++ "\n\nSource forced? %s\n", priv->forced_source_clock ? "yes" : "no"); ++ ++ return curlen; ++} ++ ++static int ha_com_socket_handle_msg(struct phc2sys_private *priv) ++{ ++ struct pollfd pollfd[HA_SCK_N_FD]; ++ struct address sender; ++ int cnt, res = 0; ++ int timeout = 0; ++ void * buffer = NULL; ++ void * response = NULL; ++ ++ while(1) { ++ pollfd[0].fd = priv->ha_socket_fd; ++ pollfd[0].events = POLLIN|POLLPRI; ++ ++ cnt = poll(pollfd, HA_SCK_N_FD, timeout); ++ if (cnt < 0) { ++ pr_err("ha_com_socket: poll failed: %m"); ++ res = -1; ++ break; ++ } ++ if (!cnt) { ++ /* timeout and fd wasn't ready */ ++ break; ++ } ++ ++ if (!(pollfd[0].revents & (POLLIN|POLLPRI))) ++ break; ++ ++ buffer = malloc(HA_SCK_BUFFER_SIZE); ++ if (!buffer) { ++ pr_err("ha_com_socket: failed to allocate memory for message"); ++ res = -1; ++ break; ++ } ++ ++ res = ha_com_socket_recv(pollfd[0].fd, buffer, HA_SCK_BUFFER_SIZE, &sender); ++ if (res < 0) ++ break; ++ ++ fprintf(stderr, "ha_com_socket: received: %s\n", (char*)buffer); ++ fprintf(stderr, "ha_com_socket: recvd from: %s\n", ((struct sockaddr_un*)&sender.sa)->sun_path); ++ ++ response = malloc(HA_SCK_BUFFER_SIZE); ++ if (!response) { ++ pr_err("ha_com_socket: failed to allocate memory for response message"); ++ res = -1; ++ break; ++ } ++ ++ /* handle messages and create responses */ ++ if (strcmp((const char*)buffer, "status") == 0) { ++ cnt = ha_handle_status_msg(priv, response, HA_SCK_BUFFER_SIZE); ++ } else if (strcmp((const char*)buffer, "clock source") == 0) { ++ if (priv->master) { ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", ++ priv->master->device); ++ } else { ++ cnt = snprintf((char*)buffer, HA_SCK_BUFFER_SIZE, "None"); ++ } ++ } else if (strcmp((const char*)buffer, "forced lock") == 0) { ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", ++ priv->forced_source_clock ? "True" : "False"); ++ } else { ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "error: invalid command"); ++ } ++ ++ fprintf(stderr, "ha_com_socket: response: \n%s", (char*)response); ++ ++ res = ha_com_socket_send(pollfd[0].fd, &sender, response, cnt); ++ } ++ ++ free(buffer); ++ free(response); ++ return res; ++} ++ + static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscriptions) + { + struct timespec interval; +@@ -1223,6 +1410,8 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (ha_enabled) { ++ ha_com_socket_handle_msg(priv); ++ + if (priv->forced_source_clock) { + /* HA automatic clock selection is disabled */ + if (priv->clock_state_changed) { +@@ -1312,6 +1501,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + update_clock(priv, clock, offset, ts, delay); + } + } ++ + return 0; + } + +@@ -1597,6 +1787,7 @@ int main(int argc, char *argv[]) + .better = NULL, + .stability_timer.tv_sec = 0, + .forced_source_clock = 0, ++ .ha_socket_fd = -1, + }; + struct pmc_agent *node = NULL; + unsigned int i, src_cnt = 0; +@@ -1861,7 +2052,7 @@ int main(int argc, char *argv[]) + ha_enabled = config_get_int(cfg, NULL, "ha_enabled"); + if (!ha_enabled && src_cnt > 1) { + fprintf(stderr, "too many source clocks\n"); +- fprintf(stderr, "Use 'ha_enabled 1' to accept more than one source clocks\n"); ++ fprintf(stderr, "Use 'ha_enabled 1' to accept more than one source clock\n"); + goto bad_usage; + } + +@@ -1877,6 +2068,9 @@ int main(int argc, char *argv[]) + if (priv.master == NULL) { + priv.master = src; + } ++ if (ha_enabled) { ++ src->ha_priority = config_get_int(cfg, src->device, "ha_priority"); ++ } + } + + dst = clock_add(&priv, dst_name ? dst_name : "CLOCK_REALTIME"); +@@ -1966,6 +2160,10 @@ int main(int argc, char *argv[]) + } + } + ++ if (ha_enabled) { ++ ha_com_socket_open(&priv.ha_socket_fd, cfg); ++ } ++ + if (pps_fd >= 0) { + /* only one destination clock allowed with PPS until we + * implement a mean to specify PTP port to PPS mapping */ +@@ -1976,6 +2174,7 @@ int main(int argc, char *argv[]) + } + + end: ++ ha_com_socket_close(priv.ha_socket_fd); + pmc_agent_cleanup(&priv); + clock_cleanup(&priv); + port_cleanup(&priv); +-- +2.25.1 + +From e77783a9873baeeda277cfa59059021ce121a693 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Fri, 4 Aug 2023 15:44:12 -0300 +Subject: [PATCH 42/47] Commands 'enable lock' and 'disable lock. + +The 'enable lock' command is used to lock to a single clock +source and disable the HA clock selection algorithm. The +interface of the clock source must be specified in the +command. For example: + +'enable lock ' + +It returns "Success" or an error message. The error message +"Error: Usage 'enable lock '" is returned when +no interface was provided in the command. The error message +"Error: Interface not found!" is returned when the interface +provided is not found in the phc2sys configuration. + +The command 'disable lock' is used to unlock the clock source +and re-enable the HA clock selection algorithm. It returns +"Success" even when the clock source was not locked. + +Test plan: enable lock and disable lock commands +PASS: Verify the enable lock changes the clock source to the given +interface. +PASS: Verify that regardless the interface state, the clock source +remains locked. +PASS: Verify that disable lock command makes the better available +clock to be selected again. + +[commit 704d9ed2e22b89308c7f0149d7fde86d456bc4e3 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 110 +++++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 93 insertions(+), 17 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 0bc3709..f89dc23 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -259,13 +259,21 @@ static void clock_cleanup(struct phc2sys_private *priv) + } + } + +-static struct clock *clock_get(struct phc2sys_private *priv, struct pmc_agent *node) ++static struct clock * clock_get_by_device(struct phc2sys_private *priv, ++ const char * device) + { + struct clock * clock = NULL; + LIST_FOREACH(clock, &priv->clocks, list) { +- if (clock->node == node) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ /* sanity check */ ++ if (!clock->device) ++ continue; ++ ++ if (strcmp(device, clock->device) == 0) + break; +- } + } + return clock; + } +@@ -508,18 +516,6 @@ static struct port *port_get(struct phc2sys_private *priv, unsigned int number) + return NULL; + } + +-static struct port *port_get_by_clock(struct phc2sys_private *priv, struct clock * clock) +-{ +- struct port *p, *port = NULL; +- LIST_FOREACH(p, &priv->ports, list) { +- if (p->clock == clock) { +- port = p; +- break; +- } +- } +- return port; +-} +- + static struct port *port_add(struct phc2sys_private *priv, unsigned int number, + char *device) + { +@@ -1287,7 +1283,82 @@ static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, + return curlen; + } + +-static int ha_com_socket_handle_msg(struct phc2sys_private *priv) ++static bool startsWith(const char *prefix, const char *str) ++{ ++ return 0 == strncmp(prefix, str, strlen(prefix) - 1); ++} ++ ++static char * strAtColumn(char *msg, size_t column) ++{ ++ int i; ++ char * str = NULL; ++ ++ /* split and walk over the columns */ ++ strtok(msg, " "); ++ for (i = 1; i < column; i++) { ++ str = strtok(NULL, " "); ++ } ++ ++ return str; ++} ++ ++static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, ++ char *response, size_t resplen) ++{ ++ size_t curlen = 0; ++ char *interface = NULL; ++ struct clock *clock = NULL; ++ ++ interface = strAtColumn(msg, 3); ++ if (strlen(interface) == 0) { ++ return snprintf(response, resplen, "Error: Usage 'enable lock '"); ++ } ++ ++ clock = clock_get_by_device(priv, interface); ++ if (!clock) { ++ return snprintf(response, resplen, "Error: Interface not found!"); ++ } ++ ++ pr_info("HA automatic source selection is disabled by command"); ++ pr_info("Only interface %s will be used as source clock", clock->device); ++ ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++ ++ priv->forced_source_clock = 1; ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_handle_disable_lock_msg(struct phc2sys_private *priv, ++ struct config *cfg, char *response, size_t resplen) ++{ ++ size_t curlen = 0; ++ struct clock *clock = NULL; ++ ++ if (priv->forced_source_clock) { ++ pr_info("HA automatic source selection is enabled by command"); ++ /* re-enable HA source selection algorithm */ ++ priv->forced_source_clock = 0; ++ /* select the best clock available */ ++ clock = ha_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ priv->master = clock; ++ pr_notice("new source clock selected %s", clock->device); ++ } ++ } ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_com_socket_handle_msg(struct phc2sys_private *priv, ++ struct config *cfg) + { + struct pollfd pollfd[HA_SCK_N_FD]; + struct address sender; +@@ -1348,6 +1419,11 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv) + } else if (strcmp((const char*)buffer, "forced lock") == 0) { + cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", + priv->forced_source_clock ? "True" : "False"); ++ } else if (startsWith("enable lock", buffer)) { ++ cnt = ha_handle_enable_lock_msg(priv, buffer, response, ++ HA_SCK_BUFFER_SIZE); ++ } else if (strcmp((const char*)buffer, "disable lock") == 0) { ++ cnt = ha_handle_disable_lock_msg(priv, cfg, response, HA_SCK_BUFFER_SIZE); + } else { + cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "error: invalid command"); + } +@@ -1410,7 +1486,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + } + + if (ha_enabled) { +- ha_com_socket_handle_msg(priv); ++ ha_com_socket_handle_msg(priv, cfg); + + if (priv->forced_source_clock) { + /* HA automatic clock selection is disabled */ +-- +2.25.1 + +From 27b5c6afff470053b30ade14537be43f1c1c376d Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Fri, 4 Aug 2023 19:01:57 -0300 +Subject: [PATCH 43/47] Commands 'enable source' and 'disable source'. + +These commands controls the list of clocks available to clock +selection algorithm. + +At startup all sources are enabled and can be selected as clock +source. The 'disable source' command removes a given interface +from the available list and it can't be selected any more. The +'enable source' command re-enables the interface. + +The last interface can't be disable. The disable command fails and +returns an error indicating the given interface is the last one. + +If the active clock source interface is disabled than a new one +will be selected. + +Every time the enable command is executed the clock selection +algorithm is executed and the best available clock is selected. + +The enable and disable source commands won't affect the active +clock if one interface is forced lock as active. + +The disabled interface is market with 'x' sign in the status +command. + +Test plan: enable source and disable source commands +PASS: Verify a new interface is selected when the active one +is disabled. +PASS: Verify the primary interface is re-selected active after +it is enabled back. +PASS: Verify the disable source command fails when attempt to +disable the last enabled interface. +PASS: Verify the active interface don't change while one of them +are forced lock as active. +PASS: Verify the active interface dont't change after enabling +an interface while in forced lock mode. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit 55ac3f4131aaa999b1b7b9eec50b7cb7cebbf0d4 upstream] +[commit c77de0acd3641833d2705e3929be2152bd5fb519 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++------- + 1 file changed, 127 insertions(+), 19 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index f89dc23..035ee21 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -98,6 +98,7 @@ struct clock { + struct clockcheck *sanity_check; + struct pmc_agent *node; + int ha_priority; ++ int enabled; + }; + typedef LIST_HEAD(head, clock) clock_list_head_t; + +@@ -228,6 +229,8 @@ static struct clock *clock_add(struct phc2sys_private *priv, char *device) + c->sysoff_method = sysoff_probe(CLOCKID_TO_FD(clkid), + priv->phc_readings); + ++ c->enabled = 1; ++ + LIST_INSERT_HEAD(&priv->clocks, c, list); + return c; + } +@@ -278,6 +281,28 @@ static struct clock * clock_get_by_device(struct phc2sys_private *priv, + return clock; + } + ++static size_t clock_count_enabled_sources(struct phc2sys_private *priv, ++ struct clock *ignore) ++{ ++ size_t count = 0; ++ struct clock * clock = NULL; ++ ++ LIST_FOREACH(clock, &priv->clocks, list) { ++ /* ignore the dst clock */ ++ if (clock->state == PS_MASTER) ++ continue; ++ ++ if (clock == ignore) ++ continue; ++ ++ if (!clock->enabled) ++ continue; ++ ++ count++; ++ } ++ return count; ++} ++ + static bool clock_match_ha_dds_requirements(struct clock *clock, struct config *cfg) + { + /* get requirements */ +@@ -404,6 +429,11 @@ static int clock_available_ha_src_clocks(struct phc2sys_private *priv, struct co + continue; + } + ++ if (!clock->enabled) { ++ pr_debug("clock %s is disabled", clock->device); ++ continue; ++ } ++ + /* get Default Data Set */ + if (!clock->node->dds_valid) { + retries = 0; +@@ -1267,7 +1297,8 @@ static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, + curlen += snprintf(response + curlen, resplen - curlen, + " %c %9s %8d %10d 0x%2x 0x%4x %s %s %d\n", + (priv->master == clock) ? '*' : +- (priv->better == clock) ? '-' : ' ', ++ (priv->better == clock) ? '-' : ++ (!clock->enabled) ? 'x' : ' ', + clock->device, clock->ha_priority, + clock->node->dds.clockQuality.clockClass, + clock->node->dds.clockQuality.clockAccuracy, +@@ -1302,6 +1333,16 @@ static char * strAtColumn(char *msg, size_t column) + return str; + } + ++static void ha_set_clock_source(struct phc2sys_private *priv, struct clock *clock) ++{ ++ pr_notice("new clock source selected %s", clock->device); ++ ++ priv->master = clock; ++ priv->better = NULL; ++ priv->stability_timer.tv_sec = 0; ++ priv->stability_timer.tv_nsec = 0; ++} ++ + static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, + char *response, size_t resplen) + { +@@ -1316,16 +1357,13 @@ static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, + + clock = clock_get_by_device(priv, interface); + if (!clock) { +- return snprintf(response, resplen, "Error: Interface not found!"); ++ return snprintf(response, resplen, "Error: Interface not found"); + } + + pr_info("HA automatic source selection is disabled by command"); + pr_info("Only interface %s will be used as source clock", clock->device); + +- priv->master = clock; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ ha_set_clock_source(priv, clock); + + priv->forced_source_clock = 1; + +@@ -1347,8 +1385,77 @@ static int ha_handle_disable_lock_msg(struct phc2sys_private *priv, + /* select the best clock available */ + clock = ha_select_clock(priv, cfg); + if (clock && clock != priv->master) { +- priv->master = clock; +- pr_notice("new source clock selected %s", clock->device); ++ ha_set_clock_source(priv, clock); ++ } ++ } ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_handle_enable_source_msg(struct phc2sys_private *priv, ++ struct config *cfg, char *msg, char *response, size_t resplen) ++{ ++ size_t curlen; ++ char *interface = NULL; ++ struct clock *clock = NULL; ++ ++ interface = strAtColumn(msg, 3); ++ if (strlen(interface) == 0) { ++ return snprintf(response, resplen, "Error: Usage 'enable source '"); ++ } ++ ++ clock = clock_get_by_device(priv, interface); ++ if (!clock) { ++ return snprintf(response, resplen, "Error: Interface not found"); ++ } ++ ++ clock->enabled = 1; ++ ++ if (!priv->forced_source_clock) { ++ /* select the best clock available */ ++ clock = ha_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ ha_set_clock_source(priv, clock); ++ } ++ } ++ ++ curlen = snprintf(response, resplen, "Success"); ++ ++ return curlen; ++} ++ ++static int ha_handle_disable_source_msg(struct phc2sys_private *priv, ++ struct config *cfg, char *msg, char *response, size_t resplen) ++{ ++ size_t curlen; ++ char *interface = NULL; ++ struct clock *clock = NULL; ++ ++ interface = strAtColumn(msg, 3); ++ if (strlen(interface) == 0) { ++ return snprintf(response, resplen, "Error: Usage 'disable source '"); ++ } ++ ++ clock = clock_get_by_device(priv, interface); ++ if (!clock) { ++ return snprintf(response, resplen, "Error: Interface not found"); ++ } ++ ++ /* check if is the last clock enabled */ ++ if (clock_count_enabled_sources(priv, clock) == 0) { ++ return snprintf(response, resplen, "Error: Last interface enabled"); ++ } ++ ++ clock->enabled = 0; ++ ++ /* disabling clock source */ ++ if (clock == priv->master && !priv->forced_source_clock) { ++ /* select the best clock available */ ++ clock = ha_select_clock(priv, cfg); ++ if (clock && clock != priv->master) { ++ ha_set_clock_source(priv, clock); + } + } + +@@ -1423,9 +1530,17 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + cnt = ha_handle_enable_lock_msg(priv, buffer, response, + HA_SCK_BUFFER_SIZE); + } else if (strcmp((const char*)buffer, "disable lock") == 0) { +- cnt = ha_handle_disable_lock_msg(priv, cfg, response, HA_SCK_BUFFER_SIZE); ++ cnt = ha_handle_disable_lock_msg(priv, cfg, response, ++ HA_SCK_BUFFER_SIZE); ++ } else if (startsWith("enable source", buffer)) { ++ cnt = ha_handle_enable_source_msg(priv, cfg, buffer, response, ++ HA_SCK_BUFFER_SIZE); ++ } else if (startsWith("disable source", buffer)) { ++ cnt = ha_handle_disable_source_msg(priv, cfg, buffer, response, ++ HA_SCK_BUFFER_SIZE); + } else { +- cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "error: invalid command"); ++ cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, ++ "Error: Invalid command"); + } + + fprintf(stderr, "ha_com_socket: response: \n%s", (char*)response); +@@ -1498,10 +1613,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + if (priv->clock_state_changed) { + clock = check_and_select_clock(priv, cfg); + if (clock && clock != priv->master) { +- priv->master = clock; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ ha_set_clock_source(priv, clock); + } + + priv->clock_state_changed = 0; +@@ -1514,11 +1626,7 @@ static int do_loop(struct phc2sys_private *priv, struct config *cfg, int subscri + if ((now.tv_sec > priv->stability_timer.tv_sec) || + (now.tv_sec == priv->stability_timer.tv_sec && + now.tv_nsec > priv->stability_timer.tv_nsec)) { +- pr_notice("new source clock selected %s", priv->better->device); +- priv->master = priv->better; +- priv->better = NULL; +- priv->stability_timer.tv_sec = 0; +- priv->stability_timer.tv_nsec = 0; ++ ha_set_clock_source(priv, priv->better); + } + } + } +-- +2.25.1 + +From 2d40cc7cf52bbf054856c34902e4bda9f13ebb79 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 7 Aug 2023 14:55:12 -0300 +Subject: [PATCH 44/47] Stream type phc2sys com socket + +The type of the socket was changed from datagram to stream. + +Test plan: status/show commands +PASS: Verify status command response +PASS: Verify forced lock command response +PASS: Verify clock source command response + +Test plan: enable lock and disable lock commands +PASS: Verify the enable lock changes the clock source to the given +interface. +PASS: Verify that disable lock command makes the better available +clock to be selected again. + +Test plan: disable source and enable source commands +PASS: Verify a new interface is selected when the active one +is disabled. +PASS: Verify the primary interface is re-selected active after +it is enabled back. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit b4f79cb626d6e40cf1d5aa2c5d5fba89e2c2e340 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 76 +++++++++++++++++++++++++++---------------------------- + 1 file changed, 38 insertions(+), 38 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 035ee21..a597014 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1203,14 +1203,12 @@ static int ha_com_socket_close(int fd) + struct sockaddr_un sa; + socklen_t len = sizeof(sa); + +- // if (fd < 0) +- // return -1; +- + if (!getsockname(fd, (struct sockaddr *) &sa, &len) && + sa.sun_family == AF_LOCAL) { + unlink(sa.sun_path); + } + ++ shutdown(fd, SHUT_RDWR); + close(fd); + return 0; + } +@@ -1219,9 +1217,10 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + { + int fd, err; + struct sockaddr_un sa; ++ const int backlog = 50; + const char *name = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); + +- fd = socket(AF_LOCAL, SOCK_DGRAM, 0); ++ fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd < 0) { + pr_err("ha_com_socket: failed to create socket: %m"); + return -1; +@@ -1238,22 +1237,27 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + return -1; + } + ++ err = listen(fd, backlog); ++ if (err < 0) { ++ pr_err("ha_com_socket: listen failed: %m"); ++ close(fd); ++ return -1; ++ } ++ + *fd_out = fd; + chmod(name, HA_SCK_FILEMODE); + + return 0; + } + +-static int ha_com_socket_recv(int fd, void *buf, size_t buflen, +- struct address *addr) ++static int ha_com_socket_recv(int fd, void *buf, size_t buflen) + { + int cnt; + +- addr->len = sizeof(addr->sun); +- cnt = recvfrom(fd, buf, buflen, 0, &addr->sa, &addr->len); ++ cnt = read(fd, buf, buflen); + if (cnt <= 0) { +- pr_err("ha_com_socket: recvfrom failed: %m"); +- return cnt; ++ pr_err("ha_com_socket: read failed: %m"); ++ return -errno; + } + + ((char*)buf)[cnt] = '\0'; +@@ -1261,13 +1265,13 @@ static int ha_com_socket_recv(int fd, void *buf, size_t buflen, + return 0; + } + +-static int ha_com_socket_send(int fd, struct address *addr, void *buf, +- size_t buflen) ++static int ha_com_socket_send(int fd, void *buf, size_t buflen) + { + int cnt; + +- cnt = sendto(fd, buf, buflen, 0, &addr->sa, addr->len); +- if (cnt < 1) { ++ cnt = send(fd, buf, buflen, 0); ++ if (cnt < 0) { ++ pr_err("ha_com_socket: send failed: %m"); + return -errno; + } + return cnt; +@@ -1467,48 +1471,42 @@ static int ha_handle_disable_source_msg(struct phc2sys_private *priv, + static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + struct config *cfg) + { +- struct pollfd pollfd[HA_SCK_N_FD]; +- struct address sender; +- int cnt, res = 0; +- int timeout = 0; ++ int cnt, res = 0, fd; + void * buffer = NULL; + void * response = NULL; + + while(1) { +- pollfd[0].fd = priv->ha_socket_fd; +- pollfd[0].events = POLLIN|POLLPRI; +- +- cnt = poll(pollfd, HA_SCK_N_FD, timeout); +- if (cnt < 0) { +- pr_err("ha_com_socket: poll failed: %m"); +- res = -1; +- break; +- } +- if (!cnt) { +- /* timeout and fd wasn't ready */ ++ fd = accept(priv->ha_socket_fd, NULL, NULL); ++ if (fd < 0) { ++ if (errno == EAGAIN || errno == EWOULDBLOCK) { ++ /* no msg available */ ++ } else { ++ pr_err("ha_com_socket: accept failed: %m"); ++ res = -1; ++ } + break; + } + +- if (!(pollfd[0].revents & (POLLIN|POLLPRI))) +- break; +- + buffer = malloc(HA_SCK_BUFFER_SIZE); + if (!buffer) { + pr_err("ha_com_socket: failed to allocate memory for message"); ++ close(fd); + res = -1; + break; + } + +- res = ha_com_socket_recv(pollfd[0].fd, buffer, HA_SCK_BUFFER_SIZE, &sender); +- if (res < 0) ++ res = ha_com_socket_recv(fd, buffer, HA_SCK_BUFFER_SIZE); ++ if (res < 0) { ++ close(fd); + break; ++ } + +- fprintf(stderr, "ha_com_socket: received: %s\n", (char*)buffer); +- fprintf(stderr, "ha_com_socket: recvd from: %s\n", ((struct sockaddr_un*)&sender.sa)->sun_path); ++ pr_debug("ha_com_socket: command received: %s", (char*)buffer); + + response = malloc(HA_SCK_BUFFER_SIZE); + if (!response) { + pr_err("ha_com_socket: failed to allocate memory for response message"); ++ close(fd); + res = -1; + break; + } +@@ -1543,9 +1541,11 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + "Error: Invalid command"); + } + +- fprintf(stderr, "ha_com_socket: response: \n%s", (char*)response); ++ pr_debug("ha_com_socket: response: %s", (char*)response); + +- res = ha_com_socket_send(pollfd[0].fd, &sender, response, cnt); ++ res = ha_com_socket_send(fd, response, cnt); ++ ++ close(fd); + } + + free(buffer); +-- +2.25.1 + +From 2896553d6dfa975102cba4cc45105b000ec0ae52 Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Tue, 8 Aug 2023 13:10:50 -0300 +Subject: [PATCH 45/47] Functions starts_with and str_at_column + +Renaming starts_with and str_at_column functions to match ptp4l code +style. + +Test plan: commands +PASS: Verify 'enable lock ', 'disabel source ' and +'enable source ' still work. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit f43f86eab5f8f5d2c9895d290d4bdfd6f60853f8 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index a597014..6965162 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1318,12 +1318,12 @@ static int ha_handle_status_msg(struct phc2sys_private *priv, char *response, + return curlen; + } + +-static bool startsWith(const char *prefix, const char *str) ++static bool starts_with(const char *prefix, const char *str) + { + return 0 == strncmp(prefix, str, strlen(prefix) - 1); + } + +-static char * strAtColumn(char *msg, size_t column) ++static char * str_at_column(char *msg, size_t column) + { + int i; + char * str = NULL; +@@ -1354,7 +1354,7 @@ static int ha_handle_enable_lock_msg(struct phc2sys_private *priv, char *msg, + char *interface = NULL; + struct clock *clock = NULL; + +- interface = strAtColumn(msg, 3); ++ interface = str_at_column(msg, 3); + if (strlen(interface) == 0) { + return snprintf(response, resplen, "Error: Usage 'enable lock '"); + } +@@ -1405,7 +1405,7 @@ static int ha_handle_enable_source_msg(struct phc2sys_private *priv, + char *interface = NULL; + struct clock *clock = NULL; + +- interface = strAtColumn(msg, 3); ++ interface = str_at_column(msg, 3); + if (strlen(interface) == 0) { + return snprintf(response, resplen, "Error: Usage 'enable source '"); + } +@@ -1437,7 +1437,7 @@ static int ha_handle_disable_source_msg(struct phc2sys_private *priv, + char *interface = NULL; + struct clock *clock = NULL; + +- interface = strAtColumn(msg, 3); ++ interface = str_at_column(msg, 3); + if (strlen(interface) == 0) { + return snprintf(response, resplen, "Error: Usage 'disable source '"); + } +@@ -1524,16 +1524,16 @@ static int ha_com_socket_handle_msg(struct phc2sys_private *priv, + } else if (strcmp((const char*)buffer, "forced lock") == 0) { + cnt = snprintf((char*)response, HA_SCK_BUFFER_SIZE, "%s", + priv->forced_source_clock ? "True" : "False"); +- } else if (startsWith("enable lock", buffer)) { ++ } else if (starts_with("enable lock", buffer)) { + cnt = ha_handle_enable_lock_msg(priv, buffer, response, + HA_SCK_BUFFER_SIZE); + } else if (strcmp((const char*)buffer, "disable lock") == 0) { + cnt = ha_handle_disable_lock_msg(priv, cfg, response, + HA_SCK_BUFFER_SIZE); +- } else if (startsWith("enable source", buffer)) { ++ } else if (starts_with("enable source", buffer)) { + cnt = ha_handle_enable_source_msg(priv, cfg, buffer, response, + HA_SCK_BUFFER_SIZE); +- } else if (startsWith("disable source", buffer)) { ++ } else if (starts_with("disable source", buffer)) { + cnt = ha_handle_disable_source_msg(priv, cfg, buffer, response, + HA_SCK_BUFFER_SIZE); + } else { +-- +2.25.1 + +From f480fb54182da36baeb35bac90154abafcaf854a Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Tue, 8 Aug 2023 14:06:55 -0300 +Subject: [PATCH 46/47] Robustness improvements to phc2sys socket + +When phc2sys abnormally exits the socket file might remain created. +To avoid error when phc2sys is relaunched, the exixting file is +deleted before recriating the socket. + +If the peer application closes the socket before sending the +response completely, it will cause a broken pipe error. The +send function generates a SIGPIPE on broken pipe errors, +killing the phc2sys process unless MSG_NOSIGNAL flag is set. + +Test plan: socket file +PASS: Verify that phc2sys can restart normally after killing it. + +Test plan: SIGPIPE +PASS: Verify the phc2sys application don't exit when client socket +is closed before the respose is sent. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit 8b3765b3f104a90a487fbcb0f61074c7677c215e upstream] +[commit 50ad1c6f81a706b8be6689bea2ba2db215cf3dc3 upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index 6965162..edc626f 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -1218,7 +1218,9 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + int fd, err; + struct sockaddr_un sa; + const int backlog = 50; +- const char *name = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); ++ const char *path = config_get_string(cfg, NULL, "ha_phc2sys_com_socket"); ++ ++ unlink(path); + + fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd < 0) { +@@ -1228,7 +1230,7 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + + memset(&sa, 0, sizeof(sa)); + sa.sun_family = AF_LOCAL; +- strncpy(sa.sun_path, name, sizeof(sa.sun_path) - 1); ++ strncpy(sa.sun_path, path, sizeof(sa.sun_path) - 1); + + err = bind(fd, (struct sockaddr *) &sa, sizeof(sa)); + if (err < 0) { +@@ -1245,7 +1247,7 @@ static int ha_com_socket_open(int *fd_out, struct config *cfg) + } + + *fd_out = fd; +- chmod(name, HA_SCK_FILEMODE); ++ chmod(path, HA_SCK_FILEMODE); + + return 0; + } +@@ -1269,7 +1271,7 @@ static int ha_com_socket_send(int fd, void *buf, size_t buflen) + { + int cnt; + +- cnt = send(fd, buf, buflen, 0); ++ cnt = send(fd, buf, buflen, MSG_NOSIGNAL); + if (cnt < 0) { + pr_err("ha_com_socket: send failed: %m"); + return -errno; +-- +2.25.1 + +From c5e1599748877f16bfd1dea6910f6b8b57be7ddd Mon Sep 17 00:00:00 2001 +From: Andre Mauricio Zelak +Date: Mon, 7 Aug 2023 18:19:37 -0300 +Subject: [PATCH 47/47] phc2sys without -w option. + +Fix bad clock and pmc initialization when -w command argument +is not provided. + +The pmc agent must be created and mapped to a clock source even +in cases the -w (wait for ptp4l) option is not used. + +Test plan: +PASS: Verify phc2sys initializes without -w argument. +PASS: Verify phc2sys initializes with two ptp4l interfaces +configured. + +Reviewed-by: Cole Walker +Reviewed-by: Andre Fernando Zanella Kantek + + +[commit 10fa27f5829787c15e9ae59c45703328ca4e644f upstream] + +Signed-off-by: Andre Mauricio Zelak +--- + phc2sys.c | 34 ++++++++++++++-------------------- + 1 file changed, 14 insertions(+), 20 deletions(-) + +diff --git a/phc2sys.c b/phc2sys.c +index edc626f..065b7f0 100644 +--- a/phc2sys.c ++++ b/phc2sys.c +@@ -2254,6 +2254,15 @@ int main(int argc, char *argv[]) + if (priv.master == NULL) { + priv.master = src; + } ++ if (i > 0) { ++ node = pmc_agent_add(&priv, i); ++ if (!node) ++ goto end; ++ } ++ /* map clock to pmc agent node */ ++ src->node = node; ++ pr_debug("pmc node index %d assigned to source interface %s", ++ node->index, src->device); + if (ha_enabled) { + src->ha_priority = config_get_int(cfg, src->device, "ha_priority"); + } +@@ -2286,35 +2295,22 @@ int main(int argc, char *argv[]) + r = -1; + + if (wait_sync) { +- i = 0; + LIST_FOREACH(src, &priv.clocks, list) { + + /* skip dst clock */ +- if (src == dst) { ++ if (src->state == PS_MASTER) + continue; +- } +- +- if (i > 0) { +- node = pmc_agent_add(&priv, i); +- if (!node) +- goto end; +- } + + /* uds local is formated '/var/run/phc2sys..' */ + snprintf(uds_local, sizeof(uds_local), "/var/run/phc2sys.%d.%s", + getpid(), src->device); + +- if (init_pmc_node(cfg, node, uds_local, ++ if (init_pmc_node(cfg, src->node, uds_local, + phc2sys_recv_subscribed, &priv)) + goto end; + +- /* map clock to pmc agent node */ +- src->node = node; +- pr_debug("pmc node index %d source clock %s initialized", +- node->index, src->device); +- + while (is_running()) { +- r = run_pmc_wait_sync(node, 1000); ++ r = run_pmc_wait_sync(src->node, 1000); + if (r < 0) + goto end; + if (r > 0) +@@ -2324,7 +2320,7 @@ int main(int argc, char *argv[]) + } + + if (!priv.forced_sync_offset) { +- r = pmc_agent_query_utc_offset(node, 1000); ++ r = pmc_agent_query_utc_offset(src->node, 1000); + if (r) { + pr_err("failed to get UTC offset"); + goto end; +@@ -2334,10 +2330,8 @@ int main(int argc, char *argv[]) + if (priv.forced_sync_offset || + (src->clkid != CLOCK_REALTIME && dst->clkid != CLOCK_REALTIME) || + src->clkid == CLOCK_INVALID) { +- pmc_agent_disable(node); ++ pmc_agent_disable(src->node); + } +- +- ++i; + } + + if (ha_enabled && !priv.forced_source_clock) { +-- +2.25.1 + diff --git a/SPECS/linuxptp/linuxptp-zerolength.patch b/SPECS/linuxptp/linuxptp-zerolength.patch new file mode 100644 index 0000000000..0ab5ed4602 --- /dev/null +++ b/SPECS/linuxptp/linuxptp-zerolength.patch @@ -0,0 +1,37 @@ +commit 9633ab52460f58c92c6daa35e9d24e4ce9c5ab1c +Author: Miroslav Lichvar +Date: Tue Feb 23 11:01:43 2021 +0100 + + sk: Don't return error for zero-length messages. + + The recvmsg() call can return zero for a zero-length UDP message, which + should be handled as a bad message and not a fault of the port. This was + addressed in commit 6b61ba29c78e ("Avoid fault when receiving zero + length packets"), but later regressed in commit a6e0b83bd503 + ("sk: Convey transmit path errors to the caller."). + + Signed-off-by: Miroslav Lichvar + Fixes: a6e0b83bd503 ("sk: Convey transmit path errors to the caller.") + +diff --git a/sk.c b/sk.c +index c9ef4d2..8be0708 100644 +--- a/sk.c ++++ b/sk.c +@@ -391,7 +391,7 @@ int sk_receive(int fd, void *buf, int buflen, + + if (!ts) { + memset(&hwts->ts, 0, sizeof(hwts->ts)); +- return cnt < 1 ? -errno : cnt; ++ return cnt < 0 ? -errno : cnt; + } + + switch (hwts->type) { +@@ -407,7 +407,7 @@ int sk_receive(int fd, void *buf, int buflen, + hwts->ts = timespec_to_tmv(ts[1]); + break; + } +- return cnt < 1 ? -errno : cnt; ++ return cnt < 0 ? -errno : cnt; + } + + int sk_set_priority(int fd, int family, uint8_t dscp) diff --git a/SPECS/linuxptp/linuxptp.signatures.json b/SPECS/linuxptp/linuxptp.signatures.json new file mode 100644 index 0000000000..80f77173f1 --- /dev/null +++ b/SPECS/linuxptp/linuxptp.signatures.json @@ -0,0 +1,12 @@ +{ + "Signatures": { + "clknetsim-9ed48d.tar.gz": "4e464ddb20c6436e8dfbc82305ae5187831a4b6b60b89dcdc92f59a0d3539496", + "linuxptp-3.1.1.tgz": "94d6855f9b7f2d8e9b0ca6d384e3fae6226ce6fc012dbad02608bdef3be1c0d9", + "linuxptp-testsuite-ff37e2.tar.gz": "038b1bb07ce5f03b3207bbfabe6f85d2cbbb16ef54b121a35749ca7e01d54648", + "phc2sys.service": "4bab3fe8ba6b801d6092d820c4fa4514973f441af5967f4c597ecd60d863c752", + "ptp4l.conf": "c3ae7d8d845619f41f3743da9e46ea6f690d218383f127e0bf2b64de2e69c283", + "ptp4l.service": "2d85b55077bb99091ddf66917b86044f75fe31584b647a7df06994eee5ecf6bd", + "timemaster.conf": "068104a097f468aaeb12bcf67eab2705736665a5546cda29be62be3c4593ddd5", + "timemaster.service": "01af35467d2400f12e7c95df94e069bba89ad06c048b6f346fc26676db2e6b42" + } +} \ No newline at end of file diff --git a/SPECS-EXTENDED/linuxptp/linuxptp.spec b/SPECS/linuxptp/linuxptp.spec similarity index 73% rename from SPECS-EXTENDED/linuxptp/linuxptp.spec rename to SPECS/linuxptp/linuxptp.spec index 50cf173b16..98d4013219 100644 --- a/SPECS-EXTENDED/linuxptp/linuxptp.spec +++ b/SPECS/linuxptp/linuxptp.spec @@ -1,32 +1,38 @@ Vendor: Microsoft Corporation Distribution: Mariner -%global gitfullver e0580929f451e685d92cd10d80b76f39e9b09a97 -%global gitver %(c=%{gitfullver}; echo ${c:0:6}) %global _hardened_build 1 -%global testsuite_ver a7f6e1 -%global clknetsim_ver 79ffe4 +%global testsuite_ver ff37e2 +%global clknetsim_ver 9ed48d Name: linuxptp -Version: 2.0 -Release: 8%{?dist} +Version: 3.1.1 +Release: 1%{?dist} Summary: PTP implementation for Linux License: GPLv2+ URL: http://linuxptp.sourceforge.net/ -#Source0: https://downloads.sourceforge.net/%{name}/%{name}-%{version}.tgz -Source0: https://github.com/richardcochran/%{name}/archive/%{gitver}/%{name}-%{gitver}.tar.gz +Source0: https://sourceforge.net/projects/%{name}/files/v3.1/%{name}-%{version}.tgz Source1: phc2sys.service Source2: ptp4l.service Source3: timemaster.service Source4: timemaster.conf +Source5: ptp4l.conf # external test suite -Source10: https://github.com/mlichvar/linuxptp-testsuite/archive/%{testsuite_ver}/linuxptp-testsuite-%{testsuite_ver}.tar.gz +Source10: linuxptp-testsuite-%{testsuite_ver}.tar.gz # simulator for test suite -Source11: https://github.com/mlichvar/clknetsim/archive/%{clknetsim_ver}/clknetsim-%{clknetsim_ver}.tar.gz +Source11: clknetsim-%{clknetsim_ver}.tar.gz -BuildRequires: gcc gcc-c++ systemd -BuildRequires: net-snmp-devel +# fix handling of zero-length messages +Patch0: linuxptp-zerolength.patch +# revert phc2sys options needed by the older version of test suite +Patch1: clknetsim-phc2sys.patch + +# The following patch is a combination of multiple patches to enable HA in linuxptp +# https://review.opendev.org/c/starlingx/integ/+/891638 +Patch2: enable-ha.patch + +BuildRequires: gcc gcc-c++ make systemd %{?systemd_requires} @@ -39,11 +45,18 @@ Supporting legacy APIs and other platforms is not a goal. %prep %setup -q -a 10 -a 11 -n %{name}-%{!?gitfullver:%{version}}%{?gitfullver} +%patch0 -p1 -b .zerolength mv linuxptp-testsuite-%{testsuite_ver}* testsuite mv clknetsim-%{clknetsim_ver}* testsuite/clknetsim +pushd testsuite/clknetsim +%patch1 -p1 -R -b .phc2sys +popd + +%patch2 -p1 -b .pre-ha + %build -make %{?_smp_mflags} \ +%{make_build} \ EXTRA_CFLAGS="$RPM_OPT_FLAGS" \ EXTRA_LDFLAGS="$RPM_LD_FLAGS" @@ -51,11 +64,10 @@ make %{?_smp_mflags} \ %makeinstall mkdir -p $RPM_BUILD_ROOT{%{_sysconfdir}/sysconfig,%{_unitdir},%{_mandir}/man5} -install -m 644 -p configs/default.cfg $RPM_BUILD_ROOT%{_sysconfdir}/ptp4l.conf install -m 644 -p %{SOURCE1} %{SOURCE2} %{SOURCE3} $RPM_BUILD_ROOT%{_unitdir} -install -m 644 -p %{SOURCE4} $RPM_BUILD_ROOT%{_sysconfdir} +install -m 644 -p %{SOURCE4} %{SOURCE5} $RPM_BUILD_ROOT%{_sysconfdir} -echo 'OPTIONS="-f /etc/ptp4l.conf -i eth0"' > \ +echo 'OPTIONS="-f /etc/ptp4l.conf"' > \ $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/ptp4l echo 'OPTIONS="-a -r"' > $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/phc2sys @@ -66,7 +78,7 @@ echo '.so man8/timemaster.8' > $RPM_BUILD_ROOT%{_mandir}/man5/timemaster.conf.5 cd testsuite # set random seed to get deterministic results export CLKNETSIM_RANDOM_SEED=26743 -make %{?_smp_mflags} -C clknetsim +%{make_build} -C clknetsim PATH=..:$PATH ./run %post @@ -93,15 +105,53 @@ PATH=..:$PATH ./run %{_sbindir}/phc_ctl %{_sbindir}/pmc %{_sbindir}/ptp4l -%{_sbindir}/snmp4lptp %{_sbindir}/timemaster +%{_sbindir}/ts2phc %{_mandir}/man5/*.5* %{_mandir}/man8/*.8* %changelog -* Thu Oct 14 2021 Pawel Winogrodzki - 2.0-8 -- Initial CBL-Mariner import from Fedora 32 (license: MIT). -- Converting the 'Release' tag to the '[number].[distribution]' format. +* Thu Nov 16 2023 Harshit Gupta - 3.1.1-1 +- Initial CBL-Mariner import from Fedora 37 (license: MIT). +- License Verified. +- Upstream linuxptp 3.1.1-6 has been imported into Azure Linux with package 3.1.1-1 +- Remove SELinux policy + +* Wed Jan 11 2023 Miroslav Lichvar 3.1.1-6 +- update selinux policy (#2159919) + +* Thu Jul 21 2022 Fedora Release Engineering - 3.1.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Tue Apr 26 2022 Miroslav Lichvar 3.1.1-4 +- fix tests on ppc64le (#2046706) + +* Thu Jan 20 2022 Fedora Release Engineering - 3.1.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Thu Jul 22 2021 Miroslav Lichvar 3.1.1-2 +- package selinux policy + +* Wed Jul 07 2021 Miroslav Lichvar 3.1.1-1 +- update to 3.1.1 (CVE-2021-3570, CVE-2021-3571) + +* Tue Mar 02 2021 Zbigniew JÄ™drzejewski-Szmek - 3.1-4 +- Rebuilt for updated systemd-rpm-macros + See https://pagure.io/fesco/issue/2583. + +* Thu Feb 25 2021 Miroslav Lichvar 3.1-3 +- fix handling of zero-length messages +- minimize default configuration +- remove obsolete build requirement + +* Tue Jan 26 2021 Fedora Release Engineering - 3.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Sep 29 2020 Miroslav Lichvar 3.1-1 +- update to 3.1 + +* Mon Jul 27 2020 Miroslav Lichvar 3.0-1 +- update to 3.0 * Mon Feb 03 2020 Miroslav Lichvar 2.0-7.20191225gite05809 - update to 20191225gite05809 diff --git a/SPECS-EXTENDED/linuxptp/phc2sys.service b/SPECS/linuxptp/phc2sys.service similarity index 100% rename from SPECS-EXTENDED/linuxptp/phc2sys.service rename to SPECS/linuxptp/phc2sys.service diff --git a/SPECS/linuxptp/ptp4l.conf b/SPECS/linuxptp/ptp4l.conf new file mode 100644 index 0000000000..4b4b1db475 --- /dev/null +++ b/SPECS/linuxptp/ptp4l.conf @@ -0,0 +1,14 @@ +# For more information about this file, see the ptp4l(8) man page. +# Examples are available in /usr/share/doc/linuxptp/configs. + +[global] +domainNumber 0 +slaveOnly 1 +time_stamping hardware +tx_timestamp_timeout 1 +logging_level 6 +summary_interval 0 + +[eth0] +network_transport UDPv4 +hybrid_e2e 0 diff --git a/SPECS-EXTENDED/linuxptp/ptp4l.service b/SPECS/linuxptp/ptp4l.service similarity index 100% rename from SPECS-EXTENDED/linuxptp/ptp4l.service rename to SPECS/linuxptp/ptp4l.service diff --git a/SPECS-EXTENDED/linuxptp/timemaster.conf b/SPECS/linuxptp/timemaster.conf similarity index 78% rename from SPECS-EXTENDED/linuxptp/timemaster.conf rename to SPECS/linuxptp/timemaster.conf index fd8e77e0a4..14762099e0 100644 --- a/SPECS-EXTENDED/linuxptp/timemaster.conf +++ b/SPECS/linuxptp/timemaster.conf @@ -14,18 +14,11 @@ ntp_program chronyd [chrony.conf] include /etc/chrony.conf -[ntp.conf] -includefile /etc/ntp.conf - [ptp4l.conf] [chronyd] path /usr/sbin/chronyd -[ntpd] -path /usr/sbin/ntpd -options -u ntp:ntp -g - [phc2sys] path /usr/sbin/phc2sys diff --git a/SPECS-EXTENDED/linuxptp/timemaster.service b/SPECS/linuxptp/timemaster.service similarity index 100% rename from SPECS-EXTENDED/linuxptp/timemaster.service rename to SPECS/linuxptp/timemaster.service diff --git a/cgmanifest.json b/cgmanifest.json index 83360827e1..61e6401e2b 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -12021,8 +12021,8 @@ "type": "other", "other": { "name": "linuxptp", - "version": "2.0", - "downloadUrl": "https://github.com/richardcochran/linuxptp/archive/e05809/linuxptp-e05809.tar.gz" + "version": "3.1.1", + "downloadUrl": "https://sourceforge.net/projects/linuxptp/files/v3.1/linuxptp-3.1.1.tgz" } } },