When master and slave instance interacting with each other operating at different interface speed, delay assymetry needs to be compensated as described in G.8271 appendix V.
Signed-off-by: Greg Armstrong <greg.armstrong...@renesas.com> Signed-off-by: Leon Goldin <leon.goldin...@renesas.com> Signed-off-by: Devasish Dey <devasish....@syncmonk.net> Signed-off-by: Vipin Sharma <vipin.sha...@syncmonk.net> --- clock.c | 14 ++++++++++ clock.h | 14 ++++++++++ config.c | 1 + configs/default.cfg | 1 + interface.c | 50 ++++++++++++++++++++++++++++++++++ interface.h | 21 +++++++++++++++ pdt.h | 1 + port.c | 5 ++++ port_private.h | 1 + port_signaling.c | 34 ++++++++++++++++++++++++ sk.c | 65 +++++++++++++++++++++++++++++++++++++++++++++ sk.h | 18 +++++++++++++ tlv.c | 1 + tlv.h | 16 +++++++++++ unicast_service.c | 33 +++++++++++++++++++++++ 15 files changed, 275 insertions(+) diff --git a/clock.c b/clock.c index f808b35..695f818 100644 --- a/clock.c +++ b/clock.c @@ -137,6 +137,7 @@ struct clock { struct monitor *slave_event_monitor; int step_window_counter; int step_window; + bool iface_rate_tlv; }; struct clock the_clock; @@ -1005,6 +1006,8 @@ struct clock *clock_create(enum clock_type type, struct config *config, rtnl_get_ts_device(interface_name(iface), ts_label); interface_set_label(iface, ts_label); interface_ensure_tslabel(iface); + /* Interface speed information */ + interface_get_ifinfo(iface); interface_get_tsinfo(iface); if (interface_tsinfo_valid(iface) && !interface_tsmodes_supported(iface, required_modes)) { @@ -1105,6 +1108,7 @@ struct clock *clock_create(enum clock_type type, struct config *config, c->utc_offset = config_get_int(config, NULL, "utc_offset"); c->time_source = config_get_int(config, NULL, "timeSource"); c->step_window = config_get_int(config, NULL, "step_window"); + c->iface_rate_tlv = config_get_int(config, NULL, "interface_rate_tlv"); if (c->free_running) { c->clkid = CLOCK_INVALID; @@ -1716,6 +1720,11 @@ int clock_slave_only(struct clock *c) return c->dds.flags & DDS_SLAVE_ONLY; } +bool clock_telecom_profile(struct clock *c) +{ + return (c->dscmp == telecom_dscmp); +} + UInteger8 clock_max_steps_removed(struct clock *c) { return c->max_steps_removed; @@ -2081,3 +2090,8 @@ enum servo_state clock_servo_state(struct clock *c) { return c->servo_state; } + +bool clock_interface_rate_tlv (struct clock *c) +{ + return c->iface_rate_tlv; +} diff --git a/clock.h b/clock.h index 0534f21..9b2738a 100644 --- a/clock.h +++ b/clock.h @@ -289,6 +289,13 @@ int clock_slave_only(struct clock *c); */ UInteger8 clock_max_steps_removed(struct clock *c); +/** + * Obtain the clock is set for telecom profile . + * @param c The clock instance. + * @return True if the profile is telecom, false otherwise. + */ +bool clock_telecom_profile(struct clock *c); + /** * Obtain the clock class threshold field from a clock's default data set. * @param c The clock instance. @@ -388,4 +395,11 @@ void clock_check_ts(struct clock *c, uint64_t ts); */ double clock_rate_ratio(struct clock *c); +/** + * Obtain interface rate tlv support configuration. + * @param c The clock instance. + * @return The interface_rate_supoort config true if supported, else false. + */ +bool clock_interface_rate_tlv (struct clock *c); + #endif diff --git a/config.c b/config.c index 6ba9996..0419423 100644 --- a/config.c +++ b/config.c @@ -266,6 +266,7 @@ struct config_item config_tab[] = { PORT_ITEM_INT("inhibit_delay_req", 0, 0, 1), PORT_ITEM_INT("inhibit_multicast_service", 0, 0, 1), GLOB_ITEM_INT("initial_delay", 0, 0, INT_MAX), + GLOB_ITEM_INT("interface_rate_tlv", 0, 0, 1), GLOB_ITEM_INT("kernel_leap", 1, 0, 1), GLOB_ITEM_STR("leapfile", NULL), PORT_ITEM_INT("logAnnounceInterval", 1, INT8_MIN, INT8_MAX), diff --git a/configs/default.cfg b/configs/default.cfg index 1b5b806..4c40a03 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -19,6 +19,7 @@ dscp_general 0 dataset_comparison ieee1588 G.8275.defaultDS.localPriority 128 maxStepsRemoved 255 +interface_rate_tlv 0 # # Port Data Set # diff --git a/interface.c b/interface.c index 445a270..8559f62 100644 --- a/interface.c +++ b/interface.c @@ -12,6 +12,7 @@ struct interface { char name[MAX_IFNAME_SIZE + 1]; char ts_label[MAX_IFNAME_SIZE + 1]; struct sk_ts_info ts_info; + struct sk_if_info if_info; int vclock; }; @@ -46,6 +47,11 @@ int interface_get_tsinfo(struct interface *iface) return sk_get_ts_info(iface->ts_label, &iface->ts_info); } +int interface_get_ifinfo(struct interface *iface) +{ + return sk_get_if_info(iface->ts_label, &iface->if_info); +} + const char *interface_label(struct interface *iface) { return iface->ts_label; @@ -71,6 +77,11 @@ bool interface_tsinfo_valid(struct interface *iface) return iface->ts_info.valid ? true : false; } +bool interface_ifinfo_valid(struct interface *iface) +{ + return iface->if_info.valid ? true : false; +} + bool interface_tsmodes_supported(struct interface *iface, int modes) { if ((iface->ts_info.so_timestamping & modes) == modes) { @@ -88,3 +99,42 @@ int interface_get_vclock(struct interface *iface) { return iface->vclock; } + +uint64_t interface_bitperiod(struct interface *iface) +{ + uint64_t if_atto_sec_bit_period = 0; + + if (!iface->if_info.valid) + return if_atto_sec_bit_period; + + switch (iface->if_info.speed) { + case 1: + if_atto_sec_bit_period = 0x0DE0B6B3A7640000; + break; + case 10: + if_atto_sec_bit_period = 0x000000174876E800; + break; + case 100: + if_atto_sec_bit_period = 0x00000002540BE400; + break; + case 1000: + if_atto_sec_bit_period = 0x000000003B9ACA00; + break; + case 10000: + if_atto_sec_bit_period = 0x0000000005F5E100; + break; + case 25000: + if_atto_sec_bit_period = 0x0000000002625A00; + break; + case 40000: + if_atto_sec_bit_period = 0x00000000017D7840; + break; + case 100000: + if_atto_sec_bit_period = 0x0000000000989680; + break; + case 1000000: + if_atto_sec_bit_period = 0x00000000000F4240; + break; + } + return if_atto_sec_bit_period; +} diff --git a/interface.h b/interface.h index 752f4f1..d9fce5c 100644 --- a/interface.h +++ b/interface.h @@ -46,6 +46,13 @@ void interface_ensure_tslabel(struct interface *iface); */ int interface_get_tsinfo(struct interface *iface); +/** + * Populate the time stamping information of a given interface. + * @param iface The interface of interest. + * @return zero on success, negative on failure. + */ +int interface_get_ifinfo(struct interface *iface); + /** * Obtain the time stamping label of a network interface. This can be * different from the name of the interface when bonding is in effect. @@ -83,6 +90,13 @@ void interface_set_label(struct interface *iface, const char *label); */ bool interface_tsinfo_valid(struct interface *iface); +/** + * Tests whether an interface's interface information is valid or not. + * @param iface The interface of interest. + * @return True if the interface information is valid, false otherwise. + */ +bool interface_ifinfo_valid(struct interface *iface); + /** * Tests whether an interface supports a set of given time stamping modes. * @param iface The interface of interest. @@ -91,6 +105,13 @@ bool interface_tsinfo_valid(struct interface *iface); */ bool interface_tsmodes_supported(struct interface *iface, int modes); +/** + * Obtains the interface bit period based on the speed. + * @param iface The interface of interest. + * @return if valid speed return interface bitperiod in atto seconds. + */ +uint64_t interface_bitperiod(struct interface *iface); + /** * Set the vclock (virtual PHC) to be used for timestamping on an interface. * @param iface The interface of interest. diff --git a/pdt.h b/pdt.h index e46b218..1ad23d4 100644 --- a/pdt.h +++ b/pdt.h @@ -39,6 +39,7 @@ typedef uint16_t UInteger16; typedef int32_t Integer32; typedef uint32_t UInteger32; typedef int64_t Integer64; +typedef uint64_t UInteger64; typedef uint8_t Octet; #endif diff --git a/port.c b/port.c index d9dac38..b93225b 100644 --- a/port.c +++ b/port.c @@ -2690,6 +2690,11 @@ void port_link_status(void *ctx, int linkup, int ts_index) p->link_status = link_state; } else { p->link_status = link_state | LINK_STATE_CHANGED; + /* Update Interface speed information on Link up*/ + if (linkup) { + interface_get_ifinfo(p->iface); + } + pr_notice("%s: link %s", p->log_name, linkup ? "up" : "down"); } diff --git a/port_private.h b/port_private.h index d27dceb..6c8eba8 100644 --- a/port_private.h +++ b/port_private.h @@ -145,6 +145,7 @@ struct port { UInteger8 versionNumber; /* UInteger4 */ UInteger8 delay_response_counter; UInteger8 delay_response_timeout; + Integer64 portAsymmetry; struct PortStats stats; struct PortServiceStats service_stats; /* foreignMasterDS */ diff --git a/port_signaling.c b/port_signaling.c index ed217c0..ccc1b11 100644 --- a/port_signaling.c +++ b/port_signaling.c @@ -22,6 +22,7 @@ #include "print.h" #include "unicast_client.h" #include "unicast_service.h" +#include "clock.h" const struct PortIdentity wildcard_pid = { .clockIdentity = { @@ -103,10 +104,39 @@ static int process_interval_request(struct port *p, return 0; } +static int process_interface_rate(struct port *p, + struct msg_interface_rate_tlv *r) +{ + Integer64 delayAsymmetry; + double nsDelay; + + if (clock_interface_rate_tlv (p->clock) && + interface_ifinfo_valid(p->iface)) { + /* Delay Asymmetry Calculation */ + delayAsymmetry = r->interfaceBitPeriod - + interface_bitperiod(p->iface); + delayAsymmetry = delayAsymmetry/2; + nsDelay = (double)delayAsymmetry / 1000000000; + if (nsDelay) { + delayAsymmetry = + (r->numberOfBitsAfterTimestamp - + r->numberOfBitsBeforeTimestamp) * nsDelay; + if (delayAsymmetry != p->portAsymmetry) { + /* Updating the nanosecond part */ + p->asymmetry += + ((delayAsymmetry - p->portAsymmetry) << 16); + p->portAsymmetry = delayAsymmetry; + } + } + } + return 0; +} + int process_signaling(struct port *p, struct ptp_message *m) { struct tlv_extra *extra; struct msg_interval_req_tlv *r; + struct msg_interface_rate_tlv *rate; int err = 0, result; switch (p->state) { @@ -161,10 +191,14 @@ int process_signaling(struct port *p, struct ptp_message *m) case TLV_ORGANIZATION_EXTENSION: r = (struct msg_interval_req_tlv *) extra->tlv; + rate = (struct msg_interface_rate_tlv *) extra->tlv; if (0 == memcmp(r->id, ieee8021_id, sizeof(ieee8021_id)) && r->subtype[0] == 0 && r->subtype[1] == 0 && r->subtype[2] == 2) err = process_interval_request(p, r); + else if (0 == memcmp(r->id, itu_t_id, sizeof(itu_t_id)) && + r->subtype[0] == 0 && r->subtype[1] == 0 && r->subtype[2] == 2) + err = process_interface_rate(p, rate); break; } } diff --git a/sk.c b/sk.c index b55d6b5..6a0fc5e 100644 --- a/sk.c +++ b/sk.c @@ -200,6 +200,71 @@ failed: return -1; } +int sk_get_if_info(const char *name, struct sk_if_info *if_info) +{ +#ifdef ETHTOOL_GLINKSETTINGS + struct ifreq ifr; + int fd, err; + + struct { + struct ethtool_link_settings req; + __u32 link_mode_data[3 * 127]; + } ecmd; + + memset(&ifr, 0, sizeof(ifr)); + memset(&ecmd, 0, sizeof(ecmd)); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + goto failed; + } + + ecmd.req.cmd = ETHTOOL_GLINKSETTINGS; + + strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); + ifr.ifr_data = (char *) &ecmd; + + /* Handshake with kernel to determine number of words for link + * mode bitmaps. When requested number of bitmap words is not + * the one expected by kernel, the latter returns the integer + * opposite of what it is expecting. We request length 0 below + * (aka. invalid bitmap length) to get this info. + */ + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err < 0) { + pr_err("ioctl SIOCETHTOOL failed: %m"); + close(fd); + goto failed; + } + + if (ecmd.req.link_mode_masks_nwords >= 0 || + ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) { + return 1; + } + ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; + + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err < 0) { + pr_err("ioctl SIOCETHTOOL failed: %m"); + close(fd); + goto failed; + } + + close(fd); + + /* copy the necessary data to sk_info */ + memset(if_info, 0, sizeof(struct sk_if_info)); + if_info->valid = 1; + if_info->speed = ecmd.req.speed; + + return 0; +failed: +#endif + /* clear data and ensure it is not marked valid */ + memset(if_info, 0, sizeof(struct sk_if_info)); + return -1; +} + static int sk_interface_guidaddr(const char *name, unsigned char *guid) { char file_name[64], buf[64], addr[8]; diff --git a/sk.h b/sk.h index 486dbc4..853aadf 100644 --- a/sk.h +++ b/sk.h @@ -49,6 +49,16 @@ struct sk_ts_info { unsigned int rx_filters; }; +/** + * Contains interface information returned by theGLINKSETTINGS ioctl. + * @valid: set to non-zero when the info struct contains valid data. + * @speed: interface speed. + */ +struct sk_if_info { + int valid; + int speed; +}; + /** * Obtains a socket suitable for use with sk_interface_index(). * @return An open socket on success, -1 otherwise. @@ -78,6 +88,14 @@ int sk_general_init(int fd); */ int sk_get_ts_info(const char *name, struct sk_ts_info *sk_info); +/** + * Obtain supporte interface information + * @param name The name of the interface + * @param info Struct containing obtained interface information. + * @return zero on success, negative on failure. + */ +int sk_get_if_info(const char *name, struct sk_if_info *sk_info); + /** * Obtain the MAC address of a network interface. * @param name The name of the interface diff --git a/tlv.c b/tlv.c index 1c13460..35bee4f 100644 --- a/tlv.c +++ b/tlv.c @@ -35,6 +35,7 @@ (tlv->length < sizeof(struct type) - sizeof(struct TLV)) uint8_t ieee8021_id[3] = { IEEE_802_1_COMMITTEE }; +uint8_t itu_t_id[3] = { ITU_T_COMMITTEE }; static TAILQ_HEAD(tlv_pool, tlv_extra) tlv_pool = TAILQ_HEAD_INITIALIZER(tlv_pool); diff --git a/tlv.h b/tlv.h index 8966696..573af89 100644 --- a/tlv.h +++ b/tlv.h @@ -312,6 +312,20 @@ struct msg_interval_req_tlv { Octet reserved[2]; } PACKED; +/* Organizationally Unique Identifiers */ +#define ITU_T_COMMITTEE 0x00, 0x19, 0xA7 +extern uint8_t itu_t_id[3]; + +struct msg_interface_rate_tlv { + Enumeration16 type; + UInteger16 length; + Octet id[3]; + Octet subtype[3]; + UInteger64 interfaceBitPeriod; + UInteger16 numberOfBitsBeforeTimestamp; + UInteger16 numberOfBitsAfterTimestamp; +} PACKED; + struct time_status_np { int64_t master_offset; /*nanoseconds*/ int64_t ingress_time; /*nanoseconds*/ @@ -395,6 +409,8 @@ struct tlv_extra { }; }; +extern uint8_t itu_t_id[3]; + /** * Allocates a new tlv_extra structure. * @return Pointer to a new structure on success or NULL otherwise. diff --git a/unicast_service.c b/unicast_service.c index 3154894..8ca2214 100644 --- a/unicast_service.c +++ b/unicast_service.c @@ -84,6 +84,30 @@ static int attach_grant(struct ptp_message *msg, return 0; } +static int attach_interface_rate(struct ptp_message *msg, + uint64_t iface_bit_period, + uint16_t no_of_bits_before_ts, + uint16_t no_of_bits_after_ts) +{ + struct msg_interface_rate_tlv *mir; + struct tlv_extra *extra; + + extra = msg_tlv_append(msg, sizeof(*mir)); + if (!extra) { + return -1; + } + mir = (struct msg_interface_rate_tlv *) extra->tlv; + mir->type = TLV_ORGANIZATION_EXTENSION; + mir->length = sizeof(*mir) - sizeof(mir->type) - sizeof(mir->length); + memcpy(mir->id, itu_t_id, sizeof(itu_t_id)); + mir->subtype[2] = 2; + mir->interfaceBitPeriod = iface_bit_period; + mir->numberOfBitsBeforeTimestamp = no_of_bits_before_ts; + mir->numberOfBitsAfterTimestamp = no_of_bits_after_ts; + + return 0; +} + static int compare_timeout(void *ain, void *bin) { struct unicast_service_interval *a, *b; @@ -256,6 +280,15 @@ static int unicast_service_reply(struct port *p, struct ptp_message *dst, if (err) { goto out; } + if (clock_interface_rate_tlv (p->clock) && duration > 0 && + clock_telecom_profile(p->clock) && + interface_ifinfo_valid(p->iface)) { + err = attach_interface_rate(msg, + interface_bitperiod(p->iface), 64, 720); + if (err) { + goto out; + } + } err = port_prepare_and_send(p, msg, TRANS_GENERAL); if (err) { pr_err("%s: signaling message failed", p->log_name); -- 2.17.1 _______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel