import OL linuxptp-4.4-1.el9_6.2
This commit is contained in:
parent
4dd6d94ed7
commit
615b7e880d
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,3 +1,3 @@
|
|||||||
SOURCES/clknetsim-5d1dc0.tar.gz
|
SOURCES/clknetsim-64df92.tar.gz
|
||||||
SOURCES/linuxptp-4.2.tgz
|
SOURCES/linuxptp-4.4.tgz
|
||||||
SOURCES/linuxptp-testsuite-bf8ead.tar.gz
|
SOURCES/linuxptp-testsuite-d27dbd.tar.gz
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
3ae72eb0ddafd8d8aeea7eac382d6e4a958717ef SOURCES/clknetsim-5d1dc0.tar.gz
|
acb5ea319dfe96edd43033b35f025b3557b5e8a7 SOURCES/clknetsim-64df92.tar.gz
|
||||||
309e6ab1fa3f61b3deb1735c3082dc2070870be1 SOURCES/linuxptp-4.2.tgz
|
f35d56a2471359856843f7e703e5e8c681a66f0d SOURCES/linuxptp-4.4.tgz
|
||||||
7594d0705a1a648d5f7380d476bb3afebff21f6c SOURCES/linuxptp-testsuite-bf8ead.tar.gz
|
8db14fbc7bdde436f2e957e77319569614ccf625 SOURCES/linuxptp-testsuite-d27dbd.tar.gz
|
||||||
|
@ -1,176 +0,0 @@
|
|||||||
commit 073faba77e8a82c54210941cee6023dc9e719329
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu Apr 25 14:25:57 2024 +0200
|
|
||||||
|
|
||||||
udp+udp6: Make IP addresses configurable.
|
|
||||||
|
|
||||||
Allow configuration of the multicast IPv4/IPv6 addresses, which can be
|
|
||||||
useful for testing. This complements the L2-specific ptp_dst_mac and
|
|
||||||
p2p_dst_mac options.
|
|
||||||
|
|
||||||
[ RPC: removed unused #defines PTP_PRIMARY_MCAST_IPADDR and PTP_PDELAY_MCAST_IPADDR ]
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
|
|
||||||
|
|
||||||
diff --git a/config.c b/config.c
|
|
||||||
index d7775c0..c220a3e 100644
|
|
||||||
--- a/config.c
|
|
||||||
+++ b/config.c
|
|
||||||
@@ -299,6 +299,9 @@ struct config_item config_tab[] = {
|
|
||||||
GLOB_ITEM_INT("offsetScaledLogVariance", 0xffff, 0, UINT16_MAX),
|
|
||||||
PORT_ITEM_INT("operLogPdelayReqInterval", 0, INT8_MIN, INT8_MAX),
|
|
||||||
PORT_ITEM_INT("operLogSyncInterval", 0, INT8_MIN, INT8_MAX),
|
|
||||||
+ PORT_ITEM_STR("p2p_dst_ipv4", "224.0.0.107"),
|
|
||||||
+ PORT_ITEM_STR("p2p_dst_ipv6", "FF02:0:0:0:0:0:0:6B"),
|
|
||||||
+ PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"),
|
|
||||||
PORT_ITEM_INT("path_trace_enabled", 0, 0, 1),
|
|
||||||
PORT_ITEM_INT("phc_index", -1, -1, INT_MAX),
|
|
||||||
GLOB_ITEM_DBL("pi_integral_const", 0.0, 0.0, DBL_MAX),
|
|
||||||
@@ -317,8 +320,9 @@ struct config_item config_tab[] = {
|
|
||||||
GLOB_ITEM_INT("priority1", 128, 0, UINT8_MAX),
|
|
||||||
GLOB_ITEM_INT("priority2", 128, 0, UINT8_MAX),
|
|
||||||
GLOB_ITEM_STR("productDescription", ";;"),
|
|
||||||
+ PORT_ITEM_STR("ptp_dst_ipv4", "224.0.1.129"),
|
|
||||||
+ PORT_ITEM_STR("ptp_dst_ipv6", "FF0E:0:0:0:0:0:0:181"),
|
|
||||||
PORT_ITEM_STR("ptp_dst_mac", "01:1B:19:00:00:00"),
|
|
||||||
- PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"),
|
|
||||||
GLOB_ITEM_INT("ptp_minor_version", 0, 0, 1),
|
|
||||||
GLOB_ITEM_STR("refclock_sock_address", "/var/run/refclock.ptp.sock"),
|
|
||||||
GLOB_ITEM_STR("revisionData", ";;"),
|
|
||||||
diff --git a/configs/default.cfg b/configs/default.cfg
|
|
||||||
index 8f94c16..54ce62a 100644
|
|
||||||
--- a/configs/default.cfg
|
|
||||||
+++ b/configs/default.cfg
|
|
||||||
@@ -94,6 +94,10 @@ write_phase_mode 0
|
|
||||||
# Transport options
|
|
||||||
#
|
|
||||||
transportSpecific 0x0
|
|
||||||
+ptp_dst_ipv4 224.0.1.129
|
|
||||||
+p2p_dst_ipv4 224.0.0.107
|
|
||||||
+ptp_dst_ipv6 FF0E:0:0:0:0:0:0:181
|
|
||||||
+p2p_dst_ipv6 FF02:0:0:0:0:0:0:6B
|
|
||||||
ptp_dst_mac 01:1B:19:00:00:00
|
|
||||||
p2p_dst_mac 01:80:C2:00:00:0E
|
|
||||||
udp_ttl 1
|
|
||||||
diff --git a/ptp4l.8 b/ptp4l.8
|
|
||||||
index c59b0b4..a4eb603 100644
|
|
||||||
--- a/ptp4l.8
|
|
||||||
+++ b/ptp4l.8
|
|
||||||
@@ -397,6 +397,27 @@ This value may be changed dynamically using the
|
|
||||||
POWER_PROFILE_SETTINGS_NP management message.
|
|
||||||
The default is "none".
|
|
||||||
|
|
||||||
+.TP
|
|
||||||
+.B ptp_dst_ipv4
|
|
||||||
+The IPv4 address to which PTP messages should be sent.
|
|
||||||
+Relevant only with UDPv4 transport. The default is 224.0.1.129.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B p2p_dst_ipv4
|
|
||||||
+The IPv4 address to which peer delay messages should be sent.
|
|
||||||
+Relevant only with UDPv4 transport. The default is 224.0.0.107.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B ptp_dst_ipv6
|
|
||||||
+The IPv6 address to which PTP messages should be sent.
|
|
||||||
+The second byte of the address is substituted with udp6_scope.
|
|
||||||
+Relevant only with UDPv6 transport. The default is FF0E:0:0:0:0:0:0:181.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B p2p_dst_ipv6
|
|
||||||
+The IPv6 address to which peer delay messages should be sent.
|
|
||||||
+Relevant only with UDPv6 transport. The default is FF02:0:0:0:0:0:0:6B.
|
|
||||||
+
|
|
||||||
.TP
|
|
||||||
.B ptp_dst_mac
|
|
||||||
The MAC address to which PTP messages should be sent.
|
|
||||||
diff --git a/udp.c b/udp.c
|
|
||||||
index 7c9402e..38d0ec4 100644
|
|
||||||
--- a/udp.c
|
|
||||||
+++ b/udp.c
|
|
||||||
@@ -39,8 +39,6 @@
|
|
||||||
|
|
||||||
#define EVENT_PORT 319
|
|
||||||
#define GENERAL_PORT 320
|
|
||||||
-#define PTP_PRIMARY_MCAST_IPADDR "224.0.1.129"
|
|
||||||
-#define PTP_PDELAY_MCAST_IPADDR "224.0.0.107"
|
|
||||||
|
|
||||||
struct udp {
|
|
||||||
struct transport t;
|
|
||||||
@@ -157,6 +155,7 @@ static int udp_open(struct transport *t, struct interface *iface,
|
|
||||||
const char *name = interface_name(iface);
|
|
||||||
uint8_t event_dscp, general_dscp;
|
|
||||||
int efd, gfd, ttl;
|
|
||||||
+ char *str;
|
|
||||||
|
|
||||||
ttl = config_get_int(t->cfg, name, "udp_ttl");
|
|
||||||
udp->mac.len = 0;
|
|
||||||
@@ -165,11 +164,17 @@ static int udp_open(struct transport *t, struct interface *iface,
|
|
||||||
udp->ip.len = 0;
|
|
||||||
sk_interface_addr(name, AF_INET, &udp->ip);
|
|
||||||
|
|
||||||
- if (!inet_aton(PTP_PRIMARY_MCAST_IPADDR, &mcast_addr[MC_PRIMARY]))
|
|
||||||
+ str = config_get_string(t->cfg, name, "ptp_dst_ipv4");
|
|
||||||
+ if (!inet_aton(str, &mcast_addr[MC_PRIMARY])) {
|
|
||||||
+ pr_err("invalid ptp_dst_ipv4 %s", str);
|
|
||||||
return -1;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (!inet_aton(PTP_PDELAY_MCAST_IPADDR, &mcast_addr[MC_PDELAY]))
|
|
||||||
+ str = config_get_string(t->cfg, name, "p2p_dst_ipv4");
|
|
||||||
+ if (!inet_aton(str, &mcast_addr[MC_PDELAY])) {
|
|
||||||
+ pr_err("invalid p2p_dst_ipv4 %s", str);
|
|
||||||
return -1;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
efd = open_socket(name, mcast_addr, EVENT_PORT, ttl);
|
|
||||||
if (efd < 0)
|
|
||||||
diff --git a/udp6.c b/udp6.c
|
|
||||||
index bde1710..188d20e 100644
|
|
||||||
--- a/udp6.c
|
|
||||||
+++ b/udp6.c
|
|
||||||
@@ -40,10 +40,6 @@
|
|
||||||
#define EVENT_PORT 319
|
|
||||||
#define GENERAL_PORT 320
|
|
||||||
|
|
||||||
-/* The 0x0e in second byte is substituted with udp6_scope at runtime. */
|
|
||||||
-#define PTP_PRIMARY_MCAST_IP6ADDR "FF0E:0:0:0:0:0:0:181"
|
|
||||||
-#define PTP_PDELAY_MCAST_IP6ADDR "FF02:0:0:0:0:0:0:6B"
|
|
||||||
-
|
|
||||||
enum { MC_PRIMARY, MC_PDELAY };
|
|
||||||
|
|
||||||
struct udp6 {
|
|
||||||
@@ -167,6 +163,7 @@ static int udp6_open(struct transport *t, struct interface *iface,
|
|
||||||
const char *name = interface_name(iface);
|
|
||||||
uint8_t event_dscp, general_dscp;
|
|
||||||
int efd, gfd, hop_limit;
|
|
||||||
+ char *str;
|
|
||||||
|
|
||||||
hop_limit = config_get_int(t->cfg, name, "udp_ttl");
|
|
||||||
udp6->mac.len = 0;
|
|
||||||
@@ -175,16 +172,20 @@ static int udp6_open(struct transport *t, struct interface *iface,
|
|
||||||
udp6->ip.len = 0;
|
|
||||||
sk_interface_addr(name, AF_INET6, &udp6->ip);
|
|
||||||
|
|
||||||
- if (1 != inet_pton(AF_INET6, PTP_PRIMARY_MCAST_IP6ADDR,
|
|
||||||
- &udp6->mc6_addr[MC_PRIMARY]))
|
|
||||||
+ str = config_get_string(t->cfg, name, "ptp_dst_ipv6");
|
|
||||||
+ if (1 != inet_pton(AF_INET6, str, &udp6->mc6_addr[MC_PRIMARY])) {
|
|
||||||
+ pr_err("invalid ptp_dst_ipv6 %s", str);
|
|
||||||
return -1;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
udp6->mc6_addr[MC_PRIMARY].s6_addr[1] = config_get_int(t->cfg, name,
|
|
||||||
"udp6_scope");
|
|
||||||
|
|
||||||
- if (1 != inet_pton(AF_INET6, PTP_PDELAY_MCAST_IP6ADDR,
|
|
||||||
- &udp6->mc6_addr[MC_PDELAY]))
|
|
||||||
+ str = config_get_string(t->cfg, name, "p2p_dst_ipv6");
|
|
||||||
+ if (1 != inet_pton(AF_INET6, str, &udp6->mc6_addr[MC_PDELAY])) {
|
|
||||||
+ pr_err("invalid p2p_dst_ipv6 %s", str);
|
|
||||||
return -1;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
efd = open_socket_ipv6(name, udp6->mc6_addr, EVENT_PORT, &udp6->index,
|
|
||||||
hop_limit);
|
|
275
SOURCES/linuxptp-externalgm.patch
Normal file
275
SOURCES/linuxptp-externalgm.patch
Normal 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.
|
149
SOURCES/linuxptp-externalpps.patch
Normal file
149
SOURCES/linuxptp-externalpps.patch
Normal 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;
|
@ -1,360 +0,0 @@
|
|||||||
commit c7acd4b5bdb95e45d93833ffaac9cac51dfe934b
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu May 16 15:52:48 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Avoid unnecessary call of getppstime().
|
|
||||||
|
|
||||||
Don't get the ToD timestamp for the pulse polarity detection if it won't
|
|
||||||
be needed (i.e. extts_polarity is not "both"). This allows PPS
|
|
||||||
timestamps to be saved even when the ToD source fails.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
|
|
||||||
index 0d399b8..76912a2 100644
|
|
||||||
--- a/ts2phc_pps_sink.c
|
|
||||||
+++ b/ts2phc_pps_sink.c
|
|
||||||
@@ -277,21 +277,22 @@ static enum extts_result ts2phc_pps_sink_event(struct ts2phc_private *priv,
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
- err = ts2phc_pps_source_getppstime(priv->src, &source_ts);
|
|
||||||
- if (err < 0) {
|
|
||||||
- pr_debug("source ts not valid");
|
|
||||||
- return 0;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- if (sink->polarity == (PTP_RISING_EDGE | PTP_FALLING_EDGE) &&
|
|
||||||
- ts2phc_pps_sink_ignore(priv, sink, source_ts)) {
|
|
||||||
+ if (sink->polarity == (PTP_RISING_EDGE | PTP_FALLING_EDGE)) {
|
|
||||||
+ err = ts2phc_pps_source_getppstime(priv->src, &source_ts);
|
|
||||||
+ if (err < 0) {
|
|
||||||
+ pr_debug("source ts not valid");
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- pr_debug("%s SKIP extts index %u at %lld.%09u src %" PRIi64 ".%ld",
|
|
||||||
- sink->name, event.index, event.t.sec, event.t.nsec,
|
|
||||||
- (int64_t) source_ts.tv_sec, source_ts.tv_nsec);
|
|
||||||
+ if (ts2phc_pps_sink_ignore(priv, sink, source_ts)) {
|
|
||||||
+ pr_debug("%s SKIP extts index %u at %lld.%09u src %" PRIi64 ".%ld",
|
|
||||||
+ sink->name, event.index, event.t.sec,
|
|
||||||
+ event.t.nsec, (int64_t)source_ts.tv_sec,
|
|
||||||
+ source_ts.tv_nsec);
|
|
||||||
|
|
||||||
- result = EXTTS_IGNORE;
|
|
||||||
- goto out;
|
|
||||||
+ result = EXTTS_IGNORE;
|
|
||||||
+ goto out;
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
|
|
||||||
commit 9880cccc928351a42a3fa9e018949442aca7ddae
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu May 16 15:52:49 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Add holdover support.
|
|
||||||
|
|
||||||
If the external PPS signal is generated by a clock with better long-term
|
|
||||||
stability than the clock synchronizing to it, it can work as a good time
|
|
||||||
source for some period of time after losing its own time source (e.g.
|
|
||||||
GPS receiver losing its signal).
|
|
||||||
|
|
||||||
Add an option to specify a holdover interval where ts2phc can continue
|
|
||||||
synchronizing the clock without any ToD information. Allow that only in
|
|
||||||
the LOCKED_STABLE servo state, which needs to be enabled by the
|
|
||||||
servo_num_offset_values option.
|
|
||||||
|
|
||||||
This is supported only in the non-automatic mode and when the pulse
|
|
||||||
polarity detection is disabled.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/config.c b/config.c
|
|
||||||
index c220a3e..58481db 100644
|
|
||||||
--- a/config.c
|
|
||||||
+++ b/config.c
|
|
||||||
@@ -344,6 +344,7 @@ struct config_item config_tab[] = {
|
|
||||||
PORT_ITEM_INT("ts2phc.channel", 0, 0, INT_MAX),
|
|
||||||
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),
|
|
||||||
PORT_ITEM_INT("ts2phc.master", 0, 0, 1),
|
|
||||||
PORT_ITEM_INT("ts2phc.nmea_baudrate", 9600, 300, INT_MAX),
|
|
||||||
GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""),
|
|
||||||
diff --git a/ts2phc.8 b/ts2phc.8
|
|
||||||
index 852a527..bcc6f61 100644
|
|
||||||
--- a/ts2phc.8
|
|
||||||
+++ b/ts2phc.8
|
|
||||||
@@ -168,6 +168,16 @@ by changing the clock frequency instead of stepping the clock. When
|
|
||||||
set to 0.0, the servo will never step the clock except on start.
|
|
||||||
The default is 0.0.
|
|
||||||
|
|
||||||
+.TP
|
|
||||||
+.B ts2phc.holdover
|
|
||||||
+The holdover interval, specified in seconds. When the ToD information stops
|
|
||||||
+working (e.g. GNSS receiver lost its fix), ts2phc is allowed for the specified
|
|
||||||
+interval to continue synchronizing the target clock as long as the servo is in
|
|
||||||
+the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the
|
|
||||||
+\fBservo_num_offset_values\fP option. The holdover is not supported with the
|
|
||||||
+\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP.
|
|
||||||
+The default is 0 (disabled).
|
|
||||||
+
|
|
||||||
.TP
|
|
||||||
.B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port
|
|
||||||
Specifies the remote host providing ToD information when using the
|
|
||||||
diff --git a/ts2phc.c b/ts2phc.c
|
|
||||||
index 4817c85..d552e0f 100644
|
|
||||||
--- a/ts2phc.c
|
|
||||||
+++ b/ts2phc.c
|
|
||||||
@@ -440,9 +440,10 @@ static int ts2phc_pps_source_implicit_tstamp(struct ts2phc_private *priv,
|
|
||||||
|
|
||||||
static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
|
|
||||||
{
|
|
||||||
+ struct timespec source_ts, now;
|
|
||||||
tmv_t source_tmv;
|
|
||||||
struct ts2phc_clock *c;
|
|
||||||
- int valid, err;
|
|
||||||
+ int holdover, valid;
|
|
||||||
|
|
||||||
if (autocfg) {
|
|
||||||
if (!priv->ref_clock) {
|
|
||||||
@@ -456,9 +457,20 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
- err = ts2phc_pps_source_implicit_tstamp(priv, &source_tmv);
|
|
||||||
- if (err < 0)
|
|
||||||
+ valid = !ts2phc_pps_source_implicit_tstamp(priv, &source_tmv);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (valid) {
|
|
||||||
+ priv->holdover_start = 0;
|
|
||||||
+ holdover = 0;
|
|
||||||
+ } else {
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
+
|
|
||||||
+ if (!priv->holdover_start)
|
|
||||||
+ priv->holdover_start = now.tv_sec;
|
|
||||||
+ if (now.tv_sec >= priv->holdover_start + priv->holdover_length)
|
|
||||||
return;
|
|
||||||
+ holdover = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LIST_FOREACH(c, &priv->clocks, list) {
|
|
||||||
@@ -475,6 +487,16 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ if (holdover) {
|
|
||||||
+ if (c->servo_state != SERVO_LOCKED_STABLE)
|
|
||||||
+ continue;
|
|
||||||
+ source_ts = tmv_to_timespec(ts);
|
|
||||||
+ if (source_ts.tv_nsec > NS_PER_SEC / 2)
|
|
||||||
+ source_ts.tv_sec++;
|
|
||||||
+ source_ts.tv_nsec = 0;
|
|
||||||
+ source_tmv = timespec_to_tmv(source_ts);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
offset = tmv_to_nanoseconds(tmv_sub(ts, source_tmv));
|
|
||||||
|
|
||||||
if (c->no_adj) {
|
|
||||||
@@ -486,8 +508,15 @@ static void ts2phc_synchronize_clocks(struct ts2phc_private *priv, int autocfg)
|
|
||||||
adj = servo_sample(c->servo, offset, tmv_to_nanoseconds(ts),
|
|
||||||
SAMPLE_WEIGHT, &c->servo_state);
|
|
||||||
|
|
||||||
- pr_info("%s offset %10" PRId64 " s%d freq %+7.0f",
|
|
||||||
- c->name, offset, c->servo_state, adj);
|
|
||||||
+ if (holdover && c->servo_state != SERVO_LOCKED_STABLE) {
|
|
||||||
+ pr_info("%s lost holdover lock (offset %10" PRId64 ")",
|
|
||||||
+ c->name, offset);
|
|
||||||
+ continue;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ pr_info("%s offset %10" PRId64 " s%d freq %+7.0f%s",
|
|
||||||
+ c->name, offset, c->servo_state, adj,
|
|
||||||
+ holdover ? " holdover" : "");
|
|
||||||
|
|
||||||
switch (c->servo_state) {
|
|
||||||
case SERVO_UNLOCKED:
|
|
||||||
@@ -751,6 +780,9 @@ int main(int argc, char *argv[])
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ priv.holdover_length = config_get_int(cfg, NULL, "ts2phc.holdover");
|
|
||||||
+ priv.holdover_start = 0;
|
|
||||||
+
|
|
||||||
while (is_running()) {
|
|
||||||
struct ts2phc_clock *c;
|
|
||||||
|
|
||||||
diff --git a/ts2phc.h b/ts2phc.h
|
|
||||||
index 4833ded..5dbde9b 100644
|
|
||||||
--- a/ts2phc.h
|
|
||||||
+++ b/ts2phc.h
|
|
||||||
@@ -55,6 +55,8 @@ struct ts2phc_private {
|
|
||||||
bool state_changed;
|
|
||||||
LIST_HEAD(port_head, ts2phc_port) ports;
|
|
||||||
LIST_HEAD(clock_head, ts2phc_clock) clocks;
|
|
||||||
+ int holdover_length;
|
|
||||||
+ time_t holdover_start;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ts2phc_clock *ts2phc_clock_add(struct ts2phc_private *priv,
|
|
||||||
commit bf237fd55d4983a42d9344890dd861f18bea70ca
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu Jul 25 12:43:59 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Describe servo options in man page.
|
|
||||||
|
|
||||||
Copy and adapt missing servo options used by ts2phc from the ptp4l man
|
|
||||||
page.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc.8 b/ts2phc.8
|
|
||||||
index bcc6f61..4c54576 100644
|
|
||||||
--- a/ts2phc.8
|
|
||||||
+++ b/ts2phc.8
|
|
||||||
@@ -117,6 +117,16 @@ command line option.
|
|
||||||
|
|
||||||
.SH GLOBAL OPTIONS
|
|
||||||
|
|
||||||
+.TP
|
|
||||||
+.B clock_servo
|
|
||||||
+The servo which is used to synchronize the local clock. Valid values
|
|
||||||
+are "pi" for a PI controller, "linreg" for an adaptive controller
|
|
||||||
+using linear regression, "ntpshm" and "refclock_sock" for the NTP SHM and
|
|
||||||
+chrony SOCK reference clocks respectively to allow another process to
|
|
||||||
+synchronize the local clock, and "nullf" for a servo that always dials
|
|
||||||
+frequency offset zero (for use in SyncE nodes).
|
|
||||||
+The default is "pi".
|
|
||||||
+
|
|
||||||
.TP
|
|
||||||
.B first_step_threshold
|
|
||||||
The maximum offset, specified in seconds, that the servo will correct by
|
|
||||||
@@ -162,12 +172,64 @@ with the log level of the message as a number. The default is an empty string
|
|
||||||
argument).
|
|
||||||
|
|
||||||
.TP
|
|
||||||
-.B step_threshold
|
|
||||||
-The maximum offset, specified in seconds, that the servo will correct
|
|
||||||
-by changing the clock frequency instead of stepping the clock. When
|
|
||||||
-set to 0.0, the servo will never step the clock except on start.
|
|
||||||
+.B ntpshm_segment
|
|
||||||
+The number of the SHM segment used by ntpshm servo.
|
|
||||||
+The default is 0.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_integral_const
|
|
||||||
+The integral constant of the PI controller. When set to 0.0, the
|
|
||||||
+integral constant will be set by the following formula from the current
|
|
||||||
+sync interval.
|
|
||||||
+The default is 0.0.
|
|
||||||
+
|
|
||||||
+ki = min(ki_scale * sync^ki_exponent, ki_norm_max / sync)
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_integral_exponent
|
|
||||||
+The ki_exponent constant in the formula used to set the integral constant of
|
|
||||||
+the PI controller from the sync interval.
|
|
||||||
+The default is 0.4.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_integral_norm_max
|
|
||||||
+The ki_norm_max constant in the formula used to set the integral constant of
|
|
||||||
+the PI controller from the sync interval.
|
|
||||||
+The default is 0.3.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_integral_scale
|
|
||||||
+The ki_scale constant in the formula used to set the integral constant of
|
|
||||||
+the PI controller from the sync interval.
|
|
||||||
+The default is 0.3.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_proportional_const
|
|
||||||
+The proportional constant of the PI controller. When set to 0.0, the
|
|
||||||
+proportional constant will be set by the following formula from the current
|
|
||||||
+sync interval.
|
|
||||||
The default is 0.0.
|
|
||||||
|
|
||||||
+kp = min(kp_scale * sync^kp_exponent, kp_norm_max / sync)
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_proportional_exponent
|
|
||||||
+The kp_exponent constant in the formula used to set the proportional constant of
|
|
||||||
+the PI controller from the sync interval.
|
|
||||||
+The default is \-0.3.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_proportional_norm_max
|
|
||||||
+The kp_norm_max constant in the formula used to set the proportional constant of
|
|
||||||
+the PI controller from the sync interval.
|
|
||||||
+The default is 0.7
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B pi_proportional_scale
|
|
||||||
+The kp_scale constant in the formula used to set the proportional constant of
|
|
||||||
+the PI controller from the sync interval.
|
|
||||||
+The default is 0.7.
|
|
||||||
+
|
|
||||||
.TP
|
|
||||||
.B ts2phc.holdover
|
|
||||||
The holdover interval, specified in seconds. When the ToD information stops
|
|
||||||
@@ -178,6 +240,28 @@ 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 servo_num_offset_values
|
|
||||||
+The number of offset values considered in order to transition from the
|
|
||||||
+SERVO_LOCKED to the SERVO_LOCKED_STABLE state.
|
|
||||||
+The transition occurs once the last 'servo_num_offset_values' offsets
|
|
||||||
+are all below the 'servo_offset_threshold' value.
|
|
||||||
+The default value is 10.
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B servo_offset_threshold
|
|
||||||
+The offset threshold used in order to transition from the SERVO_LOCKED
|
|
||||||
+to the SERVO_LOCKED_STABLE state. The transition occurs once the
|
|
||||||
+last 'servo_num_offset_values' offsets are all below the threshold value.
|
|
||||||
+The default value of offset_threshold is 0 (disabled).
|
|
||||||
+
|
|
||||||
+.TP
|
|
||||||
+.B step_threshold
|
|
||||||
+The maximum offset, specified in seconds, that the servo will correct
|
|
||||||
+by changing the clock frequency instead of stepping the clock. When
|
|
||||||
+set to 0.0, the servo will never step the clock except on start.
|
|
||||||
+The default is 0.0.
|
|
||||||
+
|
|
||||||
.TP
|
|
||||||
.B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port
|
|
||||||
Specifies the remote host providing ToD information when using the
|
|
||||||
|
|
||||||
commit 7734d2fe8ac52afaa233262548615f79021ae6ee
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu Jul 25 12:46:43 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Fix description of holdover option in man page.
|
|
||||||
|
|
||||||
Fix the man page to explain that the LOCKED_STABLE servo state is
|
|
||||||
enabled by setting the servo_offset_threshold, not
|
|
||||||
servo_num_offset_values, which is already enabled by default.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc.8 b/ts2phc.8
|
|
||||||
index 4c54576..c0b718b 100644
|
|
||||||
--- a/ts2phc.8
|
|
||||||
+++ b/ts2phc.8
|
|
||||||
@@ -236,7 +236,7 @@ The holdover interval, specified in seconds. When the ToD information stops
|
|
||||||
working (e.g. GNSS receiver lost its fix), ts2phc is allowed for the specified
|
|
||||||
interval to continue synchronizing the target clock as long as the servo is in
|
|
||||||
the SERVO_LOCKED_STABLE state. The servo state needs be enabled by the
|
|
||||||
-\fBservo_num_offset_values\fP option. The holdover is not supported with the
|
|
||||||
+\fBservo_offset_threshold\fP option. The holdover is not supported with the
|
|
||||||
\fB-a\fP option and when \fBts2phc.extts_polarity\fP is set to \fIboth\fP.
|
|
||||||
The default is 0 (disabled).
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
|||||||
commit bbfaa1e253b889aeea97702bbbc87e731e0caf87
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu Feb 22 13:51:59 2024 +0100
|
|
||||||
|
|
||||||
lstab: Limit number of parsed leap seconds.
|
|
||||||
|
|
||||||
The lstab structure has a fixed-size array for leap seconds
|
|
||||||
(currently 28 + 200). Don't read more leap seconds from the leapfile to
|
|
||||||
avoid corrupting memory.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/lstab.c b/lstab.c
|
|
||||||
index 24add26..8e35504 100644
|
|
||||||
--- a/lstab.c
|
|
||||||
+++ b/lstab.c
|
|
||||||
@@ -137,7 +137,7 @@ static int lstab_read(struct lstab *lstab, const char *name)
|
|
||||||
fprintf(stderr, "failed to open '%s' for reading: %m\n", name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
- while (1) {
|
|
||||||
+ while (index < N_LEAPS) {
|
|
||||||
if (!fgets(buf, sizeof(buf), fp)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
commit 90ad2efc74b0f348fb6b417565b3ada7d161641b
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu Feb 22 13:56:53 2024 +0100
|
|
||||||
|
|
||||||
lstab: Don't free lstab on update.
|
|
||||||
|
|
||||||
The modification timestamp of the leapfile is checked with every
|
|
||||||
call of lstab_utc2tai(). If the file is modified, the provided lstab
|
|
||||||
structure is freed and a new one is allocated from the updated leapfile.
|
|
||||||
But the new lstab is not returned to the caller as the function doesn't
|
|
||||||
accept a pointer to the pointer to lstab. This causes reading from the
|
|
||||||
freed memory and leak of the newly allocated memory.
|
|
||||||
|
|
||||||
Modify update_leapsecond_table() to read the updated leapfile into the
|
|
||||||
existing lstab structure instead of the reallocation.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/lstab.c b/lstab.c
|
|
||||||
index 8e35504..357ed27 100644
|
|
||||||
--- a/lstab.c
|
|
||||||
+++ b/lstab.c
|
|
||||||
@@ -195,7 +195,6 @@ struct lstab *lstab_create(const char *filename)
|
|
||||||
|
|
||||||
int update_leapsecond_table(struct lstab *lstab)
|
|
||||||
{
|
|
||||||
- const char* leapfile;
|
|
||||||
struct stat statbuf;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
@@ -212,14 +211,14 @@ int update_leapsecond_table(struct lstab *lstab)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
printf("updating leap seconds file\n");
|
|
||||||
- leapfile = lstab->leapfile;
|
|
||||||
- lstab_destroy(lstab);
|
|
||||||
|
|
||||||
- lstab = lstab_create(leapfile);
|
|
||||||
- if (!lstab) {
|
|
||||||
+ if (lstab_read(lstab, lstab->leapfile)) {
|
|
||||||
+ lstab->length = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ lstab->lsfile_mtime = statbuf.st_mtim.tv_sec;
|
|
||||||
+
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,365 +0,0 @@
|
|||||||
commit 0c406008b530140ed6d992915a6c8a1e5abf15d5
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Thu Jan 25 11:26:15 2024 +0100
|
|
||||||
|
|
||||||
ts2phc: Don't switch system clock to nanosecond mode.
|
|
||||||
|
|
||||||
ts2phc is not synchronizing the system clock and should not switch the
|
|
||||||
clock to the nanosecond mode with adjtimex(modes=ADJ_NANO) or make any
|
|
||||||
other modifications to it. The process that is controlling the clock
|
|
||||||
(e.g. an NTP client) might not be using the nanosecond mode.
|
|
||||||
|
|
||||||
There are two instances of the adjtimex() call in the code. One is used
|
|
||||||
only to read the clock and can be replaced with faster clock_gettime().
|
|
||||||
The other instance is also reading the TAI offset. Instead of switching
|
|
||||||
to the nanosecond mode, change the timestamp conversion to handle both
|
|
||||||
microsecond and nanosecond modes according to the current clock status.
|
|
||||||
|
|
||||||
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc_generic_pps_source.c b/ts2phc_generic_pps_source.c
|
|
||||||
index d503aac..e6b8145 100644
|
|
||||||
--- a/ts2phc_generic_pps_source.c
|
|
||||||
+++ b/ts2phc_generic_pps_source.c
|
|
||||||
@@ -38,7 +38,6 @@ static int get_ntx(struct timex *ntx)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
memset(ntx, 0, sizeof(*ntx));
|
|
||||||
- ntx->modes = ADJ_NANO;
|
|
||||||
code = adjtimex(ntx);
|
|
||||||
if (code == -1) {
|
|
||||||
pr_err("adjtimex failed: %m");
|
|
||||||
@@ -93,7 +92,10 @@ static int ts2phc_generic_pps_source_getppstime(struct ts2phc_pps_source *src,
|
|
||||||
}
|
|
||||||
|
|
||||||
ts->tv_sec = ntx.time.tv_sec + tai_offset;
|
|
||||||
- ts->tv_nsec = ntx.time.tv_usec;
|
|
||||||
+ if (ntx.status & STA_NANO)
|
|
||||||
+ ts->tv_nsec = ntx.time.tv_usec;
|
|
||||||
+ else
|
|
||||||
+ ts->tv_nsec = ntx.time.tv_usec * 1000;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
|
|
||||||
index 3a4267d..5b2e06b 100644
|
|
||||||
--- a/ts2phc_nmea_pps_source.c
|
|
||||||
+++ b/ts2phc_nmea_pps_source.c
|
|
||||||
@@ -62,14 +62,13 @@ static int open_nmea_connection(const char *host, const char *port,
|
|
||||||
|
|
||||||
static void *monitor_nmea_status(void *arg)
|
|
||||||
{
|
|
||||||
+ struct timespec rxtime, rxtime_rt, tmo = { 2, 0 };
|
|
||||||
struct nmea_parser *np = nmea_parser_create();
|
|
||||||
struct pollfd pfd = { -1, POLLIN | POLLPRI };
|
|
||||||
char *host, input[256], *port, *ptr, *uart;
|
|
||||||
struct ts2phc_nmea_pps_source *s = arg;
|
|
||||||
- struct timespec rxtime, tmo = { 2, 0 };
|
|
||||||
int cnt, num, parsed, baud;
|
|
||||||
struct nmea_rmc rmc;
|
|
||||||
- struct timex ntx;
|
|
||||||
|
|
||||||
if (!np) {
|
|
||||||
pr_err("failed to create NMEA parser");
|
|
||||||
@@ -79,8 +78,6 @@ static void *monitor_nmea_status(void *arg)
|
|
||||||
port = config_get_string(s->config, NULL, "ts2phc.nmea_remote_port");
|
|
||||||
uart = config_get_string(s->config, NULL, "ts2phc.nmea_serialport");
|
|
||||||
baud = config_get_int(s->config, NULL, "ts2phc.nmea_baudrate");
|
|
||||||
- memset(&ntx, 0, sizeof(ntx));
|
|
||||||
- ntx.modes = ADJ_NANO;
|
|
||||||
|
|
||||||
while (is_running()) {
|
|
||||||
if (pfd.fd == -1) {
|
|
||||||
@@ -92,7 +89,7 @@ static void *monitor_nmea_status(void *arg)
|
|
||||||
}
|
|
||||||
num = poll(&pfd, 1, NMEA_TMO);
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &rxtime);
|
|
||||||
- adjtimex(&ntx);
|
|
||||||
+ clock_gettime(CLOCK_REALTIME, &rxtime_rt);
|
|
||||||
if (num < 0) {
|
|
||||||
pr_err("poll failed");
|
|
||||||
break;
|
|
||||||
@@ -124,8 +121,7 @@ static void *monitor_nmea_status(void *arg)
|
|
||||||
if (!nmea_parse(np, ptr, cnt, &rmc, &parsed)) {
|
|
||||||
pthread_mutex_lock(&s->mutex);
|
|
||||||
s->local_monotime = rxtime;
|
|
||||||
- s->local_utctime.tv_sec = ntx.time.tv_sec;
|
|
||||||
- s->local_utctime.tv_nsec = ntx.time.tv_usec;
|
|
||||||
+ s->local_utctime = rxtime_rt;
|
|
||||||
s->rmc_utctime = rmc.ts;
|
|
||||||
s->rmc_fix_valid = rmc.fix_valid;
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
|
|
||||||
commit 72b44bc885e519667a12c89d5b640484807e4946
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Tue Jun 4 08:57:15 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Use CLOCK_MONOTONIC_RAW for NMEA PPS timestamp.
|
|
||||||
|
|
||||||
In the calculation of the NMEA PPS timestamp is used an interval
|
|
||||||
measured by the CLOCK_MONOTONIC system clock. This clock may have a
|
|
||||||
large frequency error when another process (e.g. phc2sys or an NTP
|
|
||||||
client) is correcting a large time error by slewing.
|
|
||||||
|
|
||||||
This frequency error may cause the timestamp to overflow into the next
|
|
||||||
second and cause a one-second error in the measured offset, or the wrong
|
|
||||||
edge of the pulse to be rejected.
|
|
||||||
|
|
||||||
Switch from CLOCK_MONOTONIC to CLOCK_MONOTONIC_RAW to avoid the
|
|
||||||
impact of the system clock adjustments.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
|
|
||||||
index 5b2e06b..7a28433 100644
|
|
||||||
--- a/ts2phc_nmea_pps_source.c
|
|
||||||
+++ b/ts2phc_nmea_pps_source.c
|
|
||||||
@@ -88,7 +88,7 @@ static void *monitor_nmea_status(void *arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
num = poll(&pfd, 1, NMEA_TMO);
|
|
||||||
- clock_gettime(CLOCK_MONOTONIC, &rxtime);
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &rxtime);
|
|
||||||
clock_gettime(CLOCK_REALTIME, &rxtime_rt);
|
|
||||||
if (num < 0) {
|
|
||||||
pr_err("poll failed");
|
|
||||||
@@ -160,7 +160,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
|
|
||||||
int64_t utc_time;
|
|
||||||
bool fix_valid;
|
|
||||||
|
|
||||||
- clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
|
|
||||||
local_t2 = timespec_to_tmv(now);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&m->mutex);
|
|
||||||
|
|
||||||
commit 30e6c4dba892236d8cfe08dc6c55238e11504c71
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Mon Jun 3 10:32:05 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Provide source type.
|
|
||||||
|
|
||||||
Save the PPS source type in the instance and add a function to retrieve
|
|
||||||
it. This will be needed for NMEA-specific rounding of timestamps.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc_pps_source.c b/ts2phc_pps_source.c
|
|
||||||
index c333f65..ae2ad46 100644
|
|
||||||
--- a/ts2phc_pps_source.c
|
|
||||||
+++ b/ts2phc_pps_source.c
|
|
||||||
@@ -26,6 +26,8 @@ struct ts2phc_pps_source *ts2phc_pps_source_create(struct ts2phc_private *priv,
|
|
||||||
src = ts2phc_phc_pps_source_create(priv, dev);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
+ if (src)
|
|
||||||
+ src->type = type;
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -46,3 +48,8 @@ struct ts2phc_clock *ts2phc_pps_source_get_clock(struct ts2phc_pps_source *src)
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+enum ts2phc_pps_source_type ts2phc_pps_source_get_type(struct ts2phc_pps_source *src)
|
|
||||||
+{
|
|
||||||
+ return src->type;
|
|
||||||
+}
|
|
||||||
diff --git a/ts2phc_pps_source.h b/ts2phc_pps_source.h
|
|
||||||
index 293c693..c87e3af 100644
|
|
||||||
--- a/ts2phc_pps_source.h
|
|
||||||
+++ b/ts2phc_pps_source.h
|
|
||||||
@@ -53,4 +53,11 @@ int ts2phc_pps_source_getppstime(struct ts2phc_pps_source *src, struct timespec
|
|
||||||
|
|
||||||
struct ts2phc_clock *ts2phc_pps_source_get_clock(struct ts2phc_pps_source *src);
|
|
||||||
|
|
||||||
+/**
|
|
||||||
+ * Returns the type of the PPS source
|
|
||||||
+ * @param src Pointer to a source obtained via @ref ts2phc_pps_source_create().
|
|
||||||
+ * @return The type of the clock.
|
|
||||||
+ */
|
|
||||||
+enum ts2phc_pps_source_type ts2phc_pps_source_get_type(struct ts2phc_pps_source *src);
|
|
||||||
+
|
|
||||||
#endif
|
|
||||||
diff --git a/ts2phc_pps_source_private.h b/ts2phc_pps_source_private.h
|
|
||||||
index 99e6a78..ea6a8ad 100644
|
|
||||||
--- a/ts2phc_pps_source_private.h
|
|
||||||
+++ b/ts2phc_pps_source_private.h
|
|
||||||
@@ -13,6 +13,7 @@
|
|
||||||
#include "ts2phc_pps_source.h"
|
|
||||||
|
|
||||||
struct ts2phc_pps_source {
|
|
||||||
+ enum ts2phc_pps_source_type type;
|
|
||||||
void (*destroy)(struct ts2phc_pps_source *src);
|
|
||||||
int (*getppstime)(struct ts2phc_pps_source *src, struct timespec *ts);
|
|
||||||
struct ts2phc_clock *(*get_clock)(struct ts2phc_pps_source *src);
|
|
||||||
|
|
||||||
commit bebd15ae90dbfcb74a33e5b428f24c733abf1134
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Mon Jun 3 11:06:16 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Fix edge rejection for pulse widths over 0.5s.
|
|
||||||
|
|
||||||
If the configured pulse width is longer than 0.5 seconds, the calculated
|
|
||||||
range of ignored offsets is too small to cover the wrong edge. Fix the
|
|
||||||
calculation of the limits to use the minimum of pulsewidth and
|
|
||||||
(1.0s - pulsewidth). A pulsewidth of 0.5s should give the shortest interval.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
|
|
||||||
index 05ac225..ca9f721 100644
|
|
||||||
--- a/ts2phc_pps_sink.c
|
|
||||||
+++ b/ts2phc_pps_sink.c
|
|
||||||
@@ -175,6 +175,8 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri
|
|
||||||
sink->correction = nanoseconds_to_tmv(correction);
|
|
||||||
|
|
||||||
pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth");
|
|
||||||
+ if (pulsewidth > 500000000)
|
|
||||||
+ pulsewidth = 1000000000 - pulsewidth;
|
|
||||||
pulsewidth /= 2;
|
|
||||||
sink->ignore_upper = 1000000000 - pulsewidth;
|
|
||||||
sink->ignore_lower = pulsewidth;
|
|
||||||
|
|
||||||
commit 435e9fc6ebec8daa8ab9f88c2d590e35ace9b2f6
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Mon Jun 3 13:57:23 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Move upper/lower rejection limit calculation.
|
|
||||||
|
|
||||||
The ignore_upper and ignore_lower fields of the ts2phc_pps_sink struct
|
|
||||||
are calculated when the PPS source is not known yet. Replace the fields
|
|
||||||
with the configured pulsewidth and calculate the limits locally later
|
|
||||||
when needed in ts2phc_pps_sink_ignore(). This will allow an
|
|
||||||
NMEA-specific calculation of the limits.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
|
|
||||||
index ca9f721..8121afb 100644
|
|
||||||
--- a/ts2phc_pps_sink.c
|
|
||||||
+++ b/ts2phc_pps_sink.c
|
|
||||||
@@ -30,8 +30,7 @@ struct ts2phc_pps_sink {
|
|
||||||
struct ptp_pin_desc pin_desc;
|
|
||||||
unsigned int polarity;
|
|
||||||
tmv_t correction;
|
|
||||||
- uint32_t ignore_lower;
|
|
||||||
- uint32_t ignore_upper;
|
|
||||||
+ uint32_t pulsewidth;
|
|
||||||
struct ts2phc_clock *clock;
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -153,8 +152,8 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri
|
|
||||||
struct config *cfg = priv->cfg;
|
|
||||||
struct ptp_extts_request extts;
|
|
||||||
struct ts2phc_pps_sink *sink;
|
|
||||||
- int err, pulsewidth;
|
|
||||||
int32_t correction;
|
|
||||||
+ int err;
|
|
||||||
|
|
||||||
sink = calloc(1, sizeof(*sink));
|
|
||||||
if (!sink) {
|
|
||||||
@@ -174,12 +173,9 @@ static struct ts2phc_pps_sink *ts2phc_pps_sink_create(struct ts2phc_private *pri
|
|
||||||
correction = config_get_int(cfg, device, "ts2phc.extts_correction");
|
|
||||||
sink->correction = nanoseconds_to_tmv(correction);
|
|
||||||
|
|
||||||
- pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth");
|
|
||||||
- if (pulsewidth > 500000000)
|
|
||||||
- pulsewidth = 1000000000 - pulsewidth;
|
|
||||||
- pulsewidth /= 2;
|
|
||||||
- sink->ignore_upper = 1000000000 - pulsewidth;
|
|
||||||
- sink->ignore_lower = pulsewidth;
|
|
||||||
+ sink->pulsewidth = config_get_int(cfg, device, "ts2phc.pulsewidth");
|
|
||||||
+ if (sink->pulsewidth > 500000000)
|
|
||||||
+ sink->pulsewidth = 1000000000 - sink->pulsewidth;
|
|
||||||
|
|
||||||
sink->clock = ts2phc_clock_add(priv, device);
|
|
||||||
if (!sink->clock) {
|
|
||||||
@@ -243,12 +239,16 @@ static bool ts2phc_pps_sink_ignore(struct ts2phc_private *priv,
|
|
||||||
struct timespec source_ts)
|
|
||||||
{
|
|
||||||
tmv_t source_tmv = timespec_to_tmv(source_ts);
|
|
||||||
+ uint32_t ignore_lower, ignore_upper;
|
|
||||||
|
|
||||||
source_tmv = tmv_sub(source_tmv, priv->perout_phase);
|
|
||||||
source_ts = tmv_to_timespec(source_tmv);
|
|
||||||
|
|
||||||
- return source_ts.tv_nsec > sink->ignore_lower &&
|
|
||||||
- source_ts.tv_nsec < sink->ignore_upper;
|
|
||||||
+ ignore_upper = 1000000000 - sink->pulsewidth / 2;
|
|
||||||
+ ignore_lower = sink->pulsewidth / 2;
|
|
||||||
+
|
|
||||||
+ return source_ts.tv_nsec > ignore_lower &&
|
|
||||||
+ source_ts.tv_nsec < ignore_upper;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum extts_result ts2phc_pps_sink_event(struct ts2phc_private *priv,
|
|
||||||
|
|
||||||
commit 0257b245df1a32869f356c0cfbeacfe5f0a522f5
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Mon Jun 3 11:31:45 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Allow longer NMEA delays.
|
|
||||||
|
|
||||||
Timestamps from PPS sources are rounded to the nearest pulse, i.e. for
|
|
||||||
correct synchronization the offset between the source timestamp and
|
|
||||||
external PPS event timestamp needs to be between -0.5 and +0.5 seconds.
|
|
||||||
For the pulse edge rejection to work correctly it needs to be even
|
|
||||||
smaller, between -pulsewidth/2 and +pulsewidth/2.
|
|
||||||
|
|
||||||
With the NMEA PPS source the offset is the delay in the reception of the
|
|
||||||
RMC message. This message is not expected to come exactly on time with
|
|
||||||
the corresponding pulse. A positive delay is expected, typically at
|
|
||||||
least few tens of milliseconds, but some receivers have delays longer
|
|
||||||
than 0.5 seconds.
|
|
||||||
|
|
||||||
Add NMEA-specific rounding of the offset to not expect negative delays
|
|
||||||
and allow positive delays of up to 1 second, or whole pulsewidth if the
|
|
||||||
edge rejection is enabled.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc.c b/ts2phc.c
|
|
||||||
index 03c88b1..4817c85 100644
|
|
||||||
--- a/ts2phc.c
|
|
||||||
+++ b/ts2phc.c
|
|
||||||
@@ -418,9 +418,16 @@ static int ts2phc_pps_source_implicit_tstamp(struct ts2phc_private *priv,
|
|
||||||
* deduce the timestamp (actually only seconds part, nanoseconds are by
|
|
||||||
* construction zero) of this edge at the emitter based on the
|
|
||||||
* emitter's current time.
|
|
||||||
+ *
|
|
||||||
+ * With an NMEA source assume its messages always follow the pulse, i.e.
|
|
||||||
+ * assign the timestamp to the previous pulse instead of nearest pulse.
|
|
||||||
*/
|
|
||||||
- if (source_ts.tv_nsec > NS_PER_SEC / 2)
|
|
||||||
+ if (ts2phc_pps_source_get_type(priv->src) == TS2PHC_PPS_SOURCE_NMEA) {
|
|
||||||
source_ts.tv_sec++;
|
|
||||||
+ } else {
|
|
||||||
+ if (source_ts.tv_nsec > NS_PER_SEC / 2)
|
|
||||||
+ source_ts.tv_sec++;
|
|
||||||
+ }
|
|
||||||
source_ts.tv_nsec = 0;
|
|
||||||
|
|
||||||
tmv = timespec_to_tmv(source_ts);
|
|
||||||
diff --git a/ts2phc_pps_sink.c b/ts2phc_pps_sink.c
|
|
||||||
index 8121afb..0d399b8 100644
|
|
||||||
--- a/ts2phc_pps_sink.c
|
|
||||||
+++ b/ts2phc_pps_sink.c
|
|
||||||
@@ -244,8 +244,13 @@ static bool ts2phc_pps_sink_ignore(struct ts2phc_private *priv,
|
|
||||||
source_tmv = tmv_sub(source_tmv, priv->perout_phase);
|
|
||||||
source_ts = tmv_to_timespec(source_tmv);
|
|
||||||
|
|
||||||
- ignore_upper = 1000000000 - sink->pulsewidth / 2;
|
|
||||||
- ignore_lower = sink->pulsewidth / 2;
|
|
||||||
+ if (ts2phc_pps_source_get_type(priv->src) == TS2PHC_PPS_SOURCE_NMEA) {
|
|
||||||
+ ignore_upper = sink->pulsewidth;
|
|
||||||
+ ignore_lower = 0;
|
|
||||||
+ } else {
|
|
||||||
+ ignore_upper = 1000000000 - sink->pulsewidth / 2;
|
|
||||||
+ ignore_lower = sink->pulsewidth / 2;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
return source_ts.tv_nsec > ignore_lower &&
|
|
||||||
source_ts.tv_nsec < ignore_upper;
|
|
@ -1,83 +0,0 @@
|
|||||||
commit 0d508d8cfb96cc32d3516f1d0d3e80db49f00468
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Mon Jun 3 16:43:54 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Add option to correct for NMEA delay.
|
|
||||||
|
|
||||||
Add an option to specify the minimum expected delay of NMEA RMC messages
|
|
||||||
to correct timestamps returned by the NMEA PPS time source.
|
|
||||||
|
|
||||||
This enables operation with receivers that have delays outside of the
|
|
||||||
expected 0-1.0s interval, or 0-pulsewidth if the PPS edge rejection is
|
|
||||||
enabled.
|
|
||||||
|
|
||||||
[ RPC: Preserve alphabetical ordering in the ts2phc man page. ]
|
|
||||||
|
|
||||||
(Rebased to 4.2)
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Signed-off-by: Richard Cochran <richardcochran@gmail.com>
|
|
||||||
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
|
||||||
|
|
||||||
diff --git a/config.c b/config.c
|
|
||||||
index 58481db..d441d1e 100644
|
|
||||||
--- a/config.c
|
|
||||||
+++ b/config.c
|
|
||||||
@@ -347,6 +347,7 @@ struct config_item config_tab[] = {
|
|
||||||
PORT_ITEM_INT("ts2phc.holdover", 0, 0, INT_MAX),
|
|
||||||
PORT_ITEM_INT("ts2phc.master", 0, 0, 1),
|
|
||||||
PORT_ITEM_INT("ts2phc.nmea_baudrate", 9600, 300, INT_MAX),
|
|
||||||
+ PORT_ITEM_INT("ts2phc.nmea_delay", 0, INT_MIN, INT_MAX),
|
|
||||||
GLOB_ITEM_STR("ts2phc.nmea_remote_host", ""),
|
|
||||||
GLOB_ITEM_STR("ts2phc.nmea_remote_port", ""),
|
|
||||||
GLOB_ITEM_STR("ts2phc.nmea_serialport", "/dev/ttyS0"),
|
|
||||||
diff --git a/ts2phc.8 b/ts2phc.8
|
|
||||||
index c0b718b..b8383ff 100644
|
|
||||||
--- a/ts2phc.8
|
|
||||||
+++ b/ts2phc.8
|
|
||||||
@@ -262,6 +262,16 @@ by changing the clock frequency instead of stepping the clock. When
|
|
||||||
set to 0.0, the servo will never step the clock except on start.
|
|
||||||
The default is 0.0.
|
|
||||||
|
|
||||||
+.TP
|
|
||||||
+.B ts2phc.nmea_delay
|
|
||||||
+Specifies the minimum expected delay of NMEA RMC messages in nanoseconds.
|
|
||||||
+If the maximum delay is longer than 1 second, or 'ts2phc.pulsewidth'
|
|
||||||
+if 'ts2phc.extts_polarity' is set to "both", this option needs to be set
|
|
||||||
+accordingly to allow the timestamps from NMEA messages to be correctly
|
|
||||||
+assigned to pulses from the PPS signal and wrong PPS edges to be rejected if
|
|
||||||
+the edge rejection is enabled.
|
|
||||||
+The default is 0 nanoseconds.
|
|
||||||
+
|
|
||||||
.TP
|
|
||||||
.B ts2phc.nmea_remote_host, ts2phc.nmea_remote_port
|
|
||||||
Specifies the remote host providing ToD information when using the
|
|
||||||
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
|
|
||||||
index bdfaf19..e345969 100644
|
|
||||||
--- a/ts2phc_nmea_pps_source.c
|
|
||||||
+++ b/ts2phc_nmea_pps_source.c
|
|
||||||
@@ -33,6 +33,7 @@ struct ts2phc_nmea_pps_source {
|
|
||||||
pthread_t worker;
|
|
||||||
/* Protects anonymous struct fields, below, from concurrent access. */
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
+ tmv_t delay_correction;
|
|
||||||
struct {
|
|
||||||
struct timespec local_monotime;
|
|
||||||
struct timespec local_utctime;
|
|
||||||
@@ -207,6 +208,7 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
|
|
||||||
}
|
|
||||||
|
|
||||||
rmc = tmv_add(rmc, duration_since_rmc);
|
|
||||||
+ rmc = tmv_add(rmc, m->delay_correction);
|
|
||||||
*ts = tmv_to_timespec(rmc);
|
|
||||||
ts->tv_sec += tai_offset;
|
|
||||||
|
|
||||||
@@ -234,6 +236,8 @@ struct ts2phc_pps_source *ts2phc_nmea_pps_source_create(struct ts2phc_private *p
|
|
||||||
s->pps_source.destroy = ts2phc_nmea_pps_source_destroy;
|
|
||||||
s->pps_source.getppstime = ts2phc_nmea_pps_source_getppstime;
|
|
||||||
s->config = priv->cfg;
|
|
||||||
+ s->delay_correction = nanoseconds_to_tmv(
|
|
||||||
+ config_get_int(priv->cfg, NULL, "ts2phc.nmea_delay"));
|
|
||||||
pthread_mutex_init(&s->mutex, NULL);
|
|
||||||
err = pthread_create(&s->worker, NULL, monitor_nmea_status, s);
|
|
||||||
if (err) {
|
|
@ -1,76 +0,0 @@
|
|||||||
commit f3c742e24a40cf75272ec39789a2cba35389230d
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Tue Jun 11 15:25:54 2024 +0200
|
|
||||||
|
|
||||||
nmea: Fix conversion of leap second.
|
|
||||||
|
|
||||||
When a leap second is inserted, the RMC message reports time of
|
|
||||||
23:59:60, which overflows in mktime() to 0:00:00 after the leap second
|
|
||||||
with an incremented TAI-UTC offset. This causes a one-second error in
|
|
||||||
the offset meaured by ts2phc.
|
|
||||||
|
|
||||||
Check the seconds field of the RMC message and convert 60 to 59 to make
|
|
||||||
the timestamp ambiguous per is_utc_ambiguous() and ignored by ts2phc to
|
|
||||||
avoid updating the clock with the one-second error.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/nmea.c b/nmea.c
|
|
||||||
index 7f1d9a2..b84b42e 100644
|
|
||||||
--- a/nmea.c
|
|
||||||
+++ b/nmea.c
|
|
||||||
@@ -155,6 +155,9 @@ static int nmea_scan_rmc(struct nmea_parser *np, struct nmea_rmc *result)
|
|
||||||
if (cnt != 3) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
+ /* Convert an inserted leap second to ambiguous 23:59:59 */
|
|
||||||
+ if (tm.tm_sec == 60)
|
|
||||||
+ tm.tm_sec = 59;
|
|
||||||
tm.tm_year += 100;
|
|
||||||
tm.tm_mon--;
|
|
||||||
tm.tm_isdst = 0;
|
|
||||||
|
|
||||||
commit b396d361b0d290ce83395851860c2dcd074e0f3b
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Tue Jun 11 15:32:55 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Fix timestamp conversion for leap seconds.
|
|
||||||
|
|
||||||
The UTC timestamp parsed from the RMC message needs to be converted to
|
|
||||||
TAI in order to calculate the PHC offset. This conversion was done after
|
|
||||||
adjusting the timestamp for the measured delay between the reception of
|
|
||||||
the message and the following pulse, which caused the offset measured by
|
|
||||||
ts2phc to have a one-second error if the message expected during or
|
|
||||||
after a leap second was missed.
|
|
||||||
|
|
||||||
Apply the TAI-UTC offset to the timestamp parsed from the RMC message
|
|
||||||
before any adjustments are made to avoid the error.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/ts2phc_nmea_pps_source.c b/ts2phc_nmea_pps_source.c
|
|
||||||
index 7a28433..bdfaf19 100644
|
|
||||||
--- a/ts2phc_nmea_pps_source.c
|
|
||||||
+++ b/ts2phc_nmea_pps_source.c
|
|
||||||
@@ -186,10 +186,9 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
|
|
||||||
pr_err("nmea: rmc time stamp stale");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
- rmc = tmv_add(rmc, duration_since_rmc);
|
|
||||||
+
|
|
||||||
utc_time = tmv_to_nanoseconds(rmc);
|
|
||||||
utc_time /= (int64_t) 1000000000;
|
|
||||||
- *ts = tmv_to_timespec(rmc);
|
|
||||||
|
|
||||||
result = lstab_utc2tai(m->lstab, utc_time, &tai_offset);
|
|
||||||
switch (result) {
|
|
||||||
@@ -206,6 +205,9 @@ static int ts2phc_nmea_pps_source_getppstime(struct ts2phc_pps_source *src,
|
|
||||||
pr_err("nmea: utc time stamp is ambiguous");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ rmc = tmv_add(rmc, duration_since_rmc);
|
|
||||||
+ *ts = tmv_to_timespec(rmc);
|
|
||||||
ts->tv_sec += tai_offset;
|
|
||||||
|
|
||||||
return lstab_error;
|
|
@ -1,25 +0,0 @@
|
|||||||
commit b4ad9dccf23cef2d79621cd36987428a65f2bcc9
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Tue Jun 11 12:35:10 2024 +0200
|
|
||||||
|
|
||||||
ts2phc: Reset parser after RMC message.
|
|
||||||
|
|
||||||
When the receiver was configured to generate only RMC messages, every
|
|
||||||
other message was skipped due to the parser not being in the right
|
|
||||||
state to process another message. Reset the parser to correctly parse
|
|
||||||
every valid message.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/nmea.c b/nmea.c
|
|
||||||
index 44c7c01..7f1d9a2 100644
|
|
||||||
--- a/nmea.c
|
|
||||||
+++ b/nmea.c
|
|
||||||
@@ -171,6 +171,7 @@ int nmea_parse(struct nmea_parser *np, const char *ptr, int buflen,
|
|
||||||
while (buflen) {
|
|
||||||
if (!nmea_parse_symbol(np, *ptr)) {
|
|
||||||
if (!nmea_scan_rmc(np, result)) {
|
|
||||||
+ nmea_reset(np);
|
|
||||||
*parsed = count + 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,4 +1,81 @@
|
|||||||
commit e3867eb1ce2282f9ac4cc729a3b1beb74bb017d6
|
commit a3420abab9eec2dc7b35e0e1e9f37ebeffc87655
|
||||||
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Date: Tue Nov 26 15:10:32 2024 +0100
|
||||||
|
|
||||||
|
pmc_agent: Use longer update interval when not subscribed.
|
||||||
|
|
||||||
|
When phc2sys is started with the -w option, the pmc agent is not
|
||||||
|
subscribed to events by the pmc_agent_subscribe() function, which also
|
||||||
|
sets the update interval. The update interval in this case is zero,
|
||||||
|
which means the pmc agent is trying to update the currentUtcOffset value
|
||||||
|
on every call of pmc_agent_update(), i.e. on every clock update in
|
||||||
|
phc2sys.
|
||||||
|
|
||||||
|
Set a default update interval of 60 seconds to reduce the rate of
|
||||||
|
pmc requests.
|
||||||
|
|
||||||
|
Fixes: e3ca7ea90a9e ("pmc_agent: Make update interval configurable.")
|
||||||
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||||
|
|
||||||
|
diff --git a/pmc_agent.c b/pmc_agent.c
|
||||||
|
index d1a3367..663adc0 100644
|
||||||
|
--- a/pmc_agent.c
|
||||||
|
+++ b/pmc_agent.c
|
||||||
|
@@ -33,6 +33,9 @@
|
||||||
|
#define UPDATES_PER_SUBSCRIPTION 3
|
||||||
|
#define MIN_UPDATE_INTERVAL 10
|
||||||
|
|
||||||
|
+/* Update interval if the agent not subscribed, just polling the UTC offset */
|
||||||
|
+#define DEFAULT_UPDATE_INTERVAL 60
|
||||||
|
+
|
||||||
|
struct pmc_agent {
|
||||||
|
struct pmc *pmc;
|
||||||
|
uint64_t pmc_last_update;
|
||||||
|
@@ -253,6 +256,7 @@ int init_pmc_node(struct config *cfg, struct pmc_agent *node, const char *uds,
|
||||||
|
}
|
||||||
|
node->recv_subscribed = recv_subscribed;
|
||||||
|
node->recv_context = context;
|
||||||
|
+ node->update_interval = DEFAULT_UPDATE_INTERVAL * NS_PER_SEC;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
commit 03ab6793abf665039b25319f0f4ad7ce03452874
|
||||||
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Date: Tue Nov 26 15:10:33 2024 +0100
|
||||||
|
|
||||||
|
phc2sys: Don't disable pmc agent with -s -d -w options.
|
||||||
|
|
||||||
|
When phc2sys is started with -s and -d options to combine a PPS device
|
||||||
|
and PHC device as a time source, but without an offset specified by
|
||||||
|
the -O option, the pmc agent is disabled after waiting for ptp4l to have
|
||||||
|
a port in a synchronized state. This prevents phc2sys from following
|
||||||
|
changes in the currentUtcOffset value.
|
||||||
|
|
||||||
|
Disable the pmc agent only if no PHC device is specified by the -s
|
||||||
|
option, i.e. there are no PHC readings to which the UTC offset could be
|
||||||
|
applied.
|
||||||
|
|
||||||
|
Fixes: 5f1b419c4102 ("phc2sys: Replace magical test with a proper test.")
|
||||||
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||||
|
|
||||||
|
diff --git a/phc2sys.c b/phc2sys.c
|
||||||
|
index d09cb53..c98ecec 100644
|
||||||
|
--- a/phc2sys.c
|
||||||
|
+++ b/phc2sys.c
|
||||||
|
@@ -1594,7 +1594,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
|
if (domains[0].forced_sync_offset ||
|
||||||
|
!phc2sys_using_systemclock(&domains[0]) ||
|
||||||
|
- hardpps_configured(pps_fd)) {
|
||||||
|
+ (hardpps_configured(pps_fd) && !src_name)) {
|
||||||
|
pmc_agent_disable(domains[0].agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commit 1e25a62e65e07f2cc9e1c390895f0a940d307a25
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
Date: Tue Nov 26 15:10:34 2024 +0100
|
Date: Tue Nov 26 15:10:34 2024 +0100
|
||||||
|
|
||||||
@ -21,10 +98,10 @@ Date: Tue Nov 26 15:10:34 2024 +0100
|
|||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
|
||||||
diff --git a/phc2sys.8 b/phc2sys.8
|
diff --git a/phc2sys.8 b/phc2sys.8
|
||||||
index df0608a..5ca467d 100644
|
index 762a1b1..3246c8f 100644
|
||||||
--- a/phc2sys.8
|
--- a/phc2sys.8
|
||||||
+++ b/phc2sys.8
|
+++ b/phc2sys.8
|
||||||
@@ -161,7 +161,10 @@ minimize the error caused by random delays in scheduling and bus utilization.
|
@@ -164,7 +164,10 @@ minimize the error caused by random delays in scheduling and bus utilization.
|
||||||
The default is 5.
|
The default is 5.
|
||||||
.TP
|
.TP
|
||||||
.BI \-O " offset"
|
.BI \-O " offset"
|
||||||
@ -37,10 +114,10 @@ index df0608a..5ca467d 100644
|
|||||||
.B \-a
|
.B \-a
|
||||||
option. See
|
option. See
|
||||||
diff --git a/phc2sys.c b/phc2sys.c
|
diff --git a/phc2sys.c b/phc2sys.c
|
||||||
index 1745558..d8b0991 100644
|
index c98ecec..ddd0a6a 100644
|
||||||
--- a/phc2sys.c
|
--- a/phc2sys.c
|
||||||
+++ b/phc2sys.c
|
+++ b/phc2sys.c
|
||||||
@@ -1406,12 +1406,6 @@ int main(int argc, char *argv[])
|
@@ -1452,12 +1452,6 @@ int main(int argc, char *argv[])
|
||||||
goto bad_usage;
|
goto bad_usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +130,7 @@ index 1745558..d8b0991 100644
|
|||||||
if (hardpps_configured(pps_fd) && (dst_cnt != 1 ||
|
if (hardpps_configured(pps_fd) && (dst_cnt != 1 ||
|
||||||
strcmp(dst_names[0], "CLOCK_REALTIME"))) {
|
strcmp(dst_names[0], "CLOCK_REALTIME"))) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
@@ -1501,7 +1495,7 @@ int main(int argc, char *argv[])
|
@@ -1559,7 +1553,7 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
r = -1;
|
r = -1;
|
||||||
|
|
||||||
@ -62,7 +139,7 @@ index 1745558..d8b0991 100644
|
|||||||
snprintf(uds_local, sizeof(uds_local),
|
snprintf(uds_local, sizeof(uds_local),
|
||||||
"/var/run/phc2sys.%d", getpid());
|
"/var/run/phc2sys.%d", getpid());
|
||||||
|
|
||||||
@@ -1516,7 +1510,7 @@ int main(int argc, char *argv[])
|
@@ -1574,7 +1568,7 @@ int main(int argc, char *argv[])
|
||||||
phc2sys_recv_subscribed, &domains[0]))
|
phc2sys_recv_subscribed, &domains[0]))
|
||||||
goto end;
|
goto end;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
commit 6e480c9572925a4ea8aac45a10a306e0c4e509a9
|
commit daa82cda8318edd6a8e75ef46d0ed065445c8922
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
Date: Thu Jan 11 11:33:54 2024 +0100
|
Date: Thu Jan 11 11:33:54 2024 +0100
|
||||||
|
|
||||||
@ -7,33 +7,33 @@ Date: Thu Jan 11 11:33:54 2024 +0100
|
|||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
|
||||||
diff --git a/config.c b/config.c
|
diff --git a/config.c b/config.c
|
||||||
index 398b420..d7775c0 100644
|
index 4a28efb..cbff976 100644
|
||||||
--- a/config.c
|
--- a/config.c
|
||||||
+++ b/config.c
|
+++ b/config.c
|
||||||
@@ -319,7 +319,7 @@ struct config_item config_tab[] = {
|
@@ -347,7 +347,7 @@ struct config_item config_tab[] = {
|
||||||
GLOB_ITEM_STR("productDescription", ";;"),
|
PORT_ITEM_STR("ptp_dst_ipv4", "224.0.1.129"),
|
||||||
|
PORT_ITEM_STR("ptp_dst_ipv6", "FF0E:0:0:0:0:0:0:181"),
|
||||||
PORT_ITEM_STR("ptp_dst_mac", "01:1B:19:00:00:00"),
|
PORT_ITEM_STR("ptp_dst_mac", "01:1B:19:00:00:00"),
|
||||||
PORT_ITEM_STR("p2p_dst_mac", "01:80:C2:00:00:0E"),
|
|
||||||
- GLOB_ITEM_INT("ptp_minor_version", 1, 0, 1),
|
- GLOB_ITEM_INT("ptp_minor_version", 1, 0, 1),
|
||||||
+ GLOB_ITEM_INT("ptp_minor_version", 0, 0, 1),
|
+ GLOB_ITEM_INT("ptp_minor_version", 0, 0, 1),
|
||||||
GLOB_ITEM_STR("refclock_sock_address", "/var/run/refclock.ptp.sock"),
|
GLOB_ITEM_STR("refclock_sock_address", "/var/run/refclock.ptp.sock"),
|
||||||
GLOB_ITEM_STR("revisionData", ";;"),
|
GLOB_ITEM_STR("revisionData", ";;"),
|
||||||
GLOB_ITEM_INT("sanity_freq_limit", 200000000, 0, INT_MAX),
|
GLOB_ITEM_STR("sa_file", NULL),
|
||||||
diff --git a/configs/default.cfg b/configs/default.cfg
|
diff --git a/configs/default.cfg b/configs/default.cfg
|
||||||
index 0c7661c..8f94c16 100644
|
index c3ad618..768eef8 100644
|
||||||
--- a/configs/default.cfg
|
--- a/configs/default.cfg
|
||||||
+++ b/configs/default.cfg
|
+++ b/configs/default.cfg
|
||||||
@@ -46,7 +46,7 @@ power_profile.2011.networkTimeInaccuracy -1
|
@@ -47,7 +47,7 @@ power_profile.2011.networkTimeInaccuracy -1
|
||||||
power_profile.2017.totalTimeInaccuracy -1
|
power_profile.2017.totalTimeInaccuracy -1
|
||||||
power_profile.grandmasterID 0
|
power_profile.grandmasterID 0
|
||||||
power_profile.version none
|
power_profile.version none
|
||||||
-ptp_minor_version 1
|
-ptp_minor_version 1
|
||||||
+ptp_minor_version 0
|
+ptp_minor_version 0
|
||||||
#
|
spp -1
|
||||||
# Run time options
|
active_key_id 0
|
||||||
#
|
#
|
||||||
diff --git a/msg.h b/msg.h
|
diff --git a/msg.h b/msg.h
|
||||||
index 9c80f45..786ddc7 100644
|
index 58c2287..d2658c9 100644
|
||||||
--- a/msg.h
|
--- a/msg.h
|
||||||
+++ b/msg.h
|
+++ b/msg.h
|
||||||
@@ -32,7 +32,7 @@
|
@@ -32,7 +32,7 @@
|
||||||
@ -45,16 +45,44 @@ index 9c80f45..786ddc7 100644
|
|||||||
#define PTP_VERSION (PTP_MINOR_VERSION << 4 | PTP_MAJOR_VERSION)
|
#define PTP_VERSION (PTP_MINOR_VERSION << 4 | PTP_MAJOR_VERSION)
|
||||||
|
|
||||||
#define MAJOR_VERSION_MASK 0x0f
|
#define MAJOR_VERSION_MASK 0x0f
|
||||||
|
diff --git a/port.c b/port.c
|
||||||
|
index db35a44..7f945ac 100644
|
||||||
|
--- a/port.c
|
||||||
|
+++ b/port.c
|
||||||
|
@@ -3696,7 +3696,7 @@ struct port *port_open(const char *phc_device,
|
||||||
|
pr_err("%s: spp not supported on one-step ports", p->log_name);
|
||||||
|
goto err_uc_service;
|
||||||
|
}
|
||||||
|
- if (port_has_security(p) && (config_get_int(cfg, NULL, "ptp_minor_version") < 1)) {
|
||||||
|
+ if (port_has_security(p) && ptp_hdr_ver >> 4 < 1) {
|
||||||
|
pr_err("%s: spp needs at least PTPv2.1", p->log_name);
|
||||||
|
goto err_uc_service;
|
||||||
|
}
|
||||||
diff --git a/ptp4l.8 b/ptp4l.8
|
diff --git a/ptp4l.8 b/ptp4l.8
|
||||||
index 4cb9adb..c59b0b4 100644
|
index 87900e3..28cc4c9 100644
|
||||||
--- a/ptp4l.8
|
--- a/ptp4l.8
|
||||||
+++ b/ptp4l.8
|
+++ b/ptp4l.8
|
||||||
@@ -813,7 +813,7 @@ The default is 128.
|
@@ -900,7 +900,8 @@ The default is 128.
|
||||||
.TP
|
.TP
|
||||||
.B ptp_minor_version
|
.B ptp_minor_version
|
||||||
This option sets the minorVersionPTP in the common PTP message header.
|
This option sets the minorVersionPTP in the common PTP message header.
|
||||||
-The default is 1.
|
-The default is 1.
|
||||||
+The default (specific to the installed linuxptp package) is 0.
|
+The default (specific to the installed linuxptp package) is 0, but setting
|
||||||
|
+\fBsa_file\fR forces the version to 1.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.B refclock_sock_address
|
.B refclock_sock_address
|
||||||
|
diff --git a/ptp4l.c b/ptp4l.c
|
||||||
|
index ac2ef96..0d54d6c 100644
|
||||||
|
--- a/ptp4l.c
|
||||||
|
+++ b/ptp4l.c
|
||||||
|
@@ -194,6 +194,9 @@ int main(int argc, char *argv[])
|
||||||
|
sk_hwts_filter_mode = config_get_int(cfg, NULL, "hwts_filter");
|
||||||
|
|
||||||
|
ptp_hdr_ver = config_get_int(cfg, NULL, "ptp_minor_version");
|
||||||
|
+ /* Override patched default for spp, which requires PTPv2.1 */
|
||||||
|
+ if (ptp_hdr_ver < 1 && config_get_string(cfg, NULL, "sa_file"))
|
||||||
|
+ ptp_hdr_ver = 1;
|
||||||
|
ptp_hdr_ver = (ptp_hdr_ver << 4) | PTP_MAJOR_VERSION;
|
||||||
|
|
||||||
|
if (sad_create(cfg)) {
|
||||||
|
302
SOURCES/linuxptp-staticauto.patch
Normal file
302
SOURCES/linuxptp-staticauto.patch
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
commit f0ea5436b60494a8c5dac8d39e2b62dd8cab6f53
|
||||||
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Date: Thu Oct 17 15:05:21 2024 +0200
|
||||||
|
|
||||||
|
pmc: Avoid race conditions in agent update.
|
||||||
|
|
||||||
|
The pmc_agent_update() function updates the subscription to
|
||||||
|
notifications and also the current UTC offset. It uses a timeout of 0
|
||||||
|
to avoid blocking. When the pmc client sends the first request, the
|
||||||
|
response from ptp4l may not come quickly enough to be received in the
|
||||||
|
same run_pmc() call. It then sends the other request and checks for the
|
||||||
|
response. If it is the response to the first request, it will be ignored.
|
||||||
|
The update works correctly only if both responses are quick enough to be
|
||||||
|
received in the same call, or are both slow enough that they are
|
||||||
|
received in the next call of the pmc_agent_update() function.
|
||||||
|
|
||||||
|
The function needs to be called a random number of times in order to
|
||||||
|
finish one update. If the mismatch between requests and responses
|
||||||
|
happened consistently, the agent would never reach the up-to-date state
|
||||||
|
and phc2sys would not enter the main synchronization loop.
|
||||||
|
|
||||||
|
Split the update into two phases, where only one thing is updated at a
|
||||||
|
time. The function now needs to be called at most 3 times to update both
|
||||||
|
the subscription and UTC offset, assuming it is not interrupted by
|
||||||
|
another request outside of the agent's update.
|
||||||
|
|
||||||
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||||
|
|
||||||
|
diff --git a/pmc_agent.c b/pmc_agent.c
|
||||||
|
index 86b6ee6..d1a3367 100644
|
||||||
|
--- a/pmc_agent.c
|
||||||
|
+++ b/pmc_agent.c
|
||||||
|
@@ -37,6 +37,7 @@ struct pmc_agent {
|
||||||
|
struct pmc *pmc;
|
||||||
|
uint64_t pmc_last_update;
|
||||||
|
uint64_t update_interval;
|
||||||
|
+ int update_phase;
|
||||||
|
|
||||||
|
struct defaultDS dds;
|
||||||
|
bool dds_valid;
|
||||||
|
@@ -427,16 +428,27 @@ int pmc_agent_update(struct pmc_agent *node)
|
||||||
|
ts = tp.tv_sec * NS_PER_SEC + tp.tv_nsec;
|
||||||
|
|
||||||
|
if (ts - node->pmc_last_update >= node->update_interval) {
|
||||||
|
- if (node->stay_subscribed) {
|
||||||
|
- renew_subscription(node, 0);
|
||||||
|
- }
|
||||||
|
- if (!pmc_agent_query_utc_offset(node, 0)) {
|
||||||
|
+ switch (node->update_phase) {
|
||||||
|
+ case 0:
|
||||||
|
+ if (node->stay_subscribed &&
|
||||||
|
+ renew_subscription(node, 0))
|
||||||
|
+ break;
|
||||||
|
+ node->update_phase++;
|
||||||
|
+ /* Fall through */
|
||||||
|
+ case 1:
|
||||||
|
+ if (pmc_agent_query_utc_offset(node, 0))
|
||||||
|
+ break;
|
||||||
|
+ node->update_phase++;
|
||||||
|
+ /* Fall through */
|
||||||
|
+ default:
|
||||||
|
node->pmc_last_update = ts;
|
||||||
|
+ node->update_phase = 0;
|
||||||
|
+ break;
|
||||||
|
}
|
||||||
|
+ } else {
|
||||||
|
+ run_pmc(node, 0, -1, &msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
- run_pmc(node, 0, -1, &msg);
|
||||||
|
-
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
commit 2ceb8289b26e98f9f2179f021aa153db28c31dcf
|
||||||
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Date: Thu Oct 17 15:05:22 2024 +0200
|
||||||
|
|
||||||
|
phc2sys: Wait until pmc agent is subscribed.
|
||||||
|
|
||||||
|
When phc2sys is configured with multiple domains, different domains may
|
||||||
|
have their pmc agent subscribed after different number of calls of the
|
||||||
|
pmc_agent_update() function depending on how quickly responses from
|
||||||
|
ptp4l are received. If one domain triggers reconfiguration and the other
|
||||||
|
domain does not have its agent subscribed yet, it will not have any of
|
||||||
|
its clocks synchronized until a port changes state and triggers another
|
||||||
|
reconfiguration of the domain.
|
||||||
|
|
||||||
|
To avoid this problem, wait for each domain to have its agent subscribed
|
||||||
|
before entering the main synchronization loop. Use a 10ms update
|
||||||
|
interval to speed up the start of phc2sys.
|
||||||
|
|
||||||
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||||
|
|
||||||
|
diff --git a/phc2sys.c b/phc2sys.c
|
||||||
|
index 6113539..47e896e 100644
|
||||||
|
--- a/phc2sys.c
|
||||||
|
+++ b/phc2sys.c
|
||||||
|
@@ -962,6 +962,12 @@ static int auto_init_ports(struct domain *domain)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ while (!pmc_agent_is_subscribed(domain->agent)) {
|
||||||
|
+ usleep(10000);
|
||||||
|
+ if (pmc_agent_update(domain->agent) < 0)
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
for (i = 1; i <= number_ports; i++) {
|
||||||
|
err = pmc_agent_query_port_properties(domain->agent, 1000, i,
|
||||||
|
&state, ×tamping,
|
||||||
|
|
||||||
|
commit 5f37c1609d4a21daf7f7c12ae6f3fee327c03c3f
|
||||||
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Date: Thu Oct 24 15:24:07 2024 +0200
|
||||||
|
|
||||||
|
phc2sys: Keep clocks in command-line or ptp4l order.
|
||||||
|
|
||||||
|
When adding a new clock to the domain, add it to the end of the list to
|
||||||
|
keep them in the same order as specified on the command line or the port
|
||||||
|
order of ptp4l.
|
||||||
|
|
||||||
|
This will be needed by new code in the domain reconfiguration expecting
|
||||||
|
the order of reinitialized clocks to be the same as in which they were
|
||||||
|
added to the domain.
|
||||||
|
|
||||||
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
|
||||||
|
|
||||||
|
diff --git a/phc2sys.c b/phc2sys.c
|
||||||
|
index 47e896e..bf36c38 100644
|
||||||
|
--- a/phc2sys.c
|
||||||
|
+++ b/phc2sys.c
|
||||||
|
@@ -161,7 +161,7 @@ static struct servo *servo_add(struct domain *domain,
|
||||||
|
static struct clock *clock_add(struct domain *domain, const char *device,
|
||||||
|
int phc_index)
|
||||||
|
{
|
||||||
|
- struct clock *c;
|
||||||
|
+ struct clock *c, *c2;
|
||||||
|
clockid_t clkid = CLOCK_INVALID;
|
||||||
|
char phc_device[19];
|
||||||
|
|
||||||
|
@@ -217,7 +217,19 @@ static struct clock *clock_add(struct domain *domain, const char *device,
|
||||||
|
c->sysoff_method = sysoff_probe(CLOCKID_TO_FD(clkid),
|
||||||
|
domain->phc_readings);
|
||||||
|
|
||||||
|
- LIST_INSERT_HEAD(&domain->clocks, c, list);
|
||||||
|
+ /* Add the clock to the end of the list to keep them in the
|
||||||
|
+ command-line or ptp4l order */
|
||||||
|
+ if (LIST_EMPTY(&domain->clocks)) {
|
||||||
|
+ LIST_INSERT_HEAD(&domain->clocks, c, list);
|
||||||
|
+ } else {
|
||||||
|
+ LIST_FOREACH(c2, &domain->clocks, list) {
|
||||||
|
+ if (LIST_NEXT(c2, list))
|
||||||
|
+ continue;
|
||||||
|
+ LIST_INSERT_AFTER(c2, c, list);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
commit 11f06a29e6cb34104785435274e3673dd72970e0
|
||||||
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Date: Thu Oct 24 15:24:08 2024 +0200
|
||||||
|
|
||||||
|
phc2sys: Allow static sink clocks in automatic mode.
|
||||||
|
|
||||||
|
Allow the -c option to be used together with the -a option. Add the
|
||||||
|
specified clocks to the first domain, where they will stay in the master
|
||||||
|
state and be synchronized when a source is available in the same domain
|
||||||
|
or other domains.
|
||||||
|
|
||||||
|
Mark the clocks added by -c as static and skip them in the domain
|
||||||
|
reconfiguration if they duplicate a clock following a ptp4l port.
|
||||||
|
|
||||||
|
A use case is synchronization of clocks of backup interfaces in an
|
||||||
|
active-backup bond to minimize the observed offset when the active
|
||||||
|
interface is switched. ptp4l tracks the active interface, which is
|
||||||
|
followed by the automatic mode of phc2sys. All interfaces included in
|
||||||
|
the bond are specified by the -c option to keep them all synchronized to
|
||||||
|
the same source. The interface which duplicates the current active
|
||||||
|
interface provided by ptp4l is skipped.
|
||||||
|
|
||||||
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
|
||||||
|
diff --git a/phc2sys.8 b/phc2sys.8
|
||||||
|
index dd97a70..762a1b1 100644
|
||||||
|
--- a/phc2sys.8
|
||||||
|
+++ b/phc2sys.8
|
||||||
|
@@ -117,10 +117,13 @@ should no longer be used.
|
||||||
|
.TP
|
||||||
|
.BI \-c " device"
|
||||||
|
Specify the time sink by device (e.g. /dev/ptp1) or interface (e.g. eth1) or
|
||||||
|
-by name. The default is CLOCK_REALTIME (the system clock). Not compatible
|
||||||
|
-with the
|
||||||
|
+by name. If used together with the
|
||||||
|
.B \-a
|
||||||
|
-option. This option may be given up to 128 times.
|
||||||
|
+option, it is an additional sink clock added to the clocks provided by the
|
||||||
|
+first ptp4l instance (if
|
||||||
|
+.B \-z
|
||||||
|
+is specified multiple times). Duplicated clocks are allowed. The default is
|
||||||
|
+CLOCK_REALTIME (the system clock). This option may be given up to 128 times.
|
||||||
|
.TP
|
||||||
|
.BI \-E " servo"
|
||||||
|
Specify which clock servo should be used. Valid values are pi for a PI
|
||||||
|
diff --git a/phc2sys.c b/phc2sys.c
|
||||||
|
index bf36c38..d09cb53 100644
|
||||||
|
--- a/phc2sys.c
|
||||||
|
+++ b/phc2sys.c
|
||||||
|
@@ -79,6 +79,7 @@ struct clock {
|
||||||
|
int dest_only;
|
||||||
|
int state;
|
||||||
|
int new_state;
|
||||||
|
+ int static_state;
|
||||||
|
int sync_offset;
|
||||||
|
int leap_set;
|
||||||
|
int utc_offset_set;
|
||||||
|
@@ -391,6 +392,18 @@ static struct clock *find_dst_clock(struct domain *domain,
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static struct clock *find_nonstatic_clock(struct domain *domain,
|
||||||
|
+ int phc_index)
|
||||||
|
+{
|
||||||
|
+ struct clock *c = NULL;
|
||||||
|
+ LIST_FOREACH(c, &domain->clocks, list) {
|
||||||
|
+ if (!c->static_state && c->phc_index == phc_index) {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return c;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int reconfigure_domain(struct domain *domain)
|
||||||
|
{
|
||||||
|
struct clock *c, *src = NULL, *dup = NULL;
|
||||||
|
@@ -422,6 +435,17 @@ static int reconfigure_domain(struct domain *domain)
|
||||||
|
c->new_state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Ignore the clock if its state is not following ptp4l and has
|
||||||
|
+ the same PHC index as a clock that is following ptp4l */
|
||||||
|
+ if (c->static_state) {
|
||||||
|
+ dup = find_nonstatic_clock(domain, c->phc_index);
|
||||||
|
+ if (dup) {
|
||||||
|
+ pr_info("skipping static %s: %s has the same clock",
|
||||||
|
+ c->device, dup->device);
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
switch (c->state) {
|
||||||
|
case PS_FAULTY:
|
||||||
|
case PS_DISABLED:
|
||||||
|
@@ -436,6 +460,8 @@ static int reconfigure_domain(struct domain *domain)
|
||||||
|
dst_cnt++;
|
||||||
|
LIST_INSERT_HEAD(&domain->dst_clocks,
|
||||||
|
c, dst_list);
|
||||||
|
+ if (c->sanity_check)
|
||||||
|
+ clockcheck_reset(c->sanity_check);
|
||||||
|
} else {
|
||||||
|
pr_info("skipping %s: %s has the same clock "
|
||||||
|
"and is already selected",
|
||||||
|
@@ -1128,6 +1154,7 @@ static int phc2sys_static_dst_configuration(struct domain *domain,
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
dst->state = PS_MASTER;
|
||||||
|
+ dst->static_state = 1;
|
||||||
|
LIST_INSERT_HEAD(&domain->dst_clocks, dst, dst_list);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
@@ -1407,7 +1434,7 @@ int main(int argc, char *argv[])
|
||||||
|
dst_names[dst_cnt++] = "CLOCK_REALTIME";
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (autocfg && (src_name || dst_cnt > 0 || hardpps_configured(pps_fd) ||
|
||||||
|
+ if (autocfg && (src_name || hardpps_configured(pps_fd) ||
|
||||||
|
wait_sync || settings.forced_sync_offset)) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"autoconfiguration cannot be mixed with manual config options.\n");
|
||||||
|
@@ -1506,6 +1533,14 @@ int main(int argc, char *argv[])
|
||||||
|
if (auto_init_ports(&domains[i]) < 0)
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < dst_cnt; i++) {
|
||||||
|
+ r = phc2sys_static_dst_configuration(&domains[0],
|
||||||
|
+ dst_names[i]);
|
||||||
|
+ if (r)
|
||||||
|
+ goto end;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
r = do_loop(domains, n_domains);
|
||||||
|
goto end;
|
||||||
|
}
|
@ -1,26 +0,0 @@
|
|||||||
commit b421a4c66ce636adff150dd1aa8eafa981c2693d
|
|
||||||
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
Date: Wed Jan 3 14:45:51 2024 +0100
|
|
||||||
|
|
||||||
pmc: Allow missing values in SUBSCRIBE_EVENTS_NP command.
|
|
||||||
|
|
||||||
Don't require all supported notifications to be specified in the command
|
|
||||||
for compatibility with older scripts.
|
|
||||||
|
|
||||||
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
|
||||||
|
|
||||||
diff --git a/pmc_common.c b/pmc_common.c
|
|
||||||
index 62e34a6..b88cfc2 100644
|
|
||||||
--- a/pmc_common.c
|
|
||||||
+++ b/pmc_common.c
|
|
||||||
@@ -310,8 +310,8 @@ static void do_set_action(struct pmc *pmc, int action, int index, char *str)
|
|
||||||
onoff_port_state,
|
|
||||||
onoff_time_status,
|
|
||||||
onoff_parent_data_set);
|
|
||||||
- if (cnt != 4) {
|
|
||||||
- fprintf(stderr, "%s SET needs 4 values\n",
|
|
||||||
+ if (cnt < 2) {
|
|
||||||
+ fprintf(stderr, "%s SET needs at least 2 values\n",
|
|
||||||
idtab[index].name);
|
|
||||||
break;
|
|
||||||
}
|
|
85
SOURCES/linuxptp-udpaddr.patch
Normal file
85
SOURCES/linuxptp-udpaddr.patch
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
commit 77504ea63484dbc0c78b1ef58b29f06ced517223
|
||||||
|
Author: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
Date: Wed Sep 25 14:16:16 2024 +0200
|
||||||
|
|
||||||
|
udp: Fix port-specific ptp/p2p_dst_ipv4 configuration.
|
||||||
|
|
||||||
|
If different ports are configured with a different ptp_dst_ipv4 or
|
||||||
|
p2p_dst_ipv4 address, only the last port in the configuration works
|
||||||
|
correctly. This is caused by a global variable holding the
|
||||||
|
destination address for all ports using the udp transport.
|
||||||
|
|
||||||
|
Move the address to the udp structure to avoid the conflict between
|
||||||
|
different ports, same as when port-specific scope in udp6 was fixed
|
||||||
|
in commit a48666bee3dd ("udp6: Make mc6_addr transport-local").
|
||||||
|
|
||||||
|
Fixes: 8a26c94cc88e ("udp+udp6: Make IP addresses configurable.")
|
||||||
|
Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
|
||||||
|
|
||||||
|
diff --git a/udp.c b/udp.c
|
||||||
|
index 38d0ec4..c9b5f39 100644
|
||||||
|
--- a/udp.c
|
||||||
|
+++ b/udp.c
|
||||||
|
@@ -44,6 +44,7 @@ struct udp {
|
||||||
|
struct transport t;
|
||||||
|
struct address ip;
|
||||||
|
struct address mac;
|
||||||
|
+ struct in_addr mcast_addr[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mcast_bind(int fd, int index)
|
||||||
|
@@ -146,8 +147,6 @@ no_socket:
|
||||||
|
|
||||||
|
enum { MC_PRIMARY, MC_PDELAY };
|
||||||
|
|
||||||
|
-static struct in_addr mcast_addr[2];
|
||||||
|
-
|
||||||
|
static int udp_open(struct transport *t, struct interface *iface,
|
||||||
|
struct fdarray *fda, enum timestamp_type ts_type)
|
||||||
|
{
|
||||||
|
@@ -165,22 +164,22 @@ static int udp_open(struct transport *t, struct interface *iface,
|
||||||
|
sk_interface_addr(name, AF_INET, &udp->ip);
|
||||||
|
|
||||||
|
str = config_get_string(t->cfg, name, "ptp_dst_ipv4");
|
||||||
|
- if (!inet_aton(str, &mcast_addr[MC_PRIMARY])) {
|
||||||
|
+ if (!inet_aton(str, &udp->mcast_addr[MC_PRIMARY])) {
|
||||||
|
pr_err("invalid ptp_dst_ipv4 %s", str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = config_get_string(t->cfg, name, "p2p_dst_ipv4");
|
||||||
|
- if (!inet_aton(str, &mcast_addr[MC_PDELAY])) {
|
||||||
|
+ if (!inet_aton(str, &udp->mcast_addr[MC_PDELAY])) {
|
||||||
|
pr_err("invalid p2p_dst_ipv4 %s", str);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
- efd = open_socket(name, mcast_addr, EVENT_PORT, ttl);
|
||||||
|
+ efd = open_socket(name, udp->mcast_addr, EVENT_PORT, ttl);
|
||||||
|
if (efd < 0)
|
||||||
|
goto no_event;
|
||||||
|
|
||||||
|
- gfd = open_socket(name, mcast_addr, GENERAL_PORT, ttl);
|
||||||
|
+ gfd = open_socket(name, udp->mcast_addr, GENERAL_PORT, ttl);
|
||||||
|
if (gfd < 0)
|
||||||
|
goto no_general;
|
||||||
|
|
||||||
|
@@ -223,6 +222,7 @@ static int udp_send(struct transport *t, struct fdarray *fda,
|
||||||
|
enum transport_event event, int peer, void *buf, int len,
|
||||||
|
struct address *addr, struct hw_timestamp *hwts)
|
||||||
|
{
|
||||||
|
+ struct udp *udp = container_of(t, struct udp, t);
|
||||||
|
struct address addr_buf;
|
||||||
|
unsigned char junk[1600];
|
||||||
|
ssize_t cnt;
|
||||||
|
@@ -243,8 +243,8 @@ static int udp_send(struct transport *t, struct fdarray *fda,
|
||||||
|
if (!addr) {
|
||||||
|
memset(&addr_buf, 0, sizeof(addr_buf));
|
||||||
|
addr_buf.sin.sin_family = AF_INET;
|
||||||
|
- addr_buf.sin.sin_addr = peer ? mcast_addr[MC_PDELAY] :
|
||||||
|
- mcast_addr[MC_PRIMARY];
|
||||||
|
+ addr_buf.sin.sin_addr = peer ? udp->mcast_addr[MC_PDELAY] :
|
||||||
|
+ udp->mcast_addr[MC_PRIMARY];
|
||||||
|
addr_buf.len = sizeof(addr_buf.sin);
|
||||||
|
addr = &addr_buf;
|
||||||
|
}
|
@ -1,16 +1,16 @@
|
|||||||
%global _hardened_build 1
|
%global _hardened_build 1
|
||||||
%global testsuite_ver bf8ead
|
%global testsuite_ver d27dbd
|
||||||
%global clknetsim_ver 5d1dc0
|
%global clknetsim_ver 64df92
|
||||||
|
|
||||||
Name: linuxptp
|
Name: linuxptp
|
||||||
Version: 4.2
|
Version: 4.4
|
||||||
Release: 3%{?dist}.1
|
Release: 1%{?dist}.2
|
||||||
Summary: PTP implementation for Linux
|
Summary: PTP implementation for Linux
|
||||||
|
|
||||||
License: GPLv2+
|
License: GPL-2.0-or-later
|
||||||
URL: http://linuxptp.sourceforge.net/
|
URL: https://www.linuxptp.org/
|
||||||
|
|
||||||
Source0: https://downloads.sourceforge.net/%{name}/%{name}-%{version}.tgz
|
Source0: https://downloads.nwtime.org/%{name}/%{name}-%{version}.tgz
|
||||||
Source1: phc2sys.service
|
Source1: phc2sys.service
|
||||||
Source2: ptp4l.service
|
Source2: ptp4l.service
|
||||||
Source3: timemaster.service
|
Source3: timemaster.service
|
||||||
@ -21,34 +21,26 @@ Source10: https://github.com/mlichvar/linuxptp-testsuite/archive/%{testsuite_ver
|
|||||||
# simulator for test suite
|
# simulator for test suite
|
||||||
Source11: https://github.com/mlichvar/clknetsim/archive/%{clknetsim_ver}/clknetsim-%{clknetsim_ver}.tar.gz
|
Source11: https://github.com/mlichvar/clknetsim/archive/%{clknetsim_ver}/clknetsim-%{clknetsim_ver}.tar.gz
|
||||||
|
|
||||||
# allow old syntax of SET SUBSCRIBE_EVENTS_NP command
|
|
||||||
Patch1: linuxptp-subscribe.patch
|
|
||||||
# disable warning messages about deprecated options
|
# disable warning messages about deprecated options
|
||||||
Patch2: linuxptp-deprecated.patch
|
Patch2: linuxptp-deprecated.patch
|
||||||
# revert default PTP version to 2.0 for better compatibility
|
# revert default PTP version to 2.0 for better compatibility
|
||||||
Patch3: linuxptp-ptpver.patch
|
Patch3: linuxptp-ptpver.patch
|
||||||
# limit unicast message rate per address and grant duration
|
# limit unicast message rate per address and grant duration
|
||||||
Patch4: linuxptp-ucastrate.patch
|
Patch4: linuxptp-ucastrate.patch
|
||||||
# fix ts2phc to handle large NMEA delay
|
# fix port-specific ptp/p2p_dst_ipv4 configuration
|
||||||
Patch5: linuxptp-nmeadelay.patch
|
Patch5: linuxptp-udpaddr.patch
|
||||||
# fix loading and reloading of leapfile
|
# support static sink clocks in phc2sys automatic mode
|
||||||
Patch6: linuxptp-lstab.patch
|
Patch6: linuxptp-staticauto.patch
|
||||||
# fix ts2phc to correctly handle leap seconds
|
# don't require -O option without -a and -w in phc2sys
|
||||||
Patch7: linuxptp-nmealeap.patch
|
Patch7: linuxptp-nowait.patch
|
||||||
# fix ts2phc to reset NMEA parser after RMC message
|
# add experimental option for external PPS in ts2phc automatic mode
|
||||||
Patch8: linuxptp-nmeareset.patch
|
Patch8: linuxptp-externalpps.patch
|
||||||
# add options to configure multicast IP addresses
|
# add command to set external grandmaster properties
|
||||||
Patch9: linuxptp-addropts.patch
|
Patch9: linuxptp-externalgm.patch
|
||||||
# add holdover support to ts2phc
|
|
||||||
Patch10: linuxptp-holdover.patch
|
|
||||||
# add option to ts2phc to specify minimum expected NMEA delay
|
|
||||||
Patch11: linuxptp-nmeadelay2.patch
|
|
||||||
# check for EL-specific kernels with vclock support
|
# check for EL-specific kernels with vclock support
|
||||||
Patch12: linuxptp-vclock.patch
|
Patch12: linuxptp-vclock.patch
|
||||||
# don't require -O option without -a and -w in phc2sys
|
|
||||||
Patch13: linuxptp-nowait.patch
|
|
||||||
|
|
||||||
BuildRequires: gcc gcc-c++ make systemd
|
BuildRequires: gcc gcc-c++ gnutls-devel make systemd
|
||||||
|
|
||||||
%{?systemd_requires}
|
%{?systemd_requires}
|
||||||
|
|
||||||
@ -62,6 +54,10 @@ Supporting legacy APIs and other platforms is not a goal.
|
|||||||
%prep
|
%prep
|
||||||
%setup -q -a 10 -a 11 -n %{name}-%{!?gitfullver:%{version}}%{?gitfullver}
|
%setup -q -a 10 -a 11 -n %{name}-%{!?gitfullver:%{version}}%{?gitfullver}
|
||||||
%autopatch -p1
|
%autopatch -p1
|
||||||
|
|
||||||
|
# disable nettle support in favor of gnutls
|
||||||
|
sed -i 's|find .*"nettle"|true|' incdefs.sh
|
||||||
|
|
||||||
mv linuxptp-testsuite-%{testsuite_ver}* testsuite
|
mv linuxptp-testsuite-%{testsuite_ver}* testsuite
|
||||||
mv clknetsim-%{clknetsim_ver}* testsuite/clknetsim
|
mv clknetsim-%{clknetsim_ver}* testsuite/clknetsim
|
||||||
|
|
||||||
@ -125,9 +121,18 @@ PATH=..:$PATH ./run
|
|||||||
%{_mandir}/man8/*.8*
|
%{_mandir}/man8/*.8*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Thu Jan 02 2025 Miroslav Lichvar <mlichvar@redhat.com> 4.2-3.el9_5.1
|
* Wed May 14 2025 Miroslav Lichvar <mlichvar@redhat.com> 4.4-1.el9_6.2
|
||||||
- add option to ts2phc to specify minimum expected NMEA delay (RHEL-70168)
|
- add command to set external grandmaster properties (RHEL-91297)
|
||||||
- don't require -O option without -a and -w in phc2sys (RHEL-70678)
|
|
||||||
|
* Tue May 06 2025 Miroslav Lichvar <mlichvar@redhat.com> 4.4-1.el9_6.1
|
||||||
|
- add experimental option for external PPS in ts2phc automatic mode
|
||||||
|
(RHEL-89604)
|
||||||
|
|
||||||
|
* 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)
|
||||||
|
- support static sink clocks in phc2sys automatic mode (RHEL-62864)
|
||||||
|
- don't require -O option without -a and -w in phc2sys (RHEL-69138)
|
||||||
|
|
||||||
* Thu Jul 25 2024 Miroslav Lichvar <mlichvar@redhat.com> 4.2-3
|
* Thu Jul 25 2024 Miroslav Lichvar <mlichvar@redhat.com> 4.2-3
|
||||||
- rework NMEA delay patch to fix PPS edge rejection (RHEL-39387)
|
- rework NMEA delay patch to fix PPS edge rejection (RHEL-39387)
|
||||||
|
Loading…
Reference in New Issue
Block a user