From f72a08b49b22702cd3864ad42fb3df090edcd0a5 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Mon, 15 Sep 2025 12:20:11 +0000 Subject: [PATCH] import CS linuxptp-4.4-3.el9 --- SOURCES/linuxptp-externalgm.patch | 275 +++++++++++++++++++++++++++++ SOURCES/linuxptp-externalpps.patch | 149 ++++++++++++++++ SOURCES/linuxptp-rtnlinit.patch | 75 ++++++++ SOURCES/linuxptp-unirecover.patch | 104 +++++++++++ SPECS/linuxptp.spec | 19 +- 5 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 SOURCES/linuxptp-externalgm.patch create mode 100644 SOURCES/linuxptp-externalpps.patch create mode 100644 SOURCES/linuxptp-rtnlinit.patch create mode 100644 SOURCES/linuxptp-unirecover.patch diff --git a/SOURCES/linuxptp-externalgm.patch b/SOURCES/linuxptp-externalgm.patch new file mode 100644 index 0000000..17719c8 --- /dev/null +++ b/SOURCES/linuxptp-externalgm.patch @@ -0,0 +1,275 @@ +commit a0fb624d9a2bb804d85f4597b1249123c7978fed +Author: Miroslav Lichvar +Date: Thu Jan 16 15:04:04 2025 +0100 + + clock: Add variables for external grandmaster. + + When ptp4l is configured to run as the server part of a bridge between + different domains, or a boundary clock split into multiple instances + (using one or two clocks), it announces its own clock identity as + grandmasterIdentity and stepsRemoved of 0. The clients of the server + don't see the true identity and distance, which can impact the result of + BMCA if there are multiple bridges/paths to the grandmaster. In the + worst case it could cause synchronization loops. + + Add new variables to the clock that will override the grandmaster + identity and stepsRemoved values included in announce messages to make + it possible to pass them from the source domain as if the bridge/BC was + a single PTP instance. The variables will be used only when in the + grandmaster state and when not zero. + + Runtime configuration over the UDS port will follow. + + Signed-off-by: Miroslav Lichvar + +diff --git a/clock.c b/clock.c +index 863e9d8..fb33ac0 100644 +--- a/clock.c ++++ b/clock.c +@@ -148,6 +148,8 @@ struct clock { + int step_window_counter; + int step_window; + struct time_zone tz[MAX_TIME_ZONES]; ++ struct ClockIdentity ext_gm_identity; ++ int ext_gm_steps_removed; + }; + + struct clock the_clock; +@@ -830,13 +832,22 @@ static int clock_compare_pds(struct parentDS *pds1, struct parentDS *pds2) + static void clock_update_grandmaster(struct clock *c) + { + struct parentDS *pds = &c->dad.pds, old_pds = *pds; ++ struct ClockIdentity gm_identity, nul_identity; ++ ++ memset(&nul_identity, 0, sizeof(nul_identity)); + memset(&c->cur, 0, sizeof(c->cur)); + memset(c->ptl, 0, sizeof(c->ptl)); + ++ if (!cid_eq(&nul_identity, &c->ext_gm_identity)) ++ gm_identity = c->ext_gm_identity; ++ else ++ gm_identity = c->dds.clockIdentity; ++ ++ c->cur.stepsRemoved = c->ext_gm_steps_removed; + pds->parentPortIdentity.clockIdentity = c->dds.clockIdentity; + /* Follow IEEE 1588 Table 30: Updates for state decision code M1 and M2 */ + pds->parentPortIdentity.portNumber = 0; +- pds->grandmasterIdentity = c->dds.clockIdentity; ++ pds->grandmasterIdentity = gm_identity; + pds->grandmasterClockQuality = c->dds.clockQuality; + pds->grandmasterPriority1 = c->dds.priority1; + pds->grandmasterPriority2 = c->dds.priority2; + +commit d76e19e4ed4a4c1892b98124ffb66dba354f76ac +Author: Miroslav Lichvar +Date: Thu Jan 16 15:04:05 2025 +0100 + + Add management message for external grandmaster properties. + + Add EXTERNAL_GRANDMASTER_PROPERTIES_NP management message to get and set + the new external grandmasterIdentity and stepsRemoved variables of the + clock. Together with the GRANDMASTER_SETTINGS_NP message all + grandmaster-specific values in announce messages can be now set. + + Signed-off-by: Miroslav Lichvar + +diff --git a/clock.c b/clock.c +index fb33ac0..7c30066 100644 +--- a/clock.c ++++ b/clock.c +@@ -422,6 +422,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + struct ptp_message *req, + struct ptp_message *rsp, int id) + { ++ struct external_grandmaster_properties_np *egpn; + struct alternate_time_offset_properties *atop; + struct alternate_time_offset_name *aton; + struct grandmaster_settings_np *gsn; +@@ -582,6 +583,12 @@ static int clock_management_fill_response(struct clock *c, struct port *p, + mtd->val = c->local_sync_uncertain; + datalen = sizeof(*mtd); + break; ++ case MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP: ++ egpn = (struct external_grandmaster_properties_np *) tlv->data; ++ egpn->gmIdentity = c->ext_gm_identity; ++ egpn->stepsRemoved = c->ext_gm_steps_removed; ++ datalen = sizeof(*egpn); ++ break; + default: + /* The caller should *not* respond to this message. */ + tlv_extra_recycle(extra); +@@ -620,6 +627,7 @@ static int clock_management_get_response(struct clock *c, struct port *p, + static int clock_management_set(struct clock *c, struct port *p, + int id, struct ptp_message *req, int *changed) + { ++ struct external_grandmaster_properties_np *egpn; + struct alternate_time_offset_properties *atop; + struct alternate_time_offset_name *aton; + struct grandmaster_settings_np *gsn; +@@ -710,6 +718,13 @@ static int clock_management_set(struct clock *c, struct port *p, + break; + } + break; ++ case MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP: ++ egpn = (struct external_grandmaster_properties_np *) tlv->data; ++ c->ext_gm_identity = egpn->gmIdentity; ++ c->ext_gm_steps_removed = egpn->stepsRemoved; ++ *changed = 1; ++ respond = 1; ++ break; + } + if (respond && !clock_management_get_response(c, p, id, req)) + pr_err("failed to send management set response"); +diff --git a/pmc.c b/pmc.c +index 0b61da4..6a6a94e 100644 +--- a/pmc.c ++++ b/pmc.c +@@ -158,6 +158,7 @@ static void pmc_show_signaling(struct ptp_message *msg, FILE *fp) + + static void pmc_show(struct ptp_message *msg, FILE *fp) + { ++ struct external_grandmaster_properties_np *egpn; + struct alternate_time_offset_properties *atop; + struct alternate_time_offset_name *aton; + struct ieee_c37_238_settings_np *pwr; +@@ -475,6 +476,14 @@ static void pmc_show(struct ptp_message *msg, FILE *fp) + fprintf(fp, "SYNCHRONIZATION_UNCERTAIN_NP " + IFMT "uncertain %hhu", mtd->val); + break; ++ case MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP: ++ egpn = (struct external_grandmaster_properties_np *) mgt->data; ++ fprintf(fp, "EXTERNAL_GRANDMASTER_PROPERTIES_NP " ++ IFMT "gmIdentity %s" ++ IFMT "stepsRemoved %hu", ++ cid2str(&egpn->gmIdentity), ++ egpn->stepsRemoved); ++ break; + case MID_PORT_DATA_SET: + p = (struct portDS *) mgt->data; + if (p->portState > PS_SLAVE) { +diff --git a/pmc_common.c b/pmc_common.c +index 7c77a10..b55e7ec 100644 +--- a/pmc_common.c ++++ b/pmc_common.c +@@ -132,6 +132,7 @@ struct management_id idtab[] = { + { "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 }, ++ { "EXTERNAL_GRANDMASTER_PROPERTIES_NP", MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP, do_set_action }, + /* Port management ID values */ + { "NULL_MANAGEMENT", MID_NULL_MANAGEMENT, null_management }, + { "CLOCK_DESCRIPTION", MID_CLOCK_DESCRIPTION, do_get_action }, +@@ -172,6 +173,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + { + int cnt, code = idtab[index].code, freq_traceable, leap_59, leap_61, + ptp_timescale, time_traceable, utc_off_valid; ++ struct external_grandmaster_properties_np egpn; + struct alternate_time_offset_properties atop; + struct ieee_c37_238_settings_np pwr; + struct grandmaster_settings_np gsn; +@@ -183,6 +185,7 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + char onoff_parent_data_set[4] = "off"; + char onoff_cmlds[4] = "off"; + char display_name[11] = {0}; ++ char identity[19]; + uint64_t jump; + uint8_t key; + int enable; +@@ -400,6 +403,19 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str) + IEEE_C37_238_VERSION_2017); + } + break; ++ case MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP: ++ cnt = sscanf(str, " %*s %*s " ++ "gmIdentity %18s " ++ "stepsRemoved %hu ", ++ identity, ++ &egpn.stepsRemoved); ++ if (cnt != 2 || str2cid(identity, &egpn.gmIdentity)) { ++ fprintf(stderr, "%s SET needs 2 values\n", ++ idtab[index].name); ++ break; ++ } ++ pmc_send_set_action(pmc, code, &egpn, sizeof(egpn)); ++ break; + } + } + +@@ -704,6 +720,9 @@ static int pmc_tlv_datalen(struct pmc *pmc, int id) + case MID_POWER_PROFILE_SETTINGS_NP: + len += sizeof(struct ieee_c37_238_settings_np); + break; ++ case MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP: ++ len += sizeof(struct external_grandmaster_properties_np); ++ break; + case MID_LOG_ANNOUNCE_INTERVAL: + case MID_ANNOUNCE_RECEIPT_TIMEOUT: + case MID_LOG_SYNC_INTERVAL: +diff --git a/tlv.c b/tlv.c +index b22277c..0639a71 100644 +--- a/tlv.c ++++ b/tlv.c +@@ -164,6 +164,7 @@ static void alttime_offset_pre_send(struct tlv_extra *extra) + static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + struct tlv_extra *extra) + { ++ struct external_grandmaster_properties_np *egpn; + struct alternate_time_offset_properties *atop; + struct alternate_time_offset_name *aton; + struct ieee_c37_238_settings_np *pwr; +@@ -490,6 +491,12 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len, + NTOHL(cmlds->scaledNeighborRateRatio); + NTOHL(cmlds->as_capable); + break; ++ case MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP: ++ if (data_len != sizeof(struct external_grandmaster_properties_np)) ++ goto bad_length; ++ egpn = (struct external_grandmaster_properties_np *) m->data; ++ NTOHS(egpn->stepsRemoved); ++ break; + case MID_SAVE_IN_NON_VOLATILE_STORAGE: + case MID_RESET_NON_VOLATILE_STORAGE: + case MID_INITIALIZE: +@@ -513,6 +520,7 @@ bad_length: + + static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + { ++ struct external_grandmaster_properties_np *egpn; + struct alternate_time_offset_properties *atop; + struct ieee_c37_238_settings_np *pwr; + struct unicast_master_table_np *umtn; +@@ -688,6 +696,10 @@ static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra) + HTONL(cmlds->scaledNeighborRateRatio); + HTONL(cmlds->as_capable); + break; ++ case MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP: ++ egpn = (struct external_grandmaster_properties_np *)m->data; ++ HTONS(egpn->stepsRemoved); ++ break; + } + } + +diff --git a/tlv.h b/tlv.h +index 1b464f3..f0c47ce 100644 +--- a/tlv.h ++++ b/tlv.h +@@ -100,6 +100,7 @@ enum management_action { + #define MID_GRANDMASTER_SETTINGS_NP 0xC001 + #define MID_SUBSCRIBE_EVENTS_NP 0xC003 + #define MID_SYNCHRONIZATION_UNCERTAIN_NP 0xC006 ++#define MID_EXTERNAL_GRANDMASTER_PROPERTIES_NP 0xC00D + + /* Port management ID values */ + #define MID_NULL_MANAGEMENT 0x0000 +@@ -489,6 +490,11 @@ struct msg_interface_rate_tlv { + UInteger16 numberOfBitsAfterTimestamp; + } PACKED; + ++struct external_grandmaster_properties_np { ++ struct ClockIdentity gmIdentity; ++ UInteger16 stepsRemoved; ++} PACKED; ++ + /** + * Allocates a new tlv_extra structure. + * @return Pointer to a new structure on success or NULL otherwise. diff --git a/SOURCES/linuxptp-externalpps.patch b/SOURCES/linuxptp-externalpps.patch new file mode 100644 index 0000000..b8f1cb8 --- /dev/null +++ b/SOURCES/linuxptp-externalpps.patch @@ -0,0 +1,149 @@ +commit f20e568cb2bf3b0ea6105e5624409f02fb9aa2bc +Author: Miroslav Lichvar +Date: Thu Jan 9 15:46:05 2025 +0100 + + ts2phc: Add option for external reference in automatic mode. + + ts2phc running in the automatic mode assumes that the PPS source is one + of the PHCs used by ptp4l. That doesn't always have to be the case. Add + "external_pps" option to use the non-automatic-mode reading of the + source timestamp and don't mark sink clocks as targets if they are + synchronized by ptp4l. + + The use case is holdover with an externally controlled stabilized clock. + The clock is synchronized by an external process to the PHC when it's + synchronized by ptp4l and the PHC is synchronized to the external clock + by ts2phc when not synchronized by ptp4l. Multiple PHCs can be connected + to the external clock to make a JBOD boundary clock. + + Don't require the PHC that is synchronized by ptp4l to be receiving the + PPS signal (providing timestamps) to make it possible to switch the PPS + direction to synchronize the external clock if there is only one + usable channel. + + (This is a RHEL-specific downstream patch using "ts2phc.rh_external_pps" + name for the option to make it clear it's considered experimental.) + + Signed-off-by: Miroslav Lichvar + +diff --git a/config.c b/config.c +index cbff976..fedc3a0 100644 +--- a/config.c ++++ b/config.c +@@ -368,6 +368,7 @@ struct config_item config_tab[] = { + GLOB_ITEM_ENU("time_stamping", TS_HARDWARE, timestamping_enu), + PORT_ITEM_INT("transportSpecific", 0, 0, 0x0F), + PORT_ITEM_INT("ts2phc.channel", 0, 0, INT_MAX), ++ GLOB_ITEM_INT("ts2phc.rh_external_pps", 0, 0, 1), + PORT_ITEM_INT("ts2phc.extts_correction", 0, INT_MIN, INT_MAX), + PORT_ITEM_ENU("ts2phc.extts_polarity", PTP_RISING_EDGE, extts_polarity_enu), + PORT_ITEM_INT("ts2phc.holdover", 0, 0, INT_MAX), +diff --git a/ts2phc.8 b/ts2phc.8 +index f17ed71..d57402c 100644 +--- a/ts2phc.8 ++++ b/ts2phc.8 +@@ -287,6 +287,20 @@ the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the + \fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP. + The default is 0 (disabled). + ++.TP ++.B ts2phc.rh_external_pps ++This is a RHEL-specific experimental option which can be renamed, ++removed, or the functionality changed, in a future minor update. ++ ++It enables an external reference with the \fB-a\fP option. If set to 1, ts2phc ++will assume the source of the PPS signal is a different clock from the PHCs ++used by ptp4l (configured with the \fBboundary_clock_jbod\fP option). The use ++case is a holdover using an externally controlled stabilized clock, which is ++expected to be synchronized to the PHC that is synchronized by ptp4l, and ++running free when ptp4l is not synchronizing any of the PHCs. Note that it is a ++different holdover than the one enabled by the \fBts2phc.holdover\fP option ++below. The default is 0 (disabled). ++ + .TP + .B ts2phc.nmea_delay + Specifies the minimum expected delay of NMEA RMC messages in nanoseconds. +diff --git a/ts2phc.c b/ts2phc.c +index 39d31b6..5448496 100644 +--- a/ts2phc.c ++++ b/ts2phc.c +@@ -348,18 +348,26 @@ static void ts2phc_reconfigure(struct ts2phc_private *priv) + } + num_target_clocks++; + break; +- case PS_UNCALIBRATED: +- num_ref_clocks++; +- break; + case PS_SLAVE: + ref_clk = c; ++ /* Fall through */ ++ case PS_UNCALIBRATED: + num_ref_clocks++; ++ if (priv->external_pps && c->is_target) { ++ pr_info("unselecting %s for synchronization", ++ c->name); ++ c->is_target = false; ++ } + break; + default: + break; + } + last = c; + } ++ if (priv->external_pps) { ++ pr_info("assuming external reference clock"); ++ return; ++ } + if (num_target_clocks >= 1 && !ref_clk) { + priv->ref_clock = last; + priv->ref_clock->is_target = false; +@@ -447,7 +455,7 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg) + struct ts2phc_clock *c; + int holdover, valid; + +- if (autocfg) { ++ if (autocfg && !priv->external_pps) { + if (!priv->ref_clock) { + pr_debug("no reference clock, skipping"); + return; +@@ -787,6 +795,8 @@ int main(int argc, char *argv[]) + return -1; + } + ++ priv.external_pps = config_get_int(cfg, NULL, "ts2phc.rh_external_pps"); ++ + priv.holdover_length = config_get_int(cfg, NULL, "ts2phc.holdover"); + priv.holdover_start = 0; + +diff --git a/ts2phc.h b/ts2phc.h +index 5dbde9b..63e6122 100644 +--- a/ts2phc.h ++++ b/ts2phc.h +@@ -52,6 +52,7 @@ struct ts2phc_private { + struct config *cfg; + struct pmc_agent *agent; + struct ts2phc_clock *ref_clock; ++ bool external_pps; + bool state_changed; + LIST_HEAD(port_head, ts2phc_port) ports; + LIST_HEAD(clock_head, ts2phc_clock) clocks; +diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c +index af34e39..9076de9 100644 +--- a/ts2phc_pps_sink.c ++++ b/ts2phc_pps_sink.c +@@ -441,6 +441,15 @@ int ts2phc_pps_sink_poll(struct ts2phc_private *priv) + all_sinks_have_events = true; + + for (i = 0; i < priv->n_sinks; i++) { ++ /* ++ * In the external PPS mode don't require non-target ++ * clocks to be receiving PPS to allow switching the ++ * PPS direction to synchronize the external clock. ++ */ ++ if (priv->external_pps && ++ !polling_array->sink[i]->clock->is_target) ++ continue; ++ + if (!polling_array->collected_events[i]) { + all_sinks_have_events = false; + break; diff --git a/SOURCES/linuxptp-rtnlinit.patch b/SOURCES/linuxptp-rtnlinit.patch new file mode 100644 index 0000000..60e011d --- /dev/null +++ b/SOURCES/linuxptp-rtnlinit.patch @@ -0,0 +1,75 @@ +commit 01de33e91f9717d0cbae5af6eee2beb45deee219 +Author: Miroslav Lichvar +Date: Tue Mar 4 15:53:37 2025 +0100 + + port: Refresh link status on faults. + + ptp4l gets the ENOBUFS error on the netlink socket when the kernel has + to drop messages due to full socket buffer. If ptp4l has a port in the + faulty state waiting for the link to go up and that event corresponds + to one of the dropped netlink messages, the port will be stuck in the + faulty state until the link goes down and up again. + + To prevent the port from getting stuck, request the current link status + when dispatching the EV_FAULT_DETECTED event. Also, reopen the socket to + get rid of the buffered messages when handling the fault and again when + reinitializing the port. + + Signed-off-by: Miroslav Lichvar + Reviewed-by: Jacob Keller + +diff --git a/port.c b/port.c +index 7f945ac..1bb407c 100644 +--- a/port.c ++++ b/port.c +@@ -1975,6 +1975,20 @@ static int port_cmlds_initialize(struct port *p) + return port_cmlds_renew(p, now.tv_sec); + } + ++static void port_rtnl_initialize(struct port *p) ++{ ++ /* Reopen the socket to get rid of buffered messages */ ++ if (p->fda.fd[FD_RTNL] >= 0) { ++ rtnl_close(p->fda.fd[FD_RTNL]); ++ } ++ p->fda.fd[FD_RTNL] = rtnl_open(); ++ if (p->fda.fd[FD_RTNL] >= 0) { ++ rtnl_link_query(p->fda.fd[FD_RTNL], interface_name(p->iface)); ++ } ++ ++ clock_fda_changed(p->clock); ++} ++ + void port_disable(struct port *p) + { + int i; +@@ -2087,13 +2101,8 @@ int port_initialize(struct port *p) + if (p->bmca == BMCA_NOOP) { + port_set_delay_tmo(p); + } +- if (p->fda.fd[FD_RTNL] == -1) { +- p->fda.fd[FD_RTNL] = rtnl_open(); +- } +- if (p->fda.fd[FD_RTNL] >= 0) { +- const char *ifname = interface_name(p->iface); +- rtnl_link_query(p->fda.fd[FD_RTNL], ifname); +- } ++ ++ port_rtnl_initialize(p); + } + + port_nrate_initialize(p); +@@ -3768,6 +3777,13 @@ int port_state_update(struct port *p, enum fsm_event event, int mdiff) + if (port_link_status_get(p) && clear_fault_asap(&i)) { + pr_notice("%s: clearing fault immediately", p->log_name); + next = p->state_machine(next, EV_FAULT_CLEARED, 0); ++ } else if (event == EV_FAULT_DETECTED) { ++ /* ++ * Reopen the netlink socket and refresh the link ++ * status in case the fault was triggered by a missed ++ * netlink message (ENOBUFS). ++ */ ++ port_rtnl_initialize(p); + } + } + diff --git a/SOURCES/linuxptp-unirecover.patch b/SOURCES/linuxptp-unirecover.patch new file mode 100644 index 0000000..f3f2bf7 --- /dev/null +++ b/SOURCES/linuxptp-unirecover.patch @@ -0,0 +1,104 @@ +commit 71241f3fdcff59b35ad5de0b8b37cb07a4b677bd +Author: Vincent Cheng +Date: Tue Sep 17 15:23:54 2024 -0400 + + port: Fix unicast negotiation doesn't recover after FAULT_DETECTED + + _Problem_ + After a port link down/up or a tx_timestamp timeout issue, a port acting + as unicast master does not issue ANNC messages after granting unicast + request for ANNC. + + _Analysis_ + When a port FAULT occurs, the port transitions to FAULTY on FAULT_DETECTED + and subsequently port_disable(p) and port_initialize(p) are called on port recovery. + + A port acting as a unicast master, stores clients in p->unicast_service->queue. + + When a port receives a unicast request, unicast_service_add() is called. + + In unicast_service_add(), if the request does not match an entry in + p->unicast_service->queue, FD_UNICAST_SRV_TIMER is started via + unicast_service_rearm_timer(). + + If the unicast request matches an existing p->unicast_service->queue entry + the request is considered an extension and FD_UNICAST_SRV_TIMER must + already be running. + + port_disable() clears FD_UNICAST_SRV_TIMER, ie. stops FD_UNICAST_SRV_TIMER. + However, port_disable() does not clear p->unicast_service->queue. + When the port is restarted, the port retains the previous client data. + + After port recovery, when the client attempts to restart the unicast + service, the request matches an existing entry in p->unicast_service->queue, + and so FD_UNICAST_SRV_TIMER is not started because the port expected + that the FD_UNICAST_SRV_TIMER is already running. + + _Fix_ + This patch clears the unicast client data in port_disable() so + that upon recovery, the initial unicast request will be considered + a new request and trigger the start of the FD_UNICAST_SRV_TIMER. + + v2: + - Add missing sign-off + - Send to develop-request instead of users list + + Signed-off-by: Vincent Cheng + +diff --git a/port.c b/port.c +index 1bb407c..90b0860 100644 +--- a/port.c ++++ b/port.c +@@ -1999,6 +1999,7 @@ void port_disable(struct port *p) + flush_peer_delay(p); + + p->best = NULL; ++ unicast_service_clear_clients(p); + free_foreign_masters(p); + transport_close(p->trp, &p->fda); + +diff --git a/unicast_service.c b/unicast_service.c +index 2fc2fbe..cee6691 100644 +--- a/unicast_service.c ++++ b/unicast_service.c +@@ -583,3 +583,24 @@ int unicast_service_timer(struct port *p) + } + return err; + } ++ ++void unicast_service_clear_clients(struct port *p) ++{ ++ struct unicast_client_address *client, *temp; ++ struct unicast_service_interval *interval; ++ ++ if (!p->unicast_service) { ++ return; ++ } ++ ++ while ((interval = pqueue_extract(p->unicast_service->queue)) != NULL) { ++ ++ LIST_REMOVE(interval, list); ++ ++ LIST_FOREACH_SAFE(client, &interval->clients, list, temp) { ++ LIST_REMOVE(client, list); ++ free(client); ++ } ++ free(interval); ++ } ++} +\ No newline at end of file +diff --git a/unicast_service.h b/unicast_service.h +index f0d6487..8ea1a59 100644 +--- a/unicast_service.h ++++ b/unicast_service.h +@@ -87,4 +87,10 @@ void unicast_service_remove(struct port *p, struct ptp_message *m, + */ + int unicast_service_timer(struct port *p); + ++/** ++ * Clears unicast clients on a given port. ++ * @param p The port in question. ++ */ ++void unicast_service_clear_clients(struct port *p); ++ + #endif diff --git a/SPECS/linuxptp.spec b/SPECS/linuxptp.spec index 96f1fe1..53fef4e 100644 --- a/SPECS/linuxptp.spec +++ b/SPECS/linuxptp.spec @@ -4,7 +4,7 @@ Name: linuxptp Version: 4.4 -Release: 1%{?dist} +Release: 3%{?dist} Summary: PTP implementation for Linux License: GPL-2.0-or-later @@ -33,6 +33,14 @@ Patch5: linuxptp-udpaddr.patch Patch6: linuxptp-staticauto.patch # don't require -O option without -a and -w in phc2sys Patch7: linuxptp-nowait.patch +# add experimental option for external PPS in ts2phc automatic mode +Patch8: linuxptp-externalpps.patch +# add command to set external grandmaster properties +Patch9: linuxptp-externalgm.patch +# refresh link status on faults +Patch10: linuxptp-rtnlinit.patch +# fix unicast server to recover after port fault +Patch11: linuxptp-unirecover.patch # check for EL-specific kernels with vclock support Patch12: linuxptp-vclock.patch @@ -117,6 +125,15 @@ PATH=..:$PATH ./run %{_mandir}/man8/*.8* %changelog +* Wed May 28 2025 Miroslav Lichvar 4.4-3 +- fix unicast server to recover after port fault (RHEL-93496) + +* Wed May 14 2025 Miroslav Lichvar 4.4-2 +- add experimental option for external PPS in ts2phc automatic mode + (RHEL-72468) +- add command to set external grandmaster properties (RHEL-90585) +- refresh link status on faults (RHEL-56168) + * Tue Dec 03 2024 Miroslav Lichvar 4.4-1 - update to 4.4 (RHEL-58213 RHEL-57040) - fix port-specific ptp/p2p_dst_ipv4 configuration (RHEL-60027)