import CS linuxptp-4.4-3.el9

This commit is contained in:
eabdullin 2025-09-15 12:20:11 +00:00
parent 47794fd076
commit f72a08b49b
5 changed files with 621 additions and 1 deletions

View File

@ -0,0 +1,275 @@
commit a0fb624d9a2bb804d85f4597b1249123c7978fed
Author: Miroslav Lichvar <mlichvar@redhat.com>
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 <mlichvar@redhat.com>
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 <mlichvar@redhat.com>
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 <mlichvar@redhat.com>
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.

View File

@ -0,0 +1,149 @@
commit f20e568cb2bf3b0ea6105e5624409f02fb9aa2bc
Author: Miroslav Lichvar <mlichvar@redhat.com>
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 <mlichvar@redhat.com>
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;

View File

@ -0,0 +1,75 @@
commit 01de33e91f9717d0cbae5af6eee2beb45deee219
Author: Miroslav Lichvar <mlichvar@redhat.com>
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 <mlichvar@redhat.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
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);
}
}

View File

@ -0,0 +1,104 @@
commit 71241f3fdcff59b35ad5de0b8b37cb07a4b677bd
Author: Vincent Cheng <vscheng@gmail.com>
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 <vincent.cheng.xh@renesas.com>
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

View File

@ -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 <mlichvar@redhat.com> 4.4-3
- fix unicast server to recover after port fault (RHEL-93496)
* Wed May 14 2025 Miroslav Lichvar <mlichvar@redhat.com> 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 <mlichvar@redhat.com> 4.4-1
- update to 4.4 (RHEL-58213 RHEL-57040)
- fix port-specific ptp/p2p_dst_ipv4 configuration (RHEL-60027)