If a given port selects the COMMON_P2P delay mechanism, let it open a local PTP Management channel to the Common Mean Link Delay Service over a UNIX domain socket.
Signed-off-by: Richard Cochran <richardcoch...@gmail.com> --- config.c | 6 ++ configs/default.cfg | 5 ++ dm.h | 3 + fd.h | 1 + makefile | 4 +- port.c | 181 +++++++++++++++++++++++++++++++++++++++++--- port_private.h | 7 ++ ptp4l.8 | 39 +++++++++- 8 files changed, 233 insertions(+), 13 deletions(-) diff --git a/config.c b/config.c index fe65b76..8cf1620 100644 --- a/config.c +++ b/config.c @@ -171,6 +171,7 @@ static struct config_enum delay_filter_enu[] = { static struct config_enum delay_mech_enu[] = { { "Auto", DM_AUTO }, + { "COMMON_P2P", DM_COMMON_P2P }, { "E2E", DM_E2E }, { "P2P", DM_P2P }, { "NONE", DM_NO_MECHANISM }, @@ -249,6 +250,11 @@ struct config_item config_tab[] = { GLOB_ITEM_INT("clock_class_threshold", CLOCK_CLASS_THRESHOLD_DEFAULT, 6, CLOCK_CLASS_THRESHOLD_DEFAULT), GLOB_ITEM_ENU("clock_servo", CLOCK_SERVO_PI, clock_servo_enu), GLOB_ITEM_ENU("clock_type", CLOCK_TYPE_ORDINARY, clock_type_enu), + PORT_ITEM_STR("cmlds.client_address", "/var/run/cmlds_cleint"), + PORT_ITEM_INT("cmlds.domainNumber", 0, 0, 255), + PORT_ITEM_INT("cmlds.majorSdoId", 2, 0, 0x0F), + PORT_ITEM_INT("cmlds.port", 0, 0, UINT16_MAX), + PORT_ITEM_STR("cmlds.server_address", "/var/run/cmlds_server"), GLOB_ITEM_ENU("dataset_comparison", DS_CMP_IEEE1588, dataset_comp_enu), PORT_ITEM_INT("delayAsymmetry", 0, INT_MIN, INT_MAX), PORT_ITEM_ENU("delay_filter", FILTER_MOVING_MEDIAN, delay_filter_enu), diff --git a/configs/default.cfg b/configs/default.cfg index 0c7661c..29a34fd 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -93,6 +93,11 @@ write_phase_mode 0 # # Transport options # +cmlds.client_address /var/run/cmlds_cleint +cmlds.domainNumber 0 +cmlds.majorSdoId 2 +cmlds.port 0 +cmlds.server_address /var/run/cmlds_server transportSpecific 0x0 ptp_dst_mac 01:1B:19:00:00:00 p2p_dst_mac 01:80:C2:00:00:0E diff --git a/dm.h b/dm.h index 47bd847..80d1ce5 100644 --- a/dm.h +++ b/dm.h @@ -34,6 +34,9 @@ enum delay_mechanism { /** Peer delay mechanism. */ DM_P2P, + /** Peer delay as measured by CMLDS. */ + DM_COMMON_P2P, + /** No Delay Mechanism. */ DM_NO_MECHANISM = 0xFE, }; diff --git a/fd.h b/fd.h index 16420d7..9a072ab 100644 --- a/fd.h +++ b/fd.h @@ -39,6 +39,7 @@ enum { FD_SYNC_TX_TIMER, FD_UNICAST_REQ_TIMER, FD_UNICAST_SRV_TIMER, + FD_CMLDS, FD_RTNL, N_POLLFD, }; diff --git a/makefile b/makefile index 7fc5f6f..e9c1ccc 100644 --- a/makefile +++ b/makefile @@ -30,8 +30,8 @@ TS2PHC = ts2phc.o lstab.o nmea.o serial.o sock.o ts2phc_generic_pps_source.o \ ts2phc_nmea_pps_source.o ts2phc_phc_pps_source.o ts2phc_pps_sink.o ts2phc_pps_source.o OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.o \ e2e_tc.o fault.o $(FILTERS) fsm.o hash.o interface.o monitor.o msg.o phc.o \ - port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o $(SERVOS) \ - sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \ + pmc_common.o port.o port_signaling.o pqueue.o print.o ptp4l.o p2p_tc.o rtnl.o \ + $(SERVOS) sk.o stats.o tc.o $(TRANSP) telecom.o tlv.o tsproc.o unicast_client.o \ unicast_fsm.o unicast_service.o util.o version.o OBJECTS = $(OBJ) hwstamp_ctl.o nsm.o phc2sys.o phc_ctl.o pmc.o pmc_agent.o \ diff --git a/port.c b/port.c index 23f021c..e2ebaeb 100644 --- a/port.c +++ b/port.c @@ -46,6 +46,8 @@ #include "util.h" #define ANNOUNCE_SPAN 1 +#define CMLDS_SUBSCRIPTION_INTERVAL 60 /*seconds*/ +#define CMLDS_UPDATE_INTERVAL (CMLDS_SUBSCRIPTION_INTERVAL / 2) enum syfu_event { SYNC_MISMATCH, @@ -965,7 +967,8 @@ static int port_management_fill_response(struct port *target, ptp_text_copy(cd->userDescription, &desc->userDescription); buf += sizeof(struct PTPText) + cd->userDescription->length; - if (target->delayMechanism == DM_P2P) { + if (target->delayMechanism == DM_P2P || + target->delayMechanism == DM_COMMON_P2P) { memcpy(buf, profile_id_p2p, PROFILE_ID_LEN); } else { struct config *cfg = clock_config(target->clock); @@ -1275,14 +1278,16 @@ int port_set_delay_tmo(struct port *p) if (p->inhibit_delay_req) { return 0; } - - if (p->delayMechanism == DM_P2P) { + switch (p->delayMechanism) { + case DM_COMMON_P2P: + case DM_P2P: return set_tmo_log(p->fda.fd[FD_DELAY_TIMER], 1, - p->logPdelayReqInterval); - } else { - return set_tmo_random(p->fda.fd[FD_DELAY_TIMER], 0, 2, - p->logMinDelayReqInterval); + p->logPdelayReqInterval); + default: + break; } + return set_tmo_random(p->fda.fd[FD_DELAY_TIMER], 0, 2, + p->logMinDelayReqInterval); } static int port_set_manno_tmo(struct port *p) @@ -1528,6 +1533,49 @@ static void port_syfufsm(struct port *p, enum syfu_event event, } } +static int port_cmlds_renew(struct port *p, time_t now) +{ + struct subscribe_events_np sen = { + .duration = CMLDS_SUBSCRIPTION_INTERVAL, + }; + int err; + + event_bitmask_set(sen.bitmask, NOTIFY_CMLDS, TRUE); + err = pmc_send_set_action(p->cmlds.pmc, MID_SUBSCRIBE_EVENTS_NP, + &sen, sizeof(sen)); + if (err) { + return err; + } + p->cmlds.last_renewal = now; + return 0; +} + +static enum fsm_event port_cmlds_timeout(struct port *p) +{ + struct timespec now; + int err; + + if (!p->cmlds.pmc) { + return EV_NONE; + } + clock_gettime(CLOCK_MONOTONIC, &now); + if (now.tv_sec - p->cmlds.last_renewal > CMLDS_UPDATE_INTERVAL) { + err = port_cmlds_renew(p, now.tv_sec); + if (err) { + return EV_FAULT_DETECTED; + } + } + p->cmlds.timer_count++; + if (p->cmlds.timer_count > p->allowedLostResponses) { + p->asCapable = NOT_CAPABLE; + err = port_cmlds_renew(p, now.tv_sec); + if (err) { + return EV_FAULT_DETECTED; + } + } + return EV_NONE; +} + static int port_pdelay_request(struct port *p) { struct ptp_message *msg; @@ -1871,6 +1919,33 @@ static void port_clear_fda(struct port *p, int count) p->fda.fd[i] = -1; } +static int port_cmlds_initialize(struct port *p) +{ + struct config *cfg = clock_config(p->clock); + const int zero_datalen = 1; + const UInteger8 hops = 0; + struct timespec now; + + p->cmlds.port = config_get_int(cfg, p->name, "cmlds.port"); + if (!p->cmlds.port) { + p->cmlds.port = portnum(p); + } + p->cmlds.pmc = pmc_create(cfg, TRANS_UDS, + config_get_string(cfg, p->name, "cmlds.client_address"), + config_get_string(cfg, p->name, "cmlds.server_address"), + hops, + config_get_int(cfg, p->name, "cmlds.domainNumber"), + config_get_int(cfg, p->name, "cmlds.majorSdoId") << 4, + zero_datalen); + if (!p->cmlds.pmc) { + return -1; + } + p->cmlds.timer_count = 0; + p->fda.fd[FD_CMLDS] = pmc_get_transport_fd(p->cmlds.pmc); + clock_gettime(CLOCK_MONOTONIC, &now); + return port_cmlds_renew(p, now.tv_sec); +} + void port_disable(struct port *p) { int i; @@ -1888,6 +1963,12 @@ void port_disable(struct port *p) close(p->fda.fd[FD_FIRST_TIMER + i]); } + if (p->cmlds.pmc) { + pmc_destroy(p->cmlds.pmc); + p->fda.fd[FD_CMLDS] = -1; + p->cmlds.pmc = NULL; + } + /* Keep rtnl socket to get link status info. */ port_clear_fda(p, FD_RTNL); clock_fda_changed(p->clock); @@ -1963,6 +2044,10 @@ int port_initialize(struct port *p) goto no_tmo; } + if (port_delay_mechanism(p) == DM_COMMON_P2P && port_cmlds_initialize(p)) { + goto no_tmo; + } + /* No need to open rtnl socket on UDS port. */ if (!port_is_uds(p)) { /* @@ -2104,6 +2189,65 @@ int process_announce(struct port *p, struct ptp_message *m) return result; } +static int process_cmlds(struct port *p) +{ + struct cmlds_info_np *cmlds; + struct management_tlv *mgt; + struct ptp_message *msg; + struct TLV *tlv; + int err = 0; + + msg = pmc_recv(p->cmlds.pmc); + if (!msg) { + pr_err("%s: pmc_recv failed", p->log_name); + return -1; + } + if (msg_type(msg) != MANAGEMENT) { + pr_err("%s: pmc_recv bad message", p->log_name); + err = -1; + goto out; + } + tlv = (struct TLV *) msg->management.suffix; + if (tlv->type != TLV_MANAGEMENT) { + pr_err("%s: pmc_recv bad message", p->log_name); + err = -1; + goto out; + } + mgt = (struct management_tlv *) msg->management.suffix; + if (mgt->length == 2 && mgt->id != MID_NULL_MANAGEMENT) { + pr_err("%s: pmc_recv bad length", p->log_name); + goto out; + } + + switch (mgt->id) { + case MID_CMLDS_INFO_NP: + if (msg->header.sourcePortIdentity.portNumber != p->cmlds.port) { + break; + } + cmlds = (struct cmlds_info_np *) mgt->data; + p->peer_delay = nanoseconds_to_tmv(cmlds->meanLinkDelay >> 16); + p->peerMeanPathDelay = cmlds->meanLinkDelay; + p->nrate.ratio = 1.0 + (double) cmlds->scaledNeighborRateRatio / POW2_41; + p->asCapable = cmlds->as_capable; + p->cmlds.timer_count = 0; + if (p->state == PS_UNCALIBRATED || p->state == PS_SLAVE) { + const tmv_t tx = tmv_zero(); + clock_peer_delay(p->clock, p->peer_delay, tx, tx, p->nrate.ratio); + } + break; + case MID_SUBSCRIBE_EVENTS_NP: + break; + default: + pr_err("%s: pmc_recv bad mgt id 0x%x", p->log_name, mgt->id); + err = -1; + break; + } + +out: + msg_put(msg); + return err; +} + static int process_delay_req(struct port *p, struct ptp_message *m) { struct ptp_message *msg; @@ -2115,7 +2259,7 @@ static int process_delay_req(struct port *p, struct ptp_message *m) return 0; } - if (p->delayMechanism == DM_P2P) { + if (p->delayMechanism == DM_P2P || p->delayMechanism == DM_COMMON_P2P) { pr_warning("%s: delay request on P2P port", p->log_name); return 0; } @@ -2280,6 +2424,9 @@ int process_pdelay_req(struct port *p, struct ptp_message *m) return -1; } + if (p->delayMechanism == DM_COMMON_P2P) { + return 0; + } if (p->delayMechanism == DM_E2E) { pr_warning("%s: pdelay_req on E2E port", p->log_name); return 0; @@ -2475,6 +2622,9 @@ calc: int process_pdelay_resp(struct port *p, struct ptp_message *m) { + if (p->delayMechanism == DM_COMMON_P2P) { + return 0; + } if (p->peer_delay_resp) { if (!p->multiple_pdr_detected) { pr_err("%s: multiple peer responses", p->log_name); @@ -2750,10 +2900,14 @@ static void bc_dispatch(struct port *p, enum fsm_event event, int mdiff) return; } - if (p->delayMechanism == DM_P2P) { + switch (p->delayMechanism) { + case DM_COMMON_P2P: + case DM_P2P: port_p2p_transition(p, p->state); - } else { + break; + default: port_e2e_transition(p, p->state); + break; } if (p->jbod && p->state == PS_UNCALIBRATED && p->phc_index >= 0 ) { @@ -2901,6 +3055,9 @@ static enum fsm_event bc_event(struct port *p, int fd_index) case FD_DELAY_TIMER: pr_debug("%s: delay timeout", p->log_name); port_set_delay_tmo(p); + if (p->delayMechanism == DM_COMMON_P2P) { + return port_cmlds_timeout(p); + } delay_req_prune(p); p->service_stats.delay_timeout++; if (port_delay_request(p)) { @@ -2947,6 +3104,10 @@ static enum fsm_event bc_event(struct port *p, int fd_index) p->service_stats.unicast_request_timeout++; return unicast_client_timer(p) ? EV_FAULT_DETECTED : EV_NONE; + case FD_CMLDS: + pr_debug("%s: CMLDS push notification", p->log_name); + return process_cmlds(p) ? EV_FAULT_DETECTED : EV_NONE; + case FD_RTNL: pr_debug("%s: received link status notification", p->log_name); rtnl_link_status(fd, p->name, port_link_status, p); diff --git a/port_private.h b/port_private.h index 1072ad6..4f158cd 100644 --- a/port_private.h +++ b/port_private.h @@ -26,6 +26,7 @@ #include "fsm.h" #include "monitor.h" #include "msg.h" +#include "pmc_common.h" #include "power_profile.h" #include "tmv.h" #include "util.h" @@ -164,6 +165,12 @@ struct port { /* slave event monitoring */ struct monitor *slave_event_monitor; bool unicast_state_dirty; + struct { + unsigned int timer_count; + time_t last_renewal; + struct pmc *pmc; + int port; + } cmlds; }; #define portnum(p) (p->portIdentity.portNumber) diff --git a/ptp4l.8 b/ptp4l.8 index 4cb9adb..87befcd 100644 --- a/ptp4l.8 +++ b/ptp4l.8 @@ -159,6 +159,41 @@ collection of clocks must be synchronized by an external program, for example phc2sys(8) in "automatic" mode. The default is 0 (disabled). +.TP +.B cmlds.client_address +Specifies the source address for the UNIX domain socket that receives +peer delay measurement when using the "COMMON_P2P" delay mechanism. +The default is /var/run/cmlds_cleint. + +.TP +.B cmlds.domainNumber +Specifies the domainNumber message field for communications with the +local Common Mean Link Delay Service, used with the "COMMON_P2P" delay +mechanism. +The default is 0. + +.TP +.B cmlds.majorSdoId +Specifies the transportSpecific message field for communications with +the local Common Mean Link Delay Service, used with the "COMMON_P2P" +delay mechanism. +The default is 2. + +.TP +.B cmlds.port +Specifies the port number of local Common Mean Link Delay Service from +which to accept delay measurements. The range of valid port numbers +is one to 65535, but the special value zero configures the port number +of the port requesting the measurements. +The default is 0, meaning the requesting port number. + +.TP +.B +cmlds.server_address +Specifies the UNIX domain socket address of the local Common Mean Link +Delay Service, for use with the "COMMON_P2P" delay mechanism. +The default is /var/run/cmlds_server. + .TP .B delayAsymmetry The time difference in nanoseconds of the transmit and receive @@ -179,7 +214,9 @@ The default is 10. .TP .B delay_mechanism -Select the delay mechanism. Possible values are E2E, P2P, NONE and Auto. + +Select the delay mechanism. Possible values are Auto, COMMON_P2P, E2E, +P2P, and NONE. The default is E2E. .TP -- 2.39.2 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel