Thread: Re: [Linuxptp-devel] [PATCH 01/11] synce4l: add config knobs for SyncE (Page 4)
PTP IEEE 1588 stack for Linux
Brought to you by:
rcochran
From: Aya L. <ay...@nv...> - 2022-06-08 09:33:35
|
On 5/2/2022 12:05 PM, Arkadiusz Kubalewski wrote: > Allow config interface to parse SyncE related config files. > > Co-developed-by: Piotr Kwapulinski <pio...@in...> > Signed-off-by: Piotr Kwapulinski <pio...@in...> > Co-developed-by: Michal Michalik <mic...@in...> > Signed-off-by: Michal Michalik <mic...@in...> > Signed-off-by: Arkadiusz Kubalewski <ark...@in...> > --- > config.c | 198 ++++++++++++++++++++++++++++++++++++---------- > config.h | 8 ++ > configs/synce.cfg | 194 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 358 insertions(+), 42 deletions(-) > create mode 100644 configs/synce.cfg > > diff --git a/config.c b/config.c > index 4f3ceb8a20d7..f0ad329a7f18 100644 > --- a/config.c > +++ b/config.c > @@ -41,6 +41,7 @@ enum config_section { > GLOBAL_SECTION, > UC_MTAB_SECTION, > PORT_SECTION, > + DEVICE_SECTION, > UNKNOWN_SECTION, > }; > > @@ -68,6 +69,7 @@ typedef union { > #define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */ > #define CFG_ITEM_PORT (1 << 2) /* item may appear in port sections */ > #define CFG_ITEM_DYNSTR (1 << 4) /* string value dynamically allocated */ > +#define CFG_ITEM_DEVICE (1 << 8) /* item may appear in device sections */ > > struct config_item { > char label[CONFIG_LABEL_SIZE]; > @@ -79,12 +81,18 @@ struct config_item { > any_t max; > }; > > -#define N_CONFIG_ITEMS (sizeof(config_tab) / sizeof(config_tab[0])) > +#define N_CONFIG_ITEMS_PTP (sizeof(config_tab_ptp) / sizeof(config_tab_ptp[0])) > +#define N_CONFIG_ITEMS_SYNCE ((sizeof(config_tab_synce) / \ > + sizeof(config_tab_synce[0]))) > + > +#define PORT_TO_FLAG(_port) (_port == PORT_SECTION ? CFG_ITEM_PORT : \ > + _port == DEVICE_SECTION ? CFG_ITEM_DEVICE : \ > + CFG_ITEM_STATIC) > > #define CONFIG_ITEM_DBL(_label, _port, _default, _min, _max) { \ > .label = _label, \ > .type = CFG_TYPE_DOUBLE, \ > - .flags = _port ? CFG_ITEM_PORT : 0, \ > + .flags = PORT_TO_FLAG(_port), \ > .val.d = _default, \ > .min.d = _min, \ > .max.d = _max, \ > @@ -92,14 +100,14 @@ struct config_item { > #define CONFIG_ITEM_ENUM(_label, _port, _default, _table) { \ > .label = _label, \ > .type = CFG_TYPE_ENUM, \ > - .flags = _port ? CFG_ITEM_PORT : 0, \ > + .flags = PORT_TO_FLAG(_port), \ > .tab = _table, \ > .val.i = _default, \ > } > #define CONFIG_ITEM_INT(_label, _port, _default, _min, _max) { \ > .label = _label, \ > .type = CFG_TYPE_INT, \ > - .flags = _port ? CFG_ITEM_PORT : 0, \ > + .flags = PORT_TO_FLAG(_port), \ > .val.i = _default, \ > .min.i = _min, \ > .max.i = _max, \ > @@ -107,33 +115,39 @@ struct config_item { > #define CONFIG_ITEM_STRING(_label, _port, _default) { \ > .label = _label, \ > .type = CFG_TYPE_STRING, \ > - .flags = _port ? CFG_ITEM_PORT : 0, \ > + .flags = PORT_TO_FLAG(_port), \ > .val.s = _default, \ > } > > #define GLOB_ITEM_DBL(label, _default, min, max) \ > - CONFIG_ITEM_DBL(label, 0, _default, min, max) > + CONFIG_ITEM_DBL(label, GLOBAL_SECTION, _default, min, max) > > #define GLOB_ITEM_ENU(label, _default, table) \ > - CONFIG_ITEM_ENUM(label, 0, _default, table) > + CONFIG_ITEM_ENUM(label, GLOBAL_SECTION, _default, table) > > #define GLOB_ITEM_INT(label, _default, min, max) \ > - CONFIG_ITEM_INT(label, 0, _default, min, max) > + CONFIG_ITEM_INT(label, GLOBAL_SECTION, _default, min, max) > > #define GLOB_ITEM_STR(label, _default) \ > - CONFIG_ITEM_STRING(label, 0, _default) > + CONFIG_ITEM_STRING(label, GLOBAL_SECTION, _default) > > #define PORT_ITEM_DBL(label, _default, min, max) \ > - CONFIG_ITEM_DBL(label, 1, _default, min, max) > + CONFIG_ITEM_DBL(label, PORT_SECTION, _default, min, max) > > #define PORT_ITEM_ENU(label, _default, table) \ > - CONFIG_ITEM_ENUM(label, 1, _default, table) > + CONFIG_ITEM_ENUM(label, PORT_SECTION, _default, table) > > #define PORT_ITEM_INT(label, _default, min, max) \ > - CONFIG_ITEM_INT(label, 1, _default, min, max) > + CONFIG_ITEM_INT(label, PORT_SECTION, _default, min, max) > > #define PORT_ITEM_STR(label, _default) \ > - CONFIG_ITEM_STRING(label, 1, _default) > + CONFIG_ITEM_STRING(label, PORT_SECTION, _default) > + > +#define DEV_ITEM_INT(label, _default, min, max) \ > + CONFIG_ITEM_INT(label, DEVICE_SECTION, _default, min, max) > + > +#define DEV_ITEM_STR(label, _default) \ > + CONFIG_ITEM_STRING(label, DEVICE_SECTION, _default) > > static struct config_enum clock_servo_enu[] = { > { "pi", CLOCK_SERVO_PI }, > @@ -220,7 +234,7 @@ static struct config_enum bmca_enu[] = { > { NULL, 0 }, > }; > > -struct config_item config_tab[] = { > +struct config_item config_tab_ptp[] = { > PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX), > PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu), > GLOB_ITEM_INT("assume_two_step", 0, 0, 1), > @@ -341,7 +355,37 @@ struct config_item config_tab[] = { > GLOB_ITEM_INT("write_phase_mode", 0, 0, 1), > }; > > +struct config_item config_tab_synce[] = { > + GLOB_ITEM_INT("logging_level", LOG_INFO, PRINT_LEVEL_MIN, PRINT_LEVEL_MAX), > + GLOB_ITEM_STR("message_tag", NULL), > + GLOB_ITEM_INT("use_syslog", 1, 0, 1), > + GLOB_ITEM_STR("userDescription", ""), > + GLOB_ITEM_INT("verbose", 0, 0, 1), > + DEV_ITEM_INT("internal_input", 1, 0, 1), > + DEV_ITEM_INT("external_input", 0, 0, 1), > + DEV_ITEM_INT("external_input_QL", 0, 0, 15), > + DEV_ITEM_INT("external_input_ext_QL", 0, 0, 255), > + DEV_ITEM_INT("extended_tlv", 0, 0, 1), > + DEV_ITEM_INT("network_option", 1, 1, 2), > + DEV_ITEM_INT("recover_time", 300, 10, 720), > + DEV_ITEM_STR("dpll_get_state_cmd", NULL), > + DEV_ITEM_STR("dpll_holdover_value", NULL), > + DEV_ITEM_STR("dpll_locked_ho_value", NULL), > + DEV_ITEM_STR("dpll_locked_value", NULL), > + DEV_ITEM_STR("dpll_freerun_value", NULL), > + DEV_ITEM_STR("dpll_invalid_value", NULL), > + PORT_ITEM_STR("allowed_qls", NULL), > + PORT_ITEM_STR("allowed_ext_qls", NULL), > + PORT_ITEM_INT("sync", 0, 0, 1), > + PORT_ITEM_STR("recover_clock_enable_cmd", NULL), > + PORT_ITEM_STR("recover_clock_disable_cmd", NULL), > + PORT_ITEM_INT("tx_heartbeat_msec", 1000, 100, 3000), > + PORT_ITEM_INT("rx_heartbeat_msec", 50, 10, 500), > +}; > + > static struct unicast_master_table *current_uc_mtab; > +static struct interface *__config_create_interface(const char *name, struct config *cfg, > + const char *type); > > static enum parser_result > parse_fault_interval(struct config *cfg, const char *section, > @@ -392,6 +436,8 @@ static struct config_item *config_item_alloc(struct config *cfg, > } > strncpy(ci->label, name, CONFIG_LABEL_SIZE - 1); > ci->type = type; > + ci->val.s = NULL; > + ci->flags = 0; > > snprintf(buf, sizeof(buf), "%s.%s", section, ci->label); > if (hash_insert(cfg->htab, buf, ci)) { > @@ -406,8 +452,11 @@ static struct config_item *config_item_alloc(struct config *cfg, > static void config_item_free(void *ptr) > { > struct config_item *ci = ptr; > - if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR) > + if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR > + && ci->val.s != NULL) { > free(ci->val.s); > + ci->val.s = NULL; > + } > if (ci->flags & CFG_ITEM_STATIC) > return; > free(ci); > @@ -512,10 +561,13 @@ static enum parser_result parse_section_line(char *s, enum config_section *secti > current_uc_mtab = NULL; > } else if (s[0] == '[') { > char c; > - *section = PORT_SECTION; > - /* Replace square brackets with white space. */ > + if (s[1] == '<') > + *section = DEVICE_SECTION; > + else > + *section = PORT_SECTION; > + /* Replace brackets with white space. */ > while (0 != (c = *s)) { > - if (c == '[' || c == ']') > + if (c == '[' || c == ']' || c == '<' || c == '>') > *s = ' '; > s++; > } > @@ -573,7 +625,8 @@ static enum parser_result parse_item(struct config *cfg, > } > > if (section) { > - if (!(cgi->flags & CFG_ITEM_PORT)) { > + if (!(cgi->flags & CFG_ITEM_PORT) && > + !(cgi->flags & CFG_ITEM_DEVICE)) { > return NOT_PARSED; > } > /* Create or update this port specific item. */ > @@ -601,8 +654,9 @@ static enum parser_result parse_item(struct config *cfg, > dst->val.d = df; > break; > case CFG_TYPE_STRING: > - if (dst->flags & CFG_ITEM_DYNSTR) { > + if (dst->flags & CFG_ITEM_DYNSTR && dst->val.s != NULL) { > free(dst->val.s); > + dst->val.s = NULL; > } > dst->val.s = strdup(value); > if (!dst->val.s) { > @@ -728,18 +782,26 @@ static void check_deprecated_options(const char **option) > } > } > > -static struct option *config_alloc_longopts(void) > +static struct option *config_alloc_longopts(enum feature_type type) > { > - struct config_item *ci; > + struct config_item *ci, *ci_tab; > struct option *opts; > - int i; > + int i, n_items; > > - opts = calloc(1, (1 + N_CONFIG_ITEMS) * sizeof(*opts)); > + if (type == PTP) { > + ci_tab = &config_tab_ptp[0]; > + n_items = N_CONFIG_ITEMS_PTP; > + } else { > + ci_tab = &config_tab_synce[0]; > + n_items = N_CONFIG_ITEMS_SYNCE; > + } > + n_items = (type == PTP ? N_CONFIG_ITEMS_PTP : N_CONFIG_ITEMS_SYNCE); > + opts = calloc(1, (1 + n_items) * sizeof(*opts)); > if (!opts) { > return NULL; > } > - for (i = 0; i < N_CONFIG_ITEMS; i++) { > - ci = &config_tab[i]; > + for (i = 0; i < n_items; i++) { > + ci = &ci_tab[i]; > opts[i].name = ci->label; > opts[i].has_arg = required_argument; > /* Avoid bug in detection of ambiguous options in glibc */ > @@ -757,6 +819,8 @@ int config_read(const char *name, struct config *cfg) > char buf[1024], *line, *c; > const char *option, *value; > struct interface *current_port = NULL; > + struct interface *current_device = NULL; > + bool is_synce = (cfg->type == SYNCE); > int line_num; > > fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r"); > @@ -787,6 +851,7 @@ int config_read(const char *name, struct config *cfg) > if (parse_section_line(line, ¤t_section) == PARSED_OK) { > if (current_section == PORT_SECTION) { > char port[17]; > + > if (1 != sscanf(line, " %16s", port)) { > fprintf(stderr, "could not parse port name on line %d\n", > line_num); > @@ -795,6 +860,27 @@ int config_read(const char *name, struct config *cfg) > current_port = config_create_interface(port, cfg); > if (!current_port) > goto parse_error; > + if (is_synce) { > + if (current_device) { > + interface_se_set_parent_dev(current_port, > + interface_name(current_device)); > + } else { > + goto parse_error; > + } > + } > + } else if (current_section == DEVICE_SECTION) { > + /* clear port on new device found in config */ > + current_port = NULL; > + char device[17]; > + > + if (sscanf(line, " %16s", device) != 1) { > + fprintf(stderr, "could not parse device name on line %d\n", > + line_num); > + goto parse_error; > + } > + current_device = __config_create_interface(device, cfg, "device"); > + if (!current_device) > + goto parse_error; > } > continue; > } > @@ -814,14 +900,16 @@ int config_read(const char *name, struct config *cfg) > if (parse_setting_line(line, &option, &value)) { > fprintf(stderr, "could not parse line %d in %s section\n", > line_num, current_section == GLOBAL_SECTION ? > - "global" : interface_name(current_port)); > + "global" : interface_name(current_port ? > + current_port : current_device)); > goto parse_error; > } > > check_deprecated_options(&option); > > parser_res = parse_item(cfg, 0, current_section == GLOBAL_SECTION ? > - NULL : interface_name(current_port), > + NULL : interface_name(current_port ? > + current_port : current_device), > option, value); > switch (parser_res) { > case PARSED_OK: > @@ -830,7 +918,8 @@ int config_read(const char *name, struct config *cfg) > fprintf(stderr, "unknown option %s at line %d in %s section\n", > option, line_num, > current_section == GLOBAL_SECTION ? "global" : > - interface_name(current_port)); > + interface_name(current_port ? > + current_port : current_device)); > goto parse_error; > case BAD_VALUE: > fprintf(stderr, "%s is a bad value for option %s at line %d\n", > @@ -856,7 +945,7 @@ parse_error: > return -2; > } > > -struct interface *config_create_interface(const char *name, struct config *cfg) > +struct interface *__config_create_interface(const char *name, struct config *cfg, const char *type) > { > struct interface *iface; > const char *ifname; > @@ -870,7 +959,7 @@ struct interface *config_create_interface(const char *name, struct config *cfg) > > iface = interface_create(name); > if (!iface) { > - fprintf(stderr, "cannot allocate memory for a port\n"); > + fprintf(stderr, "cannot allocate memory for a %s\n", type); > return NULL; > } > STAILQ_INSERT_TAIL(&cfg->interfaces, iface, list); > @@ -879,12 +968,17 @@ struct interface *config_create_interface(const char *name, struct config *cfg) > return iface; > } > > -struct config *config_create(void) > +struct interface *config_create_interface(const char *name, struct config *cfg) > +{ > + return __config_create_interface(name, cfg, "port"); > +} > + > +struct config *__config_create(enum feature_type type) > { > char buf[CONFIG_LABEL_SIZE + 8]; > - struct config_item *ci; > + struct config_item *ci, *ci_tab; > struct config *cfg; > - int i; > + int i, end; > > cfg = calloc(1, sizeof(*cfg)); > if (!cfg) { > @@ -893,7 +987,7 @@ struct config *config_create(void) > STAILQ_INIT(&cfg->interfaces); > STAILQ_INIT(&cfg->unicast_master_tables); > > - cfg->opts = config_alloc_longopts(); > + cfg->opts = config_alloc_longopts(type); > if (!cfg->opts) { > free(cfg); > return NULL; > @@ -906,9 +1000,18 @@ struct config *config_create(void) > return NULL; > } > > + cfg->type = type; > + if (type == PTP) { > + ci_tab = &config_tab_ptp[0]; > + end = N_CONFIG_ITEMS_PTP; > + } else { > + ci_tab = &config_tab_synce[0]; > + end = N_CONFIG_ITEMS_SYNCE; > + } > + > /* Populate the hash table with global defaults. */ > - for (i = 0; i < N_CONFIG_ITEMS; i++) { > - ci = &config_tab[i]; > + for (i = 0; i < end; i++) { > + ci = &ci_tab[i]; > ci->flags |= CFG_ITEM_STATIC; > snprintf(buf, sizeof(buf), "global.%s", ci->label); > if (hash_insert(cfg->htab, buf, ci)) { > @@ -918,12 +1021,12 @@ struct config *config_create(void) > } > > /* Perform a Built In Self Test.*/ > - for (i = 0; i < N_CONFIG_ITEMS; i++) { > - ci = &config_tab[i]; > + for (i = 0; i < end; i++) { > + ci = &ci_tab[i]; > ci = config_global_item(cfg, ci->label); > - if (ci != &config_tab[i]) { > + if (ci != &ci_tab[i]) { > fprintf(stderr, "config BIST failed at %s\n", > - config_tab[i].label); > + ci_tab[i].label); > goto fail; > } > } > @@ -935,6 +1038,16 @@ fail: > return NULL; > } > > +struct config *config_create(void) > +{ > + return __config_create(PTP); > +} > + > +struct config *config_create_synce(void) > +{ > + return __config_create(SYNCE); > +} > + > void config_destroy(struct config *cfg) > { > struct unicast_master_address *address; > @@ -1137,8 +1250,9 @@ int config_set_string(struct config *cfg, const char *option, > return -1; > } > ci->flags |= CFG_ITEM_LOCKED; > - if (ci->flags & CFG_ITEM_DYNSTR) { > + if (ci->flags & CFG_ITEM_DYNSTR && ci->val.s != NULL) { > free(ci->val.s); > + ci->val.s = NULL; > } > ci->val.s = strdup(val); > if (!ci->val.s) { > diff --git a/config.h b/config.h > index 14d2f64415dc..a32e0079cf21 100644 > --- a/config.h > +++ b/config.h > @@ -32,10 +32,16 @@ > #include "servo.h" > #include "sk.h" > > +enum feature_type { > + PTP = 0, > + SYNCE > +}; > + > struct config { > /* configured interfaces */ > STAILQ_HEAD(interfaces_head, interface) interfaces; > int n_interfaces; > + enum feature_type type; > > /* for parsing command line options */ > struct option *opts; > @@ -55,6 +61,8 @@ void config_destroy(struct config *cfg); > > struct config *config_create(void); > > +struct config *config_create_synce(void); > + > double config_get_double(struct config *cfg, const char *section, > const char *option); > > diff --git a/configs/synce.cfg b/configs/synce.cfg > new file mode 100644 > index 000000000000..eadb318bad6f > --- /dev/null > +++ b/configs/synce.cfg > @@ -0,0 +1,194 @@ > +# Global section is for debuging mostly > +[global] > +# > +# Runtime options > +# > +logging_level 7 > +use_syslog 0 > +verbose 1 > +message_tag [synce4l] > + > + > +# > +# Device section > +# Per-device configuration > +# > +# User defined name of a one logical device configured for SyncE in the system. > +# All the ports configured after this section will be a part of this device > +# (until next device section). > +[<synce1>] > + > +# > +# If internal inputs are allowed > +# 0 if internal input sources shall not be used > +# 1 if internal input sources shall be used > +# default: 1 > +# > +# Internal inputs mean the inputs recovered from the PHY's. > +# The ports configured (in the port-sections [<dev name>], under the device > +# section) will be monitored for the QL (Quality Level). > +# QL is sent by the peer connected to the port and represents the Holdover > +# performance of the peer. > +# The best QL (from all the ports where "sync = 1") is selected and frequency > +# recovered on that port shall be used to feed its frequency to all the other > +# ports. > +# > +internal_input 1 > + > +# > +# If external inputs are allowed > +# 0 if external input sources shall not be used > +# 1 if external input sources shall be used > +# default: 0 > +# > +# External inputs are either 1PPS from buil-in GPS module or 1PPS from the > +# on-board SMA connectors > +# > +# Device must be pre-confiured to use this setting. > +# Before running the application, one of the external inputs shall be selected. > +# This is done through the interface supplied by the NIC vendor. > +# > +# In this mode synce4l application is only responsible for sending > +# the QL to the peers on all configured ports (where "sync = 1") > +# The QL value that is sent equals configured "external_input_QL" > +# (and "external_input_ext_QL" in case of "extended_tlv=1") > +# > +external_input 0 > + > +# > +# These values are sent to the peers on configured ports ONLY when 'external > +# input' mode is enabled. > +# Valid values are defined in Table 11-7 and Table 11-8 of recommendation > +# ITU-T G.8264. > +# They shall be configured appropriately so they are understood by the peer. > +# > +# external_input_QL corresponds to the SSM code column. > +# > +# external_input_ext_QL corresponds to the Enhanced SSM code column > +# (is used only if "extended_tlv = 1") > +# > +external_input_QL 2 > +external_input_ext_QL 255 > + > +# > +# If extended TLV shall be supported on the device. > +# 0 if no extended tlv shall be supported > +# 1 if extended tlv shall be supported > +# default: 0 > +# > +# In case of 0: > +# - the port will always TX the non-extended TLV, for RX only > +# non-extended TLV will be processed for reference signal selection > +# In case of 1: > +# - If port is configured with external_input=1, the TX will always use > +# extended TLV (no RX is required in this case) > +# - If port is configured with external_input=0 and internal_input=1, the > +# TX version of TLV will be propagated from the port that was chosen as > +# candidate for frequency synchronization > +# > +extended_tlv 1 > + > +# > +# Which network option shall be supported > +# > +# 1 or 2 as defined in T-REC-G.8264 > +# default: 1 > +# > +# This is rather per-network option, all device in SyncE network > +# shall have this configured for the same value > +# > +network_option 1 > + > +# > +# Seconds indicating minimum time to recover from the QL-failed state on the > +# port. > +# Range: 10-720 > +# Default: 300 > +# > +# If valid QL was not received from one of the source ports within 5 seconds > +# the port is no longer a valid source (marked as QL-failed) > +# > +# Valid QL must be received for more then "recover_time" seconds on that port > +# to use its PHY recovered signal again as a valid source > +# > +recover_time 10 > + > +# > +# Shell command to be executed in order to obtain current DPLL status of a > +# device. > +# > +dpll_get_state_cmd cat /sys/class/net/enp1s0f0/device/cgu_state > + > +# > +# DPLL state values, must equal to values produced by stdout of > +# "dpll_get_state_cmd" command > +# > +dpll_holdover_value 4 > +dpll_locked_ho_value 3 > +dpll_locked_value 2 > +dpll_freerun_value 1 > +dpll_invalid_value 0 > + > +# > +# Port section(s) > +# > +# It starts per-port configuration. > +# Each port (of the device) that is used for SyncE, shall have its own section > +# with at least sync = 1 (which defines port as synchronous mode) > +# > +[enp1s0f0] > + > +# > +# msec resolution of TX the QL from this port to the peer > +# [100-3000], default:1000 (1000 = 1 second is expected by the standard) > +# > +# As the standard expects 1 sec, it is not recommended to use different > +# than a 1000. > +# > +tx_heartbeat_msec 2000 The default should be 1000 > + > +# > +# recovered PHY signal can be lost at anytime, this is msec resolution of > +# reading the socket, acting on signal lost shall be done just after > +# [10-500], default:50 > +# > +rx_heartbeat_msec 500 > + > +# > +# Sync mode as defined by standard > +# 0 (non-sync mode) - port not used for best signal source selection or TX its QL > +# 1 (sync mode) - port used for best signal source selection or TX its QL > +# > +sync 1 Unclear - if a port in a non-sync-mode is not SyncE operational A port in non-sync-mode shouldn't be a part of the config file > + > +# > +# Shell commands for enabling/disabling this port as main recovered clock on a > +# device. > +# > +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f0/device/phy/synce > +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f0/device/phy/synce > + > +# > +# next configured interface for the device > +# > +[enp1s0f1] > +sync 1 > +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f1/device/phy/synce > +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f1/device/phy/synce > + > + > + > +############################################################ > +# > +# next SyncE device section > +# > +#[<synce2>] > +#internal_input 0 > +#external_input 1 What is the meaning of multiple SyncE devices? Shouldn't there be a seperate execution for synce4l like in ptp4l > + > +# > +# new port belonging to the "new" device > +# > +#[enp7s0f0] > +#sync 1 > + |
From: Kubalewski, A. <ark...@in...> - 2022-06-09 19:12:37
|
-----Original Message----- From: Aya Levin via Linuxptp-devel <lin...@li...> Sent: Wednesday, June 8, 2022 11:01 AM > > On 5/2/2022 12:05 PM, Arkadiusz Kubalewski wrote: > > Allow config interface to parse SyncE related config files. > > > > Co-developed-by: Piotr Kwapulinski <pio...@in...> > > Signed-off-by: Piotr Kwapulinski <pio...@in...> > > Co-developed-by: Michal Michalik <mic...@in...> > > Signed-off-by: Michal Michalik <mic...@in...> > > Signed-off-by: Arkadiusz Kubalewski <ark...@in...> > > --- > > config.c | 198 ++++++++++++++++++++++++++++++++++++---------- > > config.h | 8 ++ > > configs/synce.cfg | 194 +++++++++++++++++++++++++++++++++++++++++++++ > > 3 files changed, 358 insertions(+), 42 deletions(-) > > create mode 100644 configs/synce.cfg > > > > diff --git a/config.c b/config.c > > index 4f3ceb8a20d7..f0ad329a7f18 100644 > > --- a/config.c > > +++ b/config.c > > @@ -41,6 +41,7 @@ enum config_section { > > GLOBAL_SECTION, > > UC_MTAB_SECTION, > > PORT_SECTION, > > + DEVICE_SECTION, > > UNKNOWN_SECTION, > > }; > > > > @@ -68,6 +69,7 @@ typedef union { > > #define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */ > > #define CFG_ITEM_PORT (1 << 2) /* item may appear in port sections */ > > #define CFG_ITEM_DYNSTR (1 << 4) /* string value dynamically allocated */ > > +#define CFG_ITEM_DEVICE (1 << 8) /* item may appear in device sections */ > > > > struct config_item { > > char label[CONFIG_LABEL_SIZE]; > > @@ -79,12 +81,18 @@ struct config_item { > > any_t max; > > }; > > > > -#define N_CONFIG_ITEMS (sizeof(config_tab) / sizeof(config_tab[0])) > > +#define N_CONFIG_ITEMS_PTP (sizeof(config_tab_ptp) / sizeof(config_tab_ptp[0])) > > +#define N_CONFIG_ITEMS_SYNCE ((sizeof(config_tab_synce) / \ > > + sizeof(config_tab_synce[0]))) > > + > > +#define PORT_TO_FLAG(_port) (_port == PORT_SECTION ? CFG_ITEM_PORT : \ > > + _port == DEVICE_SECTION ? CFG_ITEM_DEVICE : \ > > + CFG_ITEM_STATIC) > > > > #define CONFIG_ITEM_DBL(_label, _port, _default, _min, _max) { \ > > .label = _label, \ > > .type = CFG_TYPE_DOUBLE, \ > > - .flags = _port ? CFG_ITEM_PORT : 0, \ > > + .flags = PORT_TO_FLAG(_port), \ > > .val.d = _default, \ > > .min.d = _min, \ > > .max.d = _max, \ > > @@ -92,14 +100,14 @@ struct config_item { > > #define CONFIG_ITEM_ENUM(_label, _port, _default, _table) { \ > > .label = _label, \ > > .type = CFG_TYPE_ENUM, \ > > - .flags = _port ? CFG_ITEM_PORT : 0, \ > > + .flags = PORT_TO_FLAG(_port), \ > > .tab = _table, \ > > .val.i = _default, \ > > } > > #define CONFIG_ITEM_INT(_label, _port, _default, _min, _max) { \ > > .label = _label, \ > > .type = CFG_TYPE_INT, \ > > - .flags = _port ? CFG_ITEM_PORT : 0, \ > > + .flags = PORT_TO_FLAG(_port), \ > > .val.i = _default, \ > > .min.i = _min, \ > > .max.i = _max, \ > > @@ -107,33 +115,39 @@ struct config_item { > > #define CONFIG_ITEM_STRING(_label, _port, _default) { \ > > .label = _label, \ > > .type = CFG_TYPE_STRING, \ > > - .flags = _port ? CFG_ITEM_PORT : 0, \ > > + .flags = PORT_TO_FLAG(_port), \ > > .val.s = _default, \ > > } > > > > #define GLOB_ITEM_DBL(label, _default, min, max) \ > > - CONFIG_ITEM_DBL(label, 0, _default, min, max) > > + CONFIG_ITEM_DBL(label, GLOBAL_SECTION, _default, min, max) > > > > #define GLOB_ITEM_ENU(label, _default, table) \ > > - CONFIG_ITEM_ENUM(label, 0, _default, table) > > + CONFIG_ITEM_ENUM(label, GLOBAL_SECTION, _default, table) > > > > #define GLOB_ITEM_INT(label, _default, min, max) \ > > - CONFIG_ITEM_INT(label, 0, _default, min, max) > > + CONFIG_ITEM_INT(label, GLOBAL_SECTION, _default, min, max) > > > > #define GLOB_ITEM_STR(label, _default) \ > > - CONFIG_ITEM_STRING(label, 0, _default) > > + CONFIG_ITEM_STRING(label, GLOBAL_SECTION, _default) > > > > #define PORT_ITEM_DBL(label, _default, min, max) \ > > - CONFIG_ITEM_DBL(label, 1, _default, min, max) > > + CONFIG_ITEM_DBL(label, PORT_SECTION, _default, min, max) > > > > #define PORT_ITEM_ENU(label, _default, table) \ > > - CONFIG_ITEM_ENUM(label, 1, _default, table) > > + CONFIG_ITEM_ENUM(label, PORT_SECTION, _default, table) > > > > #define PORT_ITEM_INT(label, _default, min, max) \ > > - CONFIG_ITEM_INT(label, 1, _default, min, max) > > + CONFIG_ITEM_INT(label, PORT_SECTION, _default, min, max) > > > > #define PORT_ITEM_STR(label, _default) \ > > - CONFIG_ITEM_STRING(label, 1, _default) > > + CONFIG_ITEM_STRING(label, PORT_SECTION, _default) > > + > > +#define DEV_ITEM_INT(label, _default, min, max) \ > > + CONFIG_ITEM_INT(label, DEVICE_SECTION, _default, min, max) > > + > > +#define DEV_ITEM_STR(label, _default) \ > > + CONFIG_ITEM_STRING(label, DEVICE_SECTION, _default) > > > > static struct config_enum clock_servo_enu[] = { > > { "pi", CLOCK_SERVO_PI }, > > @@ -220,7 +234,7 @@ static struct config_enum bmca_enu[] = { > > { NULL, 0 }, > > }; > > > > -struct config_item config_tab[] = { > > +struct config_item config_tab_ptp[] = { > > PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX), > > PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu), > > GLOB_ITEM_INT("assume_two_step", 0, 0, 1), > > @@ -341,7 +355,37 @@ struct config_item config_tab[] = { > > GLOB_ITEM_INT("write_phase_mode", 0, 0, 1), > > }; > > > > +struct config_item config_tab_synce[] = { > > + GLOB_ITEM_INT("logging_level", LOG_INFO, PRINT_LEVEL_MIN, PRINT_LEVEL_MAX), > > + GLOB_ITEM_STR("message_tag", NULL), > > + GLOB_ITEM_INT("use_syslog", 1, 0, 1), > > + GLOB_ITEM_STR("userDescription", ""), > > + GLOB_ITEM_INT("verbose", 0, 0, 1), > > + DEV_ITEM_INT("internal_input", 1, 0, 1), > > + DEV_ITEM_INT("external_input", 0, 0, 1), > > + DEV_ITEM_INT("external_input_QL", 0, 0, 15), > > + DEV_ITEM_INT("external_input_ext_QL", 0, 0, 255), > > + DEV_ITEM_INT("extended_tlv", 0, 0, 1), > > + DEV_ITEM_INT("network_option", 1, 1, 2), > > + DEV_ITEM_INT("recover_time", 300, 10, 720), > > + DEV_ITEM_STR("dpll_get_state_cmd", NULL), > > + DEV_ITEM_STR("dpll_holdover_value", NULL), > > + DEV_ITEM_STR("dpll_locked_ho_value", NULL), > > + DEV_ITEM_STR("dpll_locked_value", NULL), > > + DEV_ITEM_STR("dpll_freerun_value", NULL), > > + DEV_ITEM_STR("dpll_invalid_value", NULL), > > + PORT_ITEM_STR("allowed_qls", NULL), > > + PORT_ITEM_STR("allowed_ext_qls", NULL), > > + PORT_ITEM_INT("sync", 0, 0, 1), > > + PORT_ITEM_STR("recover_clock_enable_cmd", NULL), > > + PORT_ITEM_STR("recover_clock_disable_cmd", NULL), > > + PORT_ITEM_INT("tx_heartbeat_msec", 1000, 100, 3000), > > + PORT_ITEM_INT("rx_heartbeat_msec", 50, 10, 500), > > +}; > > + > > static struct unicast_master_table *current_uc_mtab; > > +static struct interface *__config_create_interface(const char *name, struct config *cfg, > > + const char *type); > > > > static enum parser_result > > parse_fault_interval(struct config *cfg, const char *section, > > @@ -392,6 +436,8 @@ static struct config_item *config_item_alloc(struct config *cfg, > > } > > strncpy(ci->label, name, CONFIG_LABEL_SIZE - 1); > > ci->type = type; > > + ci->val.s = NULL; > > + ci->flags = 0; > > > > snprintf(buf, sizeof(buf), "%s.%s", section, ci->label); > > if (hash_insert(cfg->htab, buf, ci)) { > > @@ -406,8 +452,11 @@ static struct config_item *config_item_alloc(struct config *cfg, > > static void config_item_free(void *ptr) > > { > > struct config_item *ci = ptr; > > - if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR) > > + if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR > > + && ci->val.s != NULL) { > > free(ci->val.s); > > + ci->val.s = NULL; > > + } > > if (ci->flags & CFG_ITEM_STATIC) > > return; > > free(ci); > > @@ -512,10 +561,13 @@ static enum parser_result parse_section_line(char *s, enum config_section *secti > > current_uc_mtab = NULL; > > } else if (s[0] == '[') { > > char c; > > - *section = PORT_SECTION; > > - /* Replace square brackets with white space. */ > > + if (s[1] == '<') > > + *section = DEVICE_SECTION; > > + else > > + *section = PORT_SECTION; > > + /* Replace brackets with white space. */ > > while (0 != (c = *s)) { > > - if (c == '[' || c == ']') > > + if (c == '[' || c == ']' || c == '<' || c == '>') > > *s = ' '; > > s++; > > } > > @@ -573,7 +625,8 @@ static enum parser_result parse_item(struct config *cfg, > > } > > > > if (section) { > > - if (!(cgi->flags & CFG_ITEM_PORT)) { > > + if (!(cgi->flags & CFG_ITEM_PORT) && > > + !(cgi->flags & CFG_ITEM_DEVICE)) { > > return NOT_PARSED; > > } > > /* Create or update this port specific item. */ > > @@ -601,8 +654,9 @@ static enum parser_result parse_item(struct config *cfg, > > dst->val.d = df; > > break; > > case CFG_TYPE_STRING: > > - if (dst->flags & CFG_ITEM_DYNSTR) { > > + if (dst->flags & CFG_ITEM_DYNSTR && dst->val.s != NULL) { > > free(dst->val.s); > > + dst->val.s = NULL; > > } > > dst->val.s = strdup(value); > > if (!dst->val.s) { > > @@ -728,18 +782,26 @@ static void check_deprecated_options(const char **option) > > } > > } > > > > -static struct option *config_alloc_longopts(void) > > +static struct option *config_alloc_longopts(enum feature_type type) > > { > > - struct config_item *ci; > > + struct config_item *ci, *ci_tab; > > struct option *opts; > > - int i; > > + int i, n_items; > > > > - opts = calloc(1, (1 + N_CONFIG_ITEMS) * sizeof(*opts)); > > + if (type == PTP) { > > + ci_tab = &config_tab_ptp[0]; > > + n_items = N_CONFIG_ITEMS_PTP; > > + } else { > > + ci_tab = &config_tab_synce[0]; > > + n_items = N_CONFIG_ITEMS_SYNCE; > > + } > > + n_items = (type == PTP ? N_CONFIG_ITEMS_PTP : N_CONFIG_ITEMS_SYNCE); > > + opts = calloc(1, (1 + n_items) * sizeof(*opts)); > > if (!opts) { > > return NULL; > > } > > - for (i = 0; i < N_CONFIG_ITEMS; i++) { > > - ci = &config_tab[i]; > > + for (i = 0; i < n_items; i++) { > > + ci = &ci_tab[i]; > > opts[i].name = ci->label; > > opts[i].has_arg = required_argument; > > /* Avoid bug in detection of ambiguous options in glibc */ > > @@ -757,6 +819,8 @@ int config_read(const char *name, struct config *cfg) > > char buf[1024], *line, *c; > > const char *option, *value; > > struct interface *current_port = NULL; > > + struct interface *current_device = NULL; > > + bool is_synce = (cfg->type == SYNCE); > > int line_num; > > > > fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r"); > > @@ -787,6 +851,7 @@ int config_read(const char *name, struct config *cfg) > > if (parse_section_line(line, ¤t_section) == PARSED_OK) { > > if (current_section == PORT_SECTION) { > > char port[17]; > > + > > if (1 != sscanf(line, " %16s", port)) { > > fprintf(stderr, "could not parse port name on line %d\n", > > line_num); > > @@ -795,6 +860,27 @@ int config_read(const char *name, struct config *cfg) > > current_port = config_create_interface(port, cfg); > > if (!current_port) > > goto parse_error; > > + if (is_synce) { > > + if (current_device) { > > + interface_se_set_parent_dev(current_port, > > + interface_name(current_device)); > > + } else { > > + goto parse_error; > > + } > > + } > > + } else if (current_section == DEVICE_SECTION) { > > + /* clear port on new device found in config */ > > + current_port = NULL; > > + char device[17]; > > + > > + if (sscanf(line, " %16s", device) != 1) { > > + fprintf(stderr, "could not parse device name on line %d\n", > > + line_num); > > + goto parse_error; > > + } > > + current_device = __config_create_interface(device, cfg, "device"); > > + if (!current_device) > > + goto parse_error; > > } > > continue; > > } > > @@ -814,14 +900,16 @@ int config_read(const char *name, struct config *cfg) > > if (parse_setting_line(line, &option, &value)) { > > fprintf(stderr, "could not parse line %d in %s section\n", > > line_num, current_section == GLOBAL_SECTION ? > > - "global" : interface_name(current_port)); > > + "global" : interface_name(current_port ? > > + current_port : current_device)); > > goto parse_error; > > } > > > > check_deprecated_options(&option); > > > > parser_res = parse_item(cfg, 0, current_section == GLOBAL_SECTION ? > > - NULL : interface_name(current_port), > > + NULL : interface_name(current_port ? > > + current_port : current_device), > > option, value); > > switch (parser_res) { > > case PARSED_OK: > > @@ -830,7 +918,8 @@ int config_read(const char *name, struct config *cfg) > > fprintf(stderr, "unknown option %s at line %d in %s section\n", > > option, line_num, > > current_section == GLOBAL_SECTION ? "global" : > > - interface_name(current_port)); > > + interface_name(current_port ? > > + current_port : current_device)); > > goto parse_error; > > case BAD_VALUE: > > fprintf(stderr, "%s is a bad value for option %s at line %d\n", > > @@ -856,7 +945,7 @@ parse_error: > > return -2; > > } > > > > -struct interface *config_create_interface(const char *name, struct config *cfg) > > +struct interface *__config_create_interface(const char *name, struct config *cfg, const char *type) > > { > > struct interface *iface; > > const char *ifname; > > @@ -870,7 +959,7 @@ struct interface *config_create_interface(const char *name, struct config *cfg) > > > > iface = interface_create(name); > > if (!iface) { > > - fprintf(stderr, "cannot allocate memory for a port\n"); > > + fprintf(stderr, "cannot allocate memory for a %s\n", type); > > return NULL; > > } > > STAILQ_INSERT_TAIL(&cfg->interfaces, iface, list); > > @@ -879,12 +968,17 @@ struct interface *config_create_interface(const char *name, struct config *cfg) > > return iface; > > } > > > > -struct config *config_create(void) > > +struct interface *config_create_interface(const char *name, struct config *cfg) > > +{ > > + return __config_create_interface(name, cfg, "port"); > > +} > > + > > +struct config *__config_create(enum feature_type type) > > { > > char buf[CONFIG_LABEL_SIZE + 8]; > > - struct config_item *ci; > > + struct config_item *ci, *ci_tab; > > struct config *cfg; > > - int i; > > + int i, end; > > > > cfg = calloc(1, sizeof(*cfg)); > > if (!cfg) { > > @@ -893,7 +987,7 @@ struct config *config_create(void) > > STAILQ_INIT(&cfg->interfaces); > > STAILQ_INIT(&cfg->unicast_master_tables); > > > > - cfg->opts = config_alloc_longopts(); > > + cfg->opts = config_alloc_longopts(type); > > if (!cfg->opts) { > > free(cfg); > > return NULL; > > @@ -906,9 +1000,18 @@ struct config *config_create(void) > > return NULL; > > } > > > > + cfg->type = type; > > + if (type == PTP) { > > + ci_tab = &config_tab_ptp[0]; > > + end = N_CONFIG_ITEMS_PTP; > > + } else { > > + ci_tab = &config_tab_synce[0]; > > + end = N_CONFIG_ITEMS_SYNCE; > > + } > > + > > /* Populate the hash table with global defaults. */ > > - for (i = 0; i < N_CONFIG_ITEMS; i++) { > > - ci = &config_tab[i]; > > + for (i = 0; i < end; i++) { > > + ci = &ci_tab[i]; > > ci->flags |= CFG_ITEM_STATIC; > > snprintf(buf, sizeof(buf), "global.%s", ci->label); > > if (hash_insert(cfg->htab, buf, ci)) { > > @@ -918,12 +1021,12 @@ struct config *config_create(void) > > } > > > > /* Perform a Built In Self Test.*/ > > - for (i = 0; i < N_CONFIG_ITEMS; i++) { > > - ci = &config_tab[i]; > > + for (i = 0; i < end; i++) { > > + ci = &ci_tab[i]; > > ci = config_global_item(cfg, ci->label); > > - if (ci != &config_tab[i]) { > > + if (ci != &ci_tab[i]) { > > fprintf(stderr, "config BIST failed at %s\n", > > - config_tab[i].label); > > + ci_tab[i].label); > > goto fail; > > } > > } > > @@ -935,6 +1038,16 @@ fail: > > return NULL; > > } > > > > +struct config *config_create(void) > > +{ > > + return __config_create(PTP); > > +} > > + > > +struct config *config_create_synce(void) > > +{ > > + return __config_create(SYNCE); > > +} > > + > > void config_destroy(struct config *cfg) > > { > > struct unicast_master_address *address; > > @@ -1137,8 +1250,9 @@ int config_set_string(struct config *cfg, const char *option, > > return -1; > > } > > ci->flags |= CFG_ITEM_LOCKED; > > - if (ci->flags & CFG_ITEM_DYNSTR) { > > + if (ci->flags & CFG_ITEM_DYNSTR && ci->val.s != NULL) { > > free(ci->val.s); > > + ci->val.s = NULL; > > } > > ci->val.s = strdup(val); > > if (!ci->val.s) { > > diff --git a/config.h b/config.h > > index 14d2f64415dc..a32e0079cf21 100644 > > --- a/config.h > > +++ b/config.h > > @@ -32,10 +32,16 @@ > > #include "servo.h" > > #include "sk.h" > > > > +enum feature_type { > > + PTP = 0, > > + SYNCE > > +}; > > + > > struct config { > > /* configured interfaces */ > > STAILQ_HEAD(interfaces_head, interface) interfaces; > > int n_interfaces; > > + enum feature_type type; > > > > /* for parsing command line options */ > > struct option *opts; > > @@ -55,6 +61,8 @@ void config_destroy(struct config *cfg); > > > > struct config *config_create(void); > > > > +struct config *config_create_synce(void); > > + > > double config_get_double(struct config *cfg, const char *section, > > const char *option); > > > > diff --git a/configs/synce.cfg b/configs/synce.cfg > > new file mode 100644 > > index 000000000000..eadb318bad6f > > --- /dev/null > > +++ b/configs/synce.cfg > > @@ -0,0 +1,194 @@ > > +# Global section is for debuging mostly > > +[global] > > +# > > +# Runtime options > > +# > > +logging_level 7 > > +use_syslog 0 > > +verbose 1 > > +message_tag [synce4l] > > + > > + > > +# > > +# Device section > > +# Per-device configuration > > +# > > +# User defined name of a one logical device configured for SyncE in the system. > > +# All the ports configured after this section will be a part of this device > > +# (until next device section). > > +[<synce1>] > > + > > +# > > +# If internal inputs are allowed > > +# 0 if internal input sources shall not be used > > +# 1 if internal input sources shall be used > > +# default: 1 > > +# > > +# Internal inputs mean the inputs recovered from the PHY's. > > +# The ports configured (in the port-sections [<dev name>], under the device > > +# section) will be monitored for the QL (Quality Level). > > +# QL is sent by the peer connected to the port and represents the Holdover > > +# performance of the peer. > > +# The best QL (from all the ports where "sync = 1") is selected and frequency > > +# recovered on that port shall be used to feed its frequency to all the other > > +# ports. > > +# > > +internal_input 1 > > + > > +# > > +# If external inputs are allowed > > +# 0 if external input sources shall not be used > > +# 1 if external input sources shall be used > > +# default: 0 > > +# > > +# External inputs are either 1PPS from buil-in GPS module or 1PPS from the > > +# on-board SMA connectors > > +# > > +# Device must be pre-confiured to use this setting. > > +# Before running the application, one of the external inputs shall be selected. > > +# This is done through the interface supplied by the NIC vendor. > > +# > > +# In this mode synce4l application is only responsible for sending > > +# the QL to the peers on all configured ports (where "sync = 1") > > +# The QL value that is sent equals configured "external_input_QL" > > +# (and "external_input_ext_QL" in case of "extended_tlv=1") > > +# > > +external_input 0 > > + > > +# > > +# These values are sent to the peers on configured ports ONLY when 'external > > +# input' mode is enabled. > > +# Valid values are defined in Table 11-7 and Table 11-8 of recommendation > > +# ITU-T G.8264. > > +# They shall be configured appropriately so they are understood by the peer. > > +# > > +# external_input_QL corresponds to the SSM code column. > > +# > > +# external_input_ext_QL corresponds to the Enhanced SSM code column > > +# (is used only if "extended_tlv = 1") > > +# > > +external_input_QL 2 > > +external_input_ext_QL 255 > > + > > +# > > +# If extended TLV shall be supported on the device. > > +# 0 if no extended tlv shall be supported > > +# 1 if extended tlv shall be supported > > +# default: 0 > > +# > > +# In case of 0: > > +# - the port will always TX the non-extended TLV, for RX only > > +# non-extended TLV will be processed for reference signal selection > > +# In case of 1: > > +# - If port is configured with external_input=1, the TX will always use > > +# extended TLV (no RX is required in this case) > > +# - If port is configured with external_input=0 and internal_input=1, the > > +# TX version of TLV will be propagated from the port that was chosen as > > +# candidate for frequency synchronization > > +# > > +extended_tlv 1 > > + > > +# > > +# Which network option shall be supported > > +# > > +# 1 or 2 as defined in T-REC-G.8264 > > +# default: 1 > > +# > > +# This is rather per-network option, all device in SyncE network > > +# shall have this configured for the same value > > +# > > +network_option 1 > > + > > +# > > +# Seconds indicating minimum time to recover from the QL-failed state on the > > +# port. > > +# Range: 10-720 > > +# Default: 300 > > +# > > +# If valid QL was not received from one of the source ports within 5 seconds > > +# the port is no longer a valid source (marked as QL-failed) > > +# > > +# Valid QL must be received for more then "recover_time" seconds on that port > > +# to use its PHY recovered signal again as a valid source > > +# > > +recover_time 10 > > + > > +# > > +# Shell command to be executed in order to obtain current DPLL status of a > > +# device. > > +# > > +dpll_get_state_cmd cat /sys/class/net/enp1s0f0/device/cgu_state > > + > > +# > > +# DPLL state values, must equal to values produced by stdout of > > +# "dpll_get_state_cmd" command > > +# > > +dpll_holdover_value 4 > > +dpll_locked_ho_value 3 > > +dpll_locked_value 2 > > +dpll_freerun_value 1 > > +dpll_invalid_value 0 > > + > > +# > > +# Port section(s) > > +# > > +# It starts per-port configuration. > > +# Each port (of the device) that is used for SyncE, shall have its own section > > +# with at least sync = 1 (which defines port as synchronous mode) > > +# > > +[enp1s0f0] > > + > > +# > > +# msec resolution of TX the QL from this port to the peer > > +# [100-3000], default:1000 (1000 = 1 second is expected by the standard) > > +# > > +# As the standard expects 1 sec, it is not recommended to use different > > +# than a 1000. > > +# > > +tx_heartbeat_msec 2000 > The default should be 1000 > > + > > +# > > +# recovered PHY signal can be lost at anytime, this is msec resolution of > > +# reading the socket, acting on signal lost shall be done just after > > +# [10-500], default:50 > > +# > > +rx_heartbeat_msec 500 > > + > > +# > > +# Sync mode as defined by standard > > +# 0 (non-sync mode) - port not used for best signal source selection or TX its QL > > +# 1 (sync mode) - port used for best signal source selection or TX its QL > > +# > > +sync 1 > Unclear - if a port in a non-sync-mode is not SyncE operational > A port in non-sync-mode shouldn't be a part of the config file Yes, we tried to leave this explicit config item as standard defines "non-sync mode" and "sync mode" explicitly, but it is true that if it is not in sync it doesn't make much sense to have its entry in config file. IMHO we are safe to remove it. Will do that in next version. > > + > > +# > > +# Shell commands for enabling/disabling this port as main recovered clock on a > > +# device. > > +# > > +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f0/device/phy/synce > > +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f0/device/phy/synce > > + > > +# > > +# next configured interface for the device > > +# > > +[enp1s0f1] > > +sync 1 > > +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f1/device/phy/synce > > +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f1/device/phy/synce > > + > > + > > + > > +############################################################ > > +# > > +# next SyncE device section > > +# > > +#[<synce2>] > > +#internal_input 0 > > +#external_input 1 > What is the meaning of multiple SyncE devices? Shouldn't there be a > seperate execution for synce4l like in ptp4l Right now the user can either use: multiple config files (while running multiple synce4l instances) or define multiple devices in single config file (and control all of them with one instance of synce4l). Devices are implemented separately, the single synce_clock can step many devices. Thank you, Arkadiusz > > + > > +# > > +# new port belonging to the "new" device > > +# > > +#[enp7s0f0] > > +#sync 1 > > + > |
From: Erez <ere...@gm...> - 2022-07-05 10:10:29
|
Hi, I add the PHC functionality in libptpmgmt https://sf.net/p/libptpmgmt, https://github.com/erezgeva/libptpmgmt You can now integrate "phc_ctl" functionality in your application or use them in scripts. Erez On Thu, 2 Jun 2022 at 15:25, Erez <ere...@gm...> wrote: > > > On Thu, 2 Jun 2022 at 15:01, Miroslav Lichvar <mli...@re...> wrote: > >> On Thu, Jun 02, 2022 at 02:28:17PM +0200, Erez wrote: >> > Richard needs to elaborate on this. >> >> Right. If he doesn't have time for it anymore, it would be good to >> give other people commit access, at least to some specific areas. >> >> > I do mention that the linuxptp project uses GPL and opposes using LGPL. >> I >> > did ask. >> > So libraries would need a new project anyway, one that uses LGPL for >> > libraries. >> >> Hm, good point. >> >> > I think I will add the PHC functions to my library. >> > I do have a PtpClock class. >> > Just did not try to implement a phc2sys and phc_ctl yet. >> >> To me that sounds like a lot of work and there is no guarantee users >> would be willing to switch. It might be easier to make a fork. >> > > I mean a library with the PHC functionality that phc_ctl and phc2sys uses. > The best way to test the library is to clone the applications on top of > the library. > As I did with the pmc tool. > My target was to provide the functionality in a library. Not to replace or > rewrite linuxptp applications. > > Another target I set for the library is to support scripting. > Users can query the ptp4l directly from a Python or Tcl script. > No need to use the system call and parse the result. > With the system overhead that comes with. > > As phc2sys does run slowly, it can be written with a script language. > > Users can keep using linuxptp applications. Write new applications with > the library or integrate it in their own project. > We can fork linuxptp, but we can not make LGPL libraries. > > Erez > > >> -- >> Miroslav Lichvar >> >> |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:08
|
synce4l is a software implementation of Synchronous Ethernet (SyncE) according to ITU-T Recommendation G.8264. The design goal is to provide logic to supported hardware by processing Ethernet Synchronization Messaging Channel (ESMC) and control Ethernet Equipment Clock (EEC) on Network Card Interface (NIC). Application can operate in two input modes: line or external. External input mode If `synce4l` is configured to run in external input mode then EEC needs to have external 1PPS source attached (GPS or other generator). In this scenario `synce4l` always broadcasts clock quality level (QL) defined in configuration file. Additionally, for external input mode incoming SyncE frames do not participate in best source selection algorithm for EEC. Line input mode In line input mode incoming SyncE frames are processed and best clock source is extracted from the link having the best quality level. `synce4l` configures such "best quality" port as a source to recover clock for EEC. The recovered QL is broadcasted to all other interfaces then. An external clock source cannot be used in this mode. Hardware synce4l is not bound to any specific NIC hardware. Theoretically any NIC could be used for enabling SyncE device in External input mode - the NIC in this mode is in leader role (similar to Grand Master role in PTP), providing its Quality Level information in ESMC frames to its neighbors. It will not syntonize to any of its neighbor ports, since RX frames are not monitored. Only other SyncE enabled neighbor ports could recover clock from that peer and syntonize to it. Practically this mode shall be used only if the NIC PHY ports are fed with stable and reliable frequency clock, Quality Level values that are sent to the peers are configured by the user. This is user responsibility to decide and set proper Quality-Level value for the device, corresponding to the quality of frequency clock generator of used hardware. When enabling line input mode, the NIC must be a device that actually supports Synchronous Ethernet, which means it is able to syntonize frequency of all configured ports within the device to one chosen port. The NIC driver or dedicated software must provide a user with ability to: - obtain current state of a EEC used to syntonize the ports, - select one of the ports as a candidate for clock recovery, - disable other ports previously set as recovery sources. Both abilities are configurable in config file - the user must provide a shell commands - as explained in manual and descriptions inside of example config file. Arkadiusz Kubalewski (11): synce4l: add synce_msg interface synce4l: add esmc_socket interface synce4l: add synce_transport interface synce4l: add synce in interface and util code synce4l: add synce_port_ctrl interface synce4l: add synce_port interface synce4l: add synce_dev_ctrl interface synce4l: add synce_dev interface synce4l: add config knobs for SyncE synce4l: add synce_clock interface synce4l: add synce4l application .gitignore | 1 + README.org | 2 + config.c | 195 +++++-- config.h | 8 + configs/synce.cfg | 175 +++++++ esmc_socket.c | 99 ++++ esmc_socket.h | 42 ++ interface.c | 29 +- interface.h | 24 + makefile | 9 +- synce4l.8 | 222 ++++++++ synce4l.c | 132 +++++ synce4l_README.md | 202 ++++++++ synce_clock.c | 284 +++++++++++ synce_clock.h | 39 ++ synce_dev.c | 624 +++++++++++++++++++++++ synce_dev.h | 64 +++ synce_dev_ctrl.c | 153 ++++++ synce_dev_ctrl.h | 64 +++ synce_msg.c | 261 ++++++++++ synce_msg.h | 174 +++++++ synce_msg_private.h | 87 ++++ synce_port.c | 471 +++++++++++++++++ synce_port.h | 174 +++++++ synce_port_ctrl.c | 1230 +++++++++++++++++++++++++++++++++++++++++++++ synce_port_ctrl.h | 165 ++++++ synce_transport.c | 102 ++++ synce_transport.h | 55 ++ synce_transport_private.h | 18 + util.c | 22 + util.h | 8 + 31 files changed, 5089 insertions(+), 46 deletions(-) create mode 100644 configs/synce.cfg create mode 100644 esmc_socket.c create mode 100644 esmc_socket.h create mode 100644 synce4l.8 create mode 100644 synce4l.c create mode 100644 synce4l_README.md create mode 100644 synce_clock.c create mode 100644 synce_clock.h create mode 100644 synce_dev.c create mode 100644 synce_dev.h create mode 100644 synce_dev_ctrl.c create mode 100644 synce_dev_ctrl.h create mode 100644 synce_msg.c create mode 100644 synce_msg.h create mode 100644 synce_msg_private.h create mode 100644 synce_port.c create mode 100644 synce_port.h create mode 100644 synce_port_ctrl.c create mode 100644 synce_port_ctrl.h create mode 100644 synce_transport.c create mode 100644 synce_transport.h create mode 100644 synce_transport_private.h -- 2.9.5 base-commit: 58c7f9dfcdb55ee3d7616f19dc5fc3cf4da76449 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:22
|
From: Arkadiusz Kubalewski <ark...@in...> Interface used for: - creation, building and storing new SyncE PDU's for later transmission - obtain Quality Level TLV and Extended Quality Level TLV from existing SyncE PDU's synce_msg_private.h has constants and structs as defined by Synchronous Ethernet standard - Recommendation ITU-T G.8264//Y.1364. Co-developed-by: Anatolii Gerasymenko <ana...@in...> Signed-off-by: Anatolii Gerasymenko <ana...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - add zero-memory after malloc - changed order of patch in patch-series - changed naming 'Sync-E' -> 'SyncE' v3: remove redundant variable assignment v2: rebase patch series synce_msg.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++ synce_msg.h | 174 +++++++++++++++++++++++++++++++++++ synce_msg_private.h | 87 ++++++++++++++++++ 3 files changed, 522 insertions(+) create mode 100644 synce_msg.c create mode 100644 synce_msg.h create mode 100644 synce_msg_private.h diff --git a/synce_msg.c b/synce_msg.c new file mode 100644 index 0000000..099d8eb --- /dev/null +++ b/synce_msg.c @@ -0,0 +1,261 @@ +/** + * @file synce_msg.c + * @brief Implements the ESMC message type. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <stdlib.h> +#include <stdbool.h> +#include <sys/queue.h> + +#include "sk.h" +#include "print.h" +#include "address.h" +#include "synce_msg.h" +#include "synce_msg_private.h" + +static void init_tlvs(struct synce_pdu *pdu) +{ + TAILQ_INIT(&pdu->tlv_list); +} + +struct synce_pdu *synce_msg_create(const char *iface) +{ + const unsigned char dstMac[] = SYNCE_DEST_MACADDR; + const unsigned char ituOui[] = SYNCE_ITUOUI; + struct synce_pdu *pdu; + struct address srcMac; + + if (sk_interface_macaddr(iface, &srcMac)) { + pr_err("mac get failed"); + return NULL; + } + + pdu = (struct synce_pdu *) malloc(sizeof(struct synce_pdu)); + if (!pdu) { + pr_err("memory allocation for SyncE PDU failed"); + return NULL; + } + memset(pdu, 0, sizeof(struct synce_pdu)); + memcpy(&pdu->header.dstAddr, &dstMac, sizeof(pdu->header.dstAddr)); + pdu->header.ethertype = htons(ETH_P_SLOW); + pdu->header.ethersubtype = SYNCE_ETHERSUBTYPE; + memcpy(&pdu->header.ituOui, &ituOui, sizeof(pdu->header.ituOui)); + pdu->header.ituSubtype = htons(SYNCE_ITUSUBTYPE); + pdu->header.verEvtflag = SYNCE_VERSION << SYNCE_VERSION_SHIFT; + memset(&pdu->header.reserved, 0, sizeof(pdu->header.reserved)); + memcpy(&pdu->header.srcAddr, &srcMac.sll.sll_addr, + sizeof(pdu->header.srcAddr)); + + init_tlvs(pdu); + + return pdu; +} + +void synce_msg_delete(struct synce_pdu *pdu) +{ + synce_msg_reset_tlvs(pdu); + free(pdu); +} + +static void attach_esmc_tlv(struct synce_pdu *pdu, struct esmc_tlv *tlv) +{ + TAILQ_INSERT_TAIL(&pdu->tlv_list, tlv, list); +} + +int synce_msg_get_esmc_tlvs_size(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv; + int size = 0; + + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + size += ntohs(tlv->ql_tlv.length); + } + + return size; +} + +static int generate_tlvs(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv; + void *cur; + int size; + + size = synce_msg_get_esmc_tlvs_size(pdu); + if (ETH_DATA_LEN - ETH_FCS_LEN - size < 0) { + pr_err("too many tlvs"); + return -EMSGSIZE; + } + + cur = (void *)pdu + sizeof(struct esmc_header); + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + size = ntohs(tlv->ql_tlv.length); + memcpy(cur, tlv, size); + cur += size; + } + + return 0; +} + +int synce_msg_attach_ql_tlv(struct synce_pdu *pdu, uint8_t ssmCode) +{ + struct esmc_tlv *tlv; + + if (~QL_TLV_SSM_MASK & ssmCode) { + pr_err("4 upper bits of QL TLV ssmCode should not be used"); + return -EINVAL; + } + + tlv = malloc(sizeof(struct esmc_tlv)); + if (!tlv) { + pr_err("malloc failed for TLV: %m"); + return -EINVAL; + } + + memset(tlv, 0, sizeof(*tlv)); + tlv->ql_tlv.type = QL_TLV_TYPE; + tlv->ql_tlv.length = htons(QL_TLV_LEN); + tlv->ql_tlv.ssmCode = ssmCode; + + attach_esmc_tlv(pdu, tlv); + + generate_tlvs(pdu); + + return 0; +} + +int synce_msg_get_ql_tlv(struct synce_pdu *pdu, uint8_t *ssmCode) +{ + struct esmc_tlv *tlv; + + if (!pdu || !ssmCode) { + return -ENXIO; + } + + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + if (tlv->ql_tlv.type == QL_TLV_TYPE) { + break; + } + } + + if (!tlv) { + return -EAGAIN; + } + + *ssmCode = tlv->ql_tlv.ssmCode; + + return 0; +} + +int synce_msg_attach_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql) +{ + struct esmc_tlv *tlv; + + if (!pdu || !ext_ql) { + pr_err("either pdu or ext_ql not provided"); + return -ENXIO; + } + + tlv = malloc(sizeof(struct esmc_tlv)); + if (!tlv) { + pr_err("malloc failed for TLV: %m"); + return -ENOMEM; + } + + memset(tlv, 0, sizeof(*tlv)); + tlv->extended_ql_tlv.type = EXT_QL_TLV_TYPE; + tlv->extended_ql_tlv.length = htons(EXT_QL_TLV_LEN); + tlv->extended_ql_tlv.enhancedSsmCode = ext_ql->enhancedSsmCode; + memcpy(&tlv->extended_ql_tlv.clockIdentity, &ext_ql->clockIdentity, + sizeof(tlv->extended_ql_tlv.clockIdentity)); + tlv->extended_ql_tlv.flag = ext_ql->flag; + tlv->extended_ql_tlv.cascaded_eEEcs = ext_ql->cascaded_eEEcs; + tlv->extended_ql_tlv.cascaded_EEcs = ext_ql->cascaded_EEcs; + + attach_esmc_tlv(pdu, tlv); + + generate_tlvs(pdu); + + return 0; +} + +int synce_msg_get_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql) +{ + struct esmc_tlv *tlv; + + if (!pdu || !ext_ql) { + return -ENXIO; + } + + TAILQ_FOREACH(tlv, &pdu->tlv_list, list) { + if (tlv->ql_tlv.type == EXT_QL_TLV_TYPE) { + break; + } + } + + if (!tlv) { + return -EAGAIN; + } + + ext_ql->enhancedSsmCode = tlv->extended_ql_tlv.enhancedSsmCode; + memcpy(&ext_ql->clockIdentity, &tlv->extended_ql_tlv.clockIdentity, + sizeof(ext_ql->clockIdentity)); + ext_ql->flag = tlv->extended_ql_tlv.flag; + ext_ql->cascaded_eEEcs = tlv->extended_ql_tlv.cascaded_eEEcs; + ext_ql->cascaded_EEcs = tlv->extended_ql_tlv.cascaded_EEcs; + + return 0; +} + +void synce_msg_reset_tlvs(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv, *tlv_temp; + + tlv = TAILQ_FIRST(&pdu->tlv_list); + while (tlv != NULL) { + tlv_temp = TAILQ_NEXT(tlv, list); + free(tlv); + tlv = tlv_temp; + } + init_tlvs(pdu); +} + +static bool is_valid_synce_tlv(struct esmc_tlv *tlv) +{ + if (tlv->ql_tlv.type == QL_TLV_TYPE && + ntohs(tlv->ql_tlv.length) == QL_TLV_LEN) + return true; + if (tlv->ql_tlv.type == EXT_QL_TLV_TYPE && + ntohs(tlv->ql_tlv.length) == EXT_QL_TLV_LEN) + return true; + return false; +} + +void synce_msg_recover_tlvs(struct synce_pdu *pdu) +{ + struct esmc_tlv *tlv, *new_tlv; + + if (TAILQ_EMPTY(&pdu->tlv_list)) { + init_tlvs(pdu); + } + + synce_msg_reset_tlvs(pdu); + + tlv = (struct esmc_tlv *)((void *)pdu + sizeof(struct esmc_header)); + while (is_valid_synce_tlv(tlv)) { + new_tlv = malloc(sizeof(struct esmc_tlv)); + if (!new_tlv) { + pr_err("allocate new_tlv failed"); + return; + } + memset(new_tlv, 0, sizeof(*new_tlv)); + /* We copy data from receiving buffer into larger structure. The + * TLV size is validated in is_valid_synce_tlv before copying. + */ + memcpy(new_tlv, tlv, ntohs(tlv->ql_tlv.length)); + TAILQ_INSERT_TAIL(&pdu->tlv_list, new_tlv, list); + tlv = (struct esmc_tlv *)((void *)tlv + ntohs(tlv->ql_tlv.length)); + } +} diff --git a/synce_msg.h b/synce_msg.h new file mode 100644 index 0000000..a4699ce --- /dev/null +++ b/synce_msg.h @@ -0,0 +1,174 @@ +/** + * @file synce_msg.h + * @brief Implements the ESMC message type. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_MSG_H +#define HAVE_SYNCE_MSG_H + +#include "ddt.h" + +#define SYNCE_NETWORK_OPT_1 1 +#define SYNCE_NETWORK_OPT_2 2 + +/* Enhanced SSM codes for SyncE */ +#define QL_EEC1_ENHSSM 0xFF +#define QL_EEC2_ENHSSM 0xFF +#define QL_OTHER_CLOCK_TYPES_ENHSSM 0xFF +#define QL_PRTC_ENHSSM 0x20 +#define QL_ePRTC_ENHSSM 0x21 +#define QL_eEEC_ENHSSM 0x22 +#define QL_ePRC_ENHSSM 0x23 + +/* SSM codes and enhanced SSM codes for SyncE in option 1 networks */ +#define O1N_QL_PRC_SSM 0x2 +#define O1N_QL_PRC_ENHSSM 0xFF +#define O1N_QL_SSU_A_SSM 0x4 +#define O1N_QL_SSU_A_ENHSSM 0xFF +#define O1N_QL_SSU_B_SSM 0x8 +#define O1N_QL_SSU_B_ENHSSM 0xFF +#define O1N_QL_EEC1_SSM 0xB +#define O1N_QL_EEC1_ENHSSM 0xFF +#define O1N_QL_DNU_SSM 0xF +#define O1N_QL_DNU_ENHSSM 0xFF +#define O1N_QL_PRTC_SSM 0x2 +#define O1N_QL_PRTC_ENHSSM 0x20 +#define O1N_QL_EPRTC_SSM 0x2 +#define O1N_QL_EPRTC_ENHSSM 0x21 +#define O1N_QL_EEEC_SSM 0xB +#define O1N_QL_EEEC_ENHSSM 0x22 +#define O1N_QL_EPRC_SSM 0x2 +#define O1N_QL_EPRC_ENHSSM 0x23 + +/* SSM codes and enhanced SSM codes for SyncE in option 2 networks */ +#define O2N_QL_PRS_SSM 0x1 +#define O2N_QL_PRS_ENHSSM 0xFF +#define O2N_QL_STU_SSM 0x0 +#define O2N_QL_STU_ENHSSM 0xFF +#define O2N_QL_ST2_SSM 0x7 +#define O2N_QL_ST2_ENHSSM 0xFF +#define O2N_QL_TNC_SSM 0x4 +#define O2N_QL_TNC_ENHSSM 0xFF +#define O2N_QL_ST3E_SSM 0xD +#define O2N_QL_ST3E_ENHSSM 0xFF +#define O2N_QL_ST3_SSM 0xA +#define O2N_QL_ST3_ENHSSM 0xFF +#define O2N_QL_EEC2_SSM 0xA +#define O2N_QL_EEC2_ENHSSM 0xFF +#define O2N_QL_PROV_SSM 0xE +#define O2N_QL_PROV_ENHSSM 0xFF +#define O2N_QL_DUS_SSM 0xF +#define O2N_QL_DUS_ENHSSM 0xFF +#define O2N_QL_PRTC_SSM 0x1 +#define O2N_QL_PRTC_ENHSSM 0x20 +#define O2N_QL_EPRTC_SSM 0x1 +#define O2N_QL_EPRTC_ENHSSM 0x21 +#define O2N_QL_EEEC_SSM 0xA +#define O2N_QL_EEEC_ENHSSM 0x22 +#define O2N_QL_EPRC_SSM 0x1 +#define O2N_QL_EPRC_ENHSSM 0x23 + +/* Flags as defined in SyncE specification */ +#define MIXED_EEC_CHAIN_FLAG (1 << 0) +#define PARTIAL_EEC_CHAIN_FLAG (1 << 1) + +/* 5 seconds is a period defined by standard for QL-failed state */ +#define QL_FAILED_PERIOD_SEC 5 + +struct synce_msg_ext_ql { + uint8_t enhancedSsmCode; + struct ClockIdentity clockIdentity; + uint8_t flag; + uint8_t cascaded_eEEcs; + uint8_t cascaded_EEcs; +}; + +/** + * Create a ESMC Protocol Data Unit (PDU) template. + * + * This high level API creates structure for storing ESMC PDU and + * initializes TLV vector for conveniently storing and processing + * TLV structures (esmc_tlv). All the fields are stored in network + * byte ordering. + * + * @param iface A name of interface to create prepopulated template for + * @return A pointer to a ESMC SyncE PDU prepopulated template + */ +struct synce_pdu *synce_msg_create(const char *iface); + +/** + * Delete a ESMC Protocol Data Unit (PDU) from memory. + * + * This high level API frees all the memory stored in TLV vector and + * then free ESMC PDU itself. + * + * @param pdu A pointer to a ESMC SyncE PDU + */ +void synce_msg_delete(struct synce_pdu *pdu); + +/** + * Attach QL TLV to SyncE PDU. + * + * @param pdu A pointer to a ESMC SyncE PDU + * @param ssmCode SSM Code of newly created QL TLV + * @return Zero on success, non-zero if failure + */ +int synce_msg_attach_ql_tlv(struct synce_pdu *pdu, uint8_t ssmCode); + +/** + * Get QL TLVs SSM Code from SyncE PDU. + * + * @param pdu A pointer to a ESMC SyncE PDU + * @param ssmCode SSM Code variable to store + * @return Zero on success, non-zero if failure + */ +int synce_msg_get_ql_tlv(struct synce_pdu *pdu, uint8_t *ssmCode); + +/** + * Attach Extended QL TLV to SyncE PDU. + * + * @param pdu A pointer to a ESMC SyncE PDU + * @param enhancedSsmCode Enhanced SSM Code of newly created QL TLV + * @param clockIdentity SyncE clockIdentity of the originator of the extended QL TLV + * @param flag Flag (see ITU-T G.8264) + * @param cascaded_eEEcs Number of cascaded eEECs from the nearest SSU/PRC/ePRC + * @param cascaded_EEcs Number of cascaded EECs from the nearest SSU/PRC/ePRC + * @return Zero on success, non-zero if failure + */ +int synce_msg_attach_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql); + +/** + * Get QL Extended TLVs from SyncE PDU in. + * + * @param pdu A pointer to a ESMC SyncE PDU + * @param ext_ql A pointer to Extended Quality level + * @return Zero on success, non-zero if failure + */ +int synce_msg_get_extended_ql_tlv(struct synce_pdu *pdu, + struct synce_msg_ext_ql *ext_ql); + +/** + * Reset (clear and reinitialize) all the TLV vector entries. + * + * @param pdu A pointer to a ESMC SyncE PDU + */ +void synce_msg_reset_tlvs(struct synce_pdu *pdu); + +/** + * Recover TLVs from ESMC frame into TLV vector entries in PDU structure. + * + * @param pdu A pointer to a ESMC SyncE PDU + */ +void synce_msg_recover_tlvs(struct synce_pdu *pdu); + +/** + * Returns the size of TLV vector entries in PDU. + * + * @param pdu A pointer to a ESMC SyncE PDU + * @return Number of bytes of all TLV entries attached to PDU + */ +int synce_msg_get_esmc_tlvs_size(struct synce_pdu *pdu); + +#endif diff --git a/synce_msg_private.h b/synce_msg_private.h new file mode 100644 index 0000000..12b9578 --- /dev/null +++ b/synce_msg_private.h @@ -0,0 +1,87 @@ +/** + * @file synce_msg_private.h + * @brief Implements the ESMC message private structures. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_MSG_PRIVATE_H +#define HAVE_SYNCE_MSG_PRIVATE_H + +/* SyncE Frame */ +#define SYNCE_DEST_MACADDR { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x02 } +#define SYNCE_ETHERTYPE ETH_P_SLOW +#define SYNCE_ETHERSUBTYPE 0x0A +#define SYNCE_ITUOUI { 0x00, 0x19, 0xA7 } +#define SYNCE_ITUOUI_SIZE 3 +#define SYNCE_ITUSUBTYPE 0x0001 +#define SYNCE_VERSION 1 +#define SYNCE_VERSION_SHIFT 4 +#define SYNCE_EVENT_SHIFT 4 + +/* QL TLV */ +#define QL_TLV_TYPE 1 +#define QL_TLV_LEN 0x4 +#define QL_TLV_SSM_MASK 0x0f +#define EXT_QL_TLV_TYPE 2 +#define EXT_QL_TLV_LEN 0x14 + +#include <errno.h> +#include <sys/queue.h> +#include <linux/if_ether.h> + +#include "ddt.h" +#include "ether.h" + +struct macaddr { + uint8_t addr[MAC_LEN]; +} PACKED; + +struct ql_tlv { + uint8_t type; + uint16_t length; + uint8_t ssmCode; /* unused:4 | SSM code:4 */ +} PACKED; + +struct extended_ql_tlv { + uint8_t type; + uint16_t length; + uint8_t enhancedSsmCode; + struct ClockIdentity clockIdentity; + uint8_t flag; + uint8_t cascaded_eEEcs; + uint8_t cascaded_EEcs; + uint8_t reserved[5]; +} PACKED; + +struct esmc_tlv { + union { + struct ql_tlv ql_tlv; + struct extended_ql_tlv extended_ql_tlv; + }; + TAILQ_ENTRY(esmc_tlv) list; +} PACKED; + +struct esmc_header { + struct macaddr dstAddr; + struct macaddr srcAddr; + uint16_t ethertype; + uint8_t ethersubtype; + uint8_t ituOui[SYNCE_ITUOUI_SIZE]; /* Organizationally Unique Identifier */ + uint16_t ituSubtype; + uint8_t verEvtflag; /* version:4 | event flag:1 | reserved:3 */ + uint8_t reserved[3]; +} PACKED; + +struct esmc_data { + uint8_t buffer[ETH_DATA_LEN]; +}; + +struct synce_pdu { + union { + struct esmc_header header; + struct esmc_data data; + } PACKED; + TAILQ_HEAD(ql_tlv_list, esmc_tlv) tlv_list; +}; + +#endif -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:23
|
From: Arkadiusz Kubalewski <ark...@in...> Add interface for sending and receiving ESMC frames. Ethernet Synchronization Messaging Channel (ESMC) requires to operate on multicast raw L2 socket. This patch adds features to open socket, send and receive ESMC frames. Co-developed-by: Anatolii Gerasymenko <ana...@in...> Signed-off-by: Anatolii Gerasymenko <ana...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: changed order of patch in patch-series v3: rebase patch series esmc_socket.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ esmc_socket.h | 42 +++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 esmc_socket.c create mode 100644 esmc_socket.h diff --git a/esmc_socket.c b/esmc_socket.c new file mode 100644 index 0000000..a675d33 --- /dev/null +++ b/esmc_socket.c @@ -0,0 +1,99 @@ +/** + * @file esmc_socket.c + * @brief Implements the ESMC socket. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <errno.h> +#include <unistd.h> +#include <net/if.h> +#include <sys/socket.h> +#include <linux/if_ether.h> + +#include "sk.h" +#include "print.h" +#include "synce_msg.h" +#include "synce_msg_private.h" +#include "esmc_socket.h" + +int open_esmc_socket(const char *iface) +{ + unsigned char multicast_macaddr[MAC_LEN] = SYNCE_DEST_MACADDR; + struct sockaddr_ll addr; + struct packet_mreq mreq; + int fd, index, err; + + fd = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(SYNCE_ETHERTYPE)); + if (fd < 0) { + pr_err("socket failed: %m"); + return -1; + } + index = sk_interface_index(fd, iface); + if (index < 0) { + goto no_option; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_ifindex = index; + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(SYNCE_ETHERTYPE); + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr))) { + pr_err("bind failed: %m"); + goto no_option; + } + err = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface, + strnlen(iface, IFNAMSIZ)); + if (err) { + pr_err("setsockopt SO_BINDTODEVICE failed: %m"); + goto no_option; + } + + memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = index; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = MAC_LEN; + if (sizeof(mreq.mr_address) * sizeof(mreq.mr_address[0]) < + sizeof(multicast_macaddr) * sizeof(multicast_macaddr[0])) { + pr_err("setting multicast address failed"); + goto no_option; + } + memset(mreq.mr_address, 0, sizeof(mreq.mr_address)); + // we need to copy only 6 bytes to mreq.mr_address + memcpy(mreq.mr_address, multicast_macaddr, sizeof(multicast_macaddr)); + + err = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (err) { + pr_warning("setsockopt PACKET_MR_MULTICAST failed: %m"); + } + + return fd; + +no_option: + close(fd); + + return -1; +} + +int send_raw_esmc_frame(int socket, void *frame, int frame_len, int ifindex) +{ + struct sockaddr_ll saddrll; + int ret, frame_size; + + memset(&saddrll, 0, sizeof(saddrll)); + saddrll.sll_ifindex = ifindex; + + frame_size = frame_len > ETH_ZLEN ? frame_len : ETH_ZLEN; + ret = sendto(socket, frame, frame_size, 0, + (struct sockaddr *)&saddrll, sizeof(saddrll)); + if (ret < 0) { + pr_err("%s failed: %m", __func__); + return errno; + } + + return 0; +} + +int recv_raw_esmc_frame(int socket, void *buff, size_t buff_len) +{ + return recv(socket, buff, buff_len, 0); +} diff --git a/esmc_socket.h b/esmc_socket.h new file mode 100644 index 0000000..2a9c80f --- /dev/null +++ b/esmc_socket.h @@ -0,0 +1,42 @@ +/** + * @file esmc_socket.h + * @brief Implements the ESMC socket. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_ESMC_SOCKET_H +#define HAVE_ESMC_SOCKET_H + +/** + * Creates a raw ESMC socket and binds it to given interface. + * + * This high level API creates raw socket and binds it to specified + * interface and binds it to ESMC L2 multicast address. Thanks to + * those binds we are able to receive frames only on specified interface. + * + * @param iface A name of interface to bind to raw socket + * @return A raw socket file descriptor, negative if error occurred + */ +int open_esmc_socket(const char *iface); + +/** + * Sends a raw ESMC frame on given interface. + * + * @param socket A file descriptor of open raw ESMC socket + * @param frame A pointer to raw ESMC frame buffer + * @param frame_len A size of frame to be sent + * @return Zero on success, non-zero if the sending failed + */ +int send_raw_esmc_frame(int socket, void *frame, int frame_len, int ifindex); + +/** + * Receives a raw ESMC frame on given interface. + * + * @param socket A file descriptor of open raw ESMC socket + * @param buff A pointer to raw ESMC frame buffer + * @param buff_len A buffer size - maximum data to recv + * @return Zero on success, non-zero if the reception failed + */ +int recv_raw_esmc_frame(int socket, void *buff, size_t buff_len); + +#endif -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:24
|
From: Arkadiusz Kubalewski <ark...@in...> Interface is used to create and store an opened socket for sending and receiving SyncE PDU's on a given NIC interface. Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - changed order of patch in patch-series - changed naming 'Sync-E' -> 'SyncE' v3: - rebase patch series v2: - added buffer length parameter to recv_raw_esmc_frame to avoid buffer overruns - updated license headers synce_transport.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++ synce_transport.h | 55 +++++++++++++++++++++++++ synce_transport_private.h | 18 ++++++++ 3 files changed, 175 insertions(+) create mode 100644 synce_transport.c create mode 100644 synce_transport.h create mode 100644 synce_transport_private.h diff --git a/synce_transport.c b/synce_transport.c new file mode 100644 index 0000000..350270e --- /dev/null +++ b/synce_transport.c @@ -0,0 +1,102 @@ +/** + * @file synce_transport.c + * @brief Implements the SyncE transport interface. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <errno.h> +#include <net/if.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <linux/if_ether.h> + +#include "sk.h" +#include "print.h" +#include "synce_msg.h" +#include "synce_msg_private.h" +#include "esmc_socket.h" +#include "synce_transport.h" +#include "synce_transport_private.h" + +struct synce_transport *synce_transport_create(const char *iface) +{ + struct synce_transport *transport; + + transport = malloc(sizeof(struct synce_transport)); + if (!transport) { + pr_err("transport creation in SyncE failed"); + return NULL; + } + + if (snprintf(transport->iface, IFNAMSIZ, "%s", iface) >= IFNAMSIZ) { + pr_err("interface name too long"); + goto err; + } + + transport->raw_socket_fd = open_esmc_socket(iface); + if (transport->raw_socket_fd < 0) { + pr_err("socket creation in SyncE transport failed"); + goto err; + } + + transport->iface_index = sk_interface_index(transport->raw_socket_fd, + iface); + if (transport->iface_index < 0) { + goto err_socket; + } + + return transport; + +err_socket: + close(transport->raw_socket_fd); +err: + free(transport); + + return NULL; +} + +void synce_transport_delete(struct synce_transport *transport) +{ + close(transport->raw_socket_fd); + free(transport); +} + +int synce_transport_send_pdu(struct synce_transport *transport, + struct synce_pdu *pdu) +{ + int tlvs_size, pdu_size; + + tlvs_size = synce_msg_get_esmc_tlvs_size(pdu); + + pdu_size = sizeof(pdu->header) + tlvs_size; + + if (send_raw_esmc_frame(transport->raw_socket_fd, (void *)pdu, + pdu_size, transport->iface_index)) { + return -EIO; + } + + return 0; +} + +int synce_transport_recv_pdu(struct synce_transport *transport, + struct synce_pdu *pdu) +{ + int ret; + + synce_msg_reset_tlvs(pdu); + + ret = recv_raw_esmc_frame(transport->raw_socket_fd, &pdu->data, + sizeof(pdu->data)); + if (ret < 0) { + return -EAGAIN; + } else if (ret > 0 && ret < ETH_ZLEN) { + pr_err("%s failed: received only %i bytes", __func__, ret); + return -EBADMSG; + } + + synce_msg_recover_tlvs(pdu); + + return 0; +} diff --git a/synce_transport.h b/synce_transport.h new file mode 100644 index 0000000..0d0e8e4 --- /dev/null +++ b/synce_transport.h @@ -0,0 +1,55 @@ +/** + * @file synce_transport.h + * @brief Implements the SyncE transport interface. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_TRANSPORT_H +#define HAVE_SYNCE_TRANSPORT_H + +#include <stdint.h> + +struct synce_transport; +struct ClockIdentity; +struct synce_pdu; + +/** + * Create a SyncE transport. + * + * This high level API creates SyncE transport but and initialize it. + * + * @param iface A name of interface to create transport for + * @return A SyncE transport structure + */ +struct synce_transport *synce_transport_create(const char *iface); + +/** + * Delete a SyncE transport. + * + * This high level API deletes SyncE transport and frees all memory allocations. + * + * @param transport A SyncE transport interface + */ +void synce_transport_delete(struct synce_transport *transport); + +/** + * Send PDU via SyncE transport. + * + * @param transport A SyncE transport interface + * @param pdu A pointer to a ESMC SyncE PDU + * @return Zero on success, non-zero if failure + */ +int synce_transport_send_pdu(struct synce_transport *transport, + struct synce_pdu *pdu); + +/** + * Recv PDU via SyncE transport. + * + * @param transport A SyncE transport interface + * @param pdu A pointer to a ESMC SyncE PDU + * @return Zero on success, non-zero if failure + */ +int synce_transport_recv_pdu(struct synce_transport *transport, + struct synce_pdu *pdu); + +#endif diff --git a/synce_transport_private.h b/synce_transport_private.h new file mode 100644 index 0000000..3304c9a --- /dev/null +++ b/synce_transport_private.h @@ -0,0 +1,18 @@ +/** + * @file synce_transport_private.h + * @brief Implements the SyncE transport private structures. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_TRANSPORT_PRIVATE_H +#define HAVE_SYNCE_TRANSPORT_PRIVATE_H + +#include <net/if.h> + +struct synce_transport { + char iface[IFNAMSIZ]; + int iface_index; + int raw_socket_fd; +}; + +#endif -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:24
|
From: Arkadiusz Kubalewski <ark...@in...> Extend interface struct, allow to keep information about SyncE parent device. Add helper function (util.c) to acquire Do Not Use - Quality Level value. Co-developed-by: Anatolii Gerasymenko <ana...@in...> Signed-off-by: Anatolii Gerasymenko <ana...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: changed order of patch in patch-series v3: rebase patch series v2: updated license headers interface.c | 29 +++++++++++++++++++++++++++-- interface.h | 24 ++++++++++++++++++++++++ util.c | 22 ++++++++++++++++++++++ util.h | 8 ++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/interface.c b/interface.c index 445a270..6fc11bf 100644 --- a/interface.c +++ b/interface.c @@ -7,11 +7,19 @@ #include <stdlib.h> #include "interface.h" +#define SYNCE_PARENT_EXIST (1 << 0) + struct interface { STAILQ_ENTRY(interface) list; char name[MAX_IFNAME_SIZE + 1]; - char ts_label[MAX_IFNAME_SIZE + 1]; - struct sk_ts_info ts_info; + union { + char ts_label[MAX_IFNAME_SIZE + 1]; + char synce_parent_label[MAX_IFNAME_SIZE + 1]; + }; + union { + struct sk_ts_info ts_info; + int synce_flags; + }; int vclock; }; @@ -88,3 +96,20 @@ int interface_get_vclock(struct interface *iface) { return iface->vclock; } + +void interface_se_set_parent_dev(struct interface *iface, const char *dev_name) +{ + strncpy(iface->synce_parent_label, dev_name, MAX_IFNAME_SIZE); + iface->synce_flags |= SYNCE_PARENT_EXIST; +} + +const char *interface_se_get_parent_dev_label(struct interface *iface) +{ + return iface->synce_parent_label; +} + +bool interface_se_has_parent_dev(struct interface *iface) +{ + return !!(iface->synce_flags & SYNCE_PARENT_EXIST); +} + diff --git a/interface.h b/interface.h index 752f4f1..47cc25f 100644 --- a/interface.h +++ b/interface.h @@ -105,4 +105,28 @@ void interface_set_vclock(struct interface *iface, int vclock); */ int interface_get_vclock(struct interface *iface); +/** + * Set the synce parent device for a given interface. + * @param iface The interface of interest. + * @param dev_name The desired label for the interface. + */ +void interface_se_set_parent_dev(struct interface *iface, const char *dev_name); + +/** + * Obtain the name of the parent synce device to which an interface belongs. + * The parent device must provde an interface to control device-level synce + * configuration. + * @param iface The interface of interest. + * @return Name of the parent synce device + */ +const char *interface_se_get_parent_dev_label(struct interface *iface); + +/** + * Tests whether an interface has a synce parent device. + * Which means it is regular port configured for synce. + * @param iface The interface of interest. + * @return True if the interface is a synce parent device + */ +bool interface_se_has_parent_dev(struct interface *iface); + #endif diff --git a/util.c b/util.c index a59b559..1c04870 100644 --- a/util.c +++ b/util.c @@ -30,6 +30,7 @@ #include "print.h" #include "sk.h" #include "util.h" +#include "synce_msg.h" #define NS_PER_SEC 1000000000LL #define NS_PER_HOUR (3600 * NS_PER_SEC) @@ -779,3 +780,24 @@ int rate_limited(int interval, time_t *last) return 0; } + +uint8_t synce_get_dnu_value(int network_option, int extended_tlv) +{ + uint8_t ret = O1N_QL_DNU_ENHSSM; + + if (extended_tlv) { + if (network_option == SYNCE_NETWORK_OPT_1) { + ret = O1N_QL_DNU_ENHSSM; + } else if (network_option == SYNCE_NETWORK_OPT_2) { + ret = O2N_QL_DUS_ENHSSM; + } + } else { + if (network_option == SYNCE_NETWORK_OPT_1) { + ret = O1N_QL_DNU_SSM; + } else if (network_option == SYNCE_NETWORK_OPT_2) { + ret = O2N_QL_DUS_SSM; + } + } + + return ret; +} diff --git a/util.h b/util.h index 558a675..c52dc2e 100644 --- a/util.h +++ b/util.h @@ -458,4 +458,12 @@ void parray_extend(void ***a, ...); */ int rate_limited(int interval, time_t *last); +/** + * Acquire proper SynceE Do Not Use signal value basing on given arguments + * + * @param network_option Type of the network + * @param extended_tlv If QL for extended tlv + * @return Do Not Use signal value + */ +uint8_t synce_get_dnu_value(int netwotk_option, int extended_tlv); #endif -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:26
|
From: Arkadiusz Kubalewski <ark...@in...> Used by synce_port to create, control and destroy RX/TX threads. Ports in sync mode can use only TX thread (device in external input mode) or both RX and TX threads (device in internal input mode). RX thread is responsible for: - receiving ESMC frames - verifying received QL - storing last received valid QL and time it was received - determining if QL-failed state occurred or port recovered from QL-failed state. TX thread is responsible for: - building new TX ESMC if requested - sending ESMC on timer. synce_port_ctrl interface allows: - initialize threads - obtain last received valid QL - compare QL's between 2 ports - check if RX QL has changed - check if RX QL has Do Not Use QL - check if RX QL-failed state occurred - invalidate RX QL's. Co-developed-by: Anatolii Gerasymenko <ana...@in...> Signed-off-by: Anatolii Gerasymenko <ana...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - improved protection on threads data with additional mutex locking - changed order of patch in patch-series v3: rebase patch series v2: updated license headers synce_port_ctrl.c | 1230 +++++++++++++++++++++++++++++++++++++++++++++++++++++ synce_port_ctrl.h | 165 +++++++ 2 files changed, 1395 insertions(+) create mode 100644 synce_port_ctrl.c create mode 100644 synce_port_ctrl.h diff --git a/synce_port_ctrl.c b/synce_port_ctrl.c new file mode 100644 index 0000000..b821225 --- /dev/null +++ b/synce_port_ctrl.c @@ -0,0 +1,1230 @@ +/** + * @file synce_port_ctrl.c + * @brief Interface between synce port and socket handling theads, used + * for controling data on the wire. Allows acquire incoming data and + * submit new outgoing data. + * TX thread is always present, RX only if required (internal_input mode). + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <stdlib.h> +#include <limits.h> +#include <pthread.h> +#include <unistd.h> +#include <net/if.h> +#include <errno.h> +#include "print.h" +#include "ddt.h" +#include "config.h" +#include "synce_port_ctrl.h" +#include "synce_transport.h" +#include "synce_msg.h" + +#define MSEC_TO_USEC(X) (X * 1000) +#define THREAD_STOP_SLEEP_USEC MSEC_TO_USEC(50) +#define THREAD_START_SLEEP_USEC MSEC_TO_USEC(20) +#define SYNCE_THREAD_STACK_SIZE 0xffff +#define RX_THREAD 0 +#define TX_THREAD 1 +#define TASK_COMM_LEN 16 + +#define ENHANCED_SSM_SHIFT 8 +#define QL_PRIORITY(r, e) ((e << ENHANCED_SSM_SHIFT) | r) + +#define O1N_PRIORITY_COUNT 5 +static const uint16_t O1N_priority[O1N_PRIORITY_COUNT] = { + QL_PRIORITY(O1N_QL_EPRTC_SSM, O1N_QL_EPRTC_ENHSSM), + QL_PRIORITY(O1N_QL_PRTC_SSM, O1N_QL_PRTC_ENHSSM), + QL_PRIORITY(O1N_QL_PRC_SSM, O1N_QL_PRC_ENHSSM), + QL_PRIORITY(O1N_QL_SSU_A_SSM, O1N_QL_SSU_A_ENHSSM), + QL_PRIORITY(O1N_QL_SSU_B_SSM, O1N_QL_SSU_B_ENHSSM) +}; + +#define O2N_PRIORITY_COUNT 8 +static const uint16_t O2N_priority[O2N_PRIORITY_COUNT] = { + QL_PRIORITY(O2N_QL_EPRTC_SSM, O2N_QL_EPRTC_ENHSSM), + QL_PRIORITY(O2N_QL_PRTC_SSM, O2N_QL_PRTC_ENHSSM), + QL_PRIORITY(O2N_QL_PRS_SSM, O2N_QL_PRS_ENHSSM), + QL_PRIORITY(O2N_QL_STU_SSM, O2N_QL_STU_ENHSSM), + QL_PRIORITY(O2N_QL_ST2_SSM, O2N_QL_ST2_ENHSSM), + QL_PRIORITY(O2N_QL_TNC_SSM, O2N_QL_TNC_ENHSSM), + QL_PRIORITY(O2N_QL_ST3E_SSM, O2N_QL_ST3E_ENHSSM), + QL_PRIORITY(O2N_QL_PROV_SSM, O2N_QL_PROV_ENHSSM) +}; + +struct ql { + STAILQ_ENTRY(ql) list; + uint8_t value; +}; + +struct thread_common_data { + int heartbeat_usec; + int state; + uint8_t ql; + int extended; + struct synce_msg_ext_ql ext_ql; + struct synce_transport *transport; + struct synce_pdu *pdu; + char *name; + int enabled; + pthread_mutex_t lock; +}; + +struct synce_port_tx { + struct thread_common_data cd; + int rebuild_tlv; +}; + +struct synce_port_rx { + struct thread_common_data cd; + uint8_t last_ql; + struct synce_msg_ext_ql last_ext_ql; + int ql_failed; + struct timespec last_recv_ts; + struct timespec first_valid_ts; + uint64_t n_recv; + int recover_time; + int ext_tlv_recvd; + uint8_t ql_dnu_val; + uint8_t ext_ql_dnu_val; + + STAILQ_HEAD(allowed_qls_head, ql) allowed_qls; + STAILQ_HEAD(allowed_ext_qls_head, ql) allowed_ext_qls; +}; + +struct synce_port_ctrl { + char name[IF_NAMESIZE]; + struct synce_port_tx tx; + struct synce_port_rx rx; + pthread_t tx_thread_id; + pthread_t rx_thread_id; + struct synce_transport *transport; + const uint16_t *priority_list; + int priority_list_count; +}; + +enum thread_state { + THREAD_NOT_STARTED = 0, + THREAD_STARTED, + THREAD_STOPPING, + THREAD_STOPPED, + THREAD_FAILED, +}; + +static int lock_mutex(struct thread_common_data *cd, const char *func) +{ + int ret = pthread_mutex_lock(&cd->lock); + + if (ret) { + pr_err("%s: lock mutex failed err: %d on %s", + func, ret, cd->name); + } + + return ret; +} + +static int unlock_mutex(struct thread_common_data *cd, const char *func) +{ + int ret = pthread_mutex_unlock(&cd->lock); + + if (ret) { + pr_err("%s: lock mutex failed err: %d on %s", + func, ret, cd->name); + } + + return ret; +} + +static int tx_rebuild_tlv(struct synce_port_tx *tx) +{ + struct thread_common_data *cd; + int ret = -ENXIO; + + if (!tx) { + pr_err("synce_port_tx is NULL"); + return ret; + } + + cd = &tx->cd; + if (!cd) { + pr_err("%s cd is NULL", __func__); + return ret; + } + + if (!cd->pdu) { + pr_err("tx pdu is NULL"); + return ret; + } + + synce_msg_reset_tlvs(cd->pdu); + + ret = synce_msg_attach_ql_tlv(cd->pdu, cd->ql); + if (ret) { + pr_err("attach QL=%u TLV failed on %s", cd->ql, cd->name); + goto err; + } else { + pr_info("%s: attached new TLV, QL=%d on %s", + __func__, cd->ql, cd->name); + } + + if (cd->extended) { + ret = synce_msg_attach_extended_ql_tlv(cd->pdu, + &cd->ext_ql); + if (ret) { + pr_err("attach EXT_QL TLV failed on %s", cd->name); + goto err; + } else { + pr_info("%s: attached new extended TLV, EXT_QL=%d on %s", + __func__, cd->ext_ql.enhancedSsmCode, + cd->name); + } + } + + tx->rebuild_tlv = 0; + + return ret; +err: + cd->state = THREAD_FAILED; + return ret; +} + +static void *tx_thread(void *data) +{ + struct synce_port_tx *tx = (struct synce_port_tx *) data; + struct thread_common_data *cd; + volatile int *state; + + if (!tx) { + pr_err("%s tx data is NULL", __func__); + pthread_exit(NULL); + } + + cd = &tx->cd; + state = &cd->state; + + if (lock_mutex(cd, __func__) == 0) { + if (*state != THREAD_NOT_STARTED) { + pr_err("tx wrong state"); + goto unlock_out; + } + } else { + goto out; + } + + pr_debug("tx thread started on port %s", cd->name); + *state = THREAD_STARTED; + while (*state == THREAD_STARTED) { + if (tx->rebuild_tlv) { + if (tx_rebuild_tlv(tx)) { + pr_err("tx rebuild failed"); + goto unlock_out; + } + } + + /* any errors are traced inside */ + if (cd->enabled) { + synce_transport_send_pdu(cd->transport, cd->pdu); + } + unlock_mutex(cd, __func__); + usleep(cd->heartbeat_usec); + if (lock_mutex(cd, __func__) != 0) { + goto out; + } + }; + +unlock_out: + unlock_mutex(cd, __func__); +out: + *state = (*state == THREAD_STOPPING) ? THREAD_STOPPED : *state; + pr_debug("tx thread exit state %d=%s port %s", *state, + *state == THREAD_STOPPED ? "OK" : "failed", cd->name); + pthread_exit(NULL); +} + +static int diff_sec(struct timespec now, struct timespec before) +{ + return (now.tv_sec - before.tv_sec); +} + +static void update_ql(struct thread_common_data *cd, int ext_tlv_recvd, + uint8_t ql, const struct synce_msg_ext_ql *ext_ql) +{ + cd->ql = ql; + + if (ext_tlv_recvd == 1) { + memcpy(&cd->ext_ql, ext_ql, sizeof(cd->ext_ql)); + } +} + +static int is_ql_allowed(struct allowed_qls_head *qls_stailq_head, uint8_t ql) +{ + struct ql *checked_ql; + + if (STAILQ_EMPTY(qls_stailq_head)) { + /* no filter list - accept all */ + return 1; + } + + STAILQ_FOREACH(checked_ql, qls_stailq_head, list) { + if (checked_ql->value == ql) { + return 1; + } + } + + return 0; +} + +static int get_rx_qls(struct synce_port_rx *rx, uint8_t *ql, + struct synce_msg_ext_ql *ext_ql) +{ + struct thread_common_data *cd = &rx->cd; + + if (synce_msg_get_ql_tlv(cd->pdu, ql)) { + return -EAGAIN; + } + + pr_debug("QL=%d found on %s", *ql, cd->name); + + if (!is_ql_allowed(&rx->allowed_qls, *ql)) { + pr_debug("Received not allowed QL: %i, discarding", *ql); + return -EBADMSG; + } + + if (cd->extended) { + if (synce_msg_get_extended_ql_tlv(cd->pdu, ext_ql)) { + rx->ext_tlv_recvd = 0; + + /* only extended missing - not an error */ + return 0; + } + + pr_debug("extended QL=%d found on %s", + ext_ql->enhancedSsmCode, cd->name); + + if (!is_ql_allowed((struct allowed_qls_head *) + &rx->allowed_ext_qls, + ext_ql->enhancedSsmCode)) { + pr_debug("Received not allowed ext_QL: %i, discarding", + ext_ql->enhancedSsmCode); + rx->ext_tlv_recvd = 0; + return -EBADMSG; + } + + rx->ext_tlv_recvd = 1; + } + + return 0; +} + +static int rx_act(struct synce_port_rx *rx) +{ + struct thread_common_data *cd = &rx->cd; + struct synce_msg_ext_ql ext_ql; + struct timespec now; + uint8_t ql; + int err; + + /* read socket for ESMC and fill pdu */ + err = synce_transport_recv_pdu(cd->transport, cd->pdu); + if (!err) { + rx->n_recv++; + } + + /* wait for first frame received before starting any logic */ + if (!rx->n_recv) { + return -EAGAIN; + } + + err = get_rx_qls(rx, &ql, &ext_ql); + if (err) { + /* go to ql_failed state if continue missing frames */ + if (rx->ql_failed == 0) { + clock_gettime(CLOCK_REALTIME, &now); + if (diff_sec(now, rx->last_recv_ts) >= + QL_FAILED_PERIOD_SEC) { + pr_info("QL not received on %s within %d s", + cd->name, QL_FAILED_PERIOD_SEC); + rx->ql_failed = 1; + /* clear first_valid_ts so we can recover from + * ql_failed state + */ + memset(&rx->first_valid_ts, 0, + sizeof(rx->first_valid_ts)); + } + } + } else { + clock_gettime(CLOCK_REALTIME, &rx->last_recv_ts); + now.tv_sec = rx->last_recv_ts.tv_sec; + if (rx->ql_failed == 1) { + if (rx->first_valid_ts.tv_sec == 0) { + clock_gettime(CLOCK_REALTIME, + &rx->first_valid_ts); + } else { + /* May be required to add counter for number + * of received frames before exit ql_failed + */ + if (diff_sec(now, rx->first_valid_ts) >= + rx->recover_time) { + update_ql(cd, rx->ext_tlv_recvd, + ql, &ext_ql); + rx->ql_failed = 0; + pr_info("QL-failed recovered on %s", + cd->name); + } + } + } else { + update_ql(cd, rx->ext_tlv_recvd, ql, &ext_ql); + pr_debug("QL=%u received on %s", cd->ql, cd->name); + } + } + + return 0; +} + +static void *rx_thread(void *data) +{ + struct synce_port_rx *rx = (struct synce_port_rx *) data; + struct thread_common_data *cd; + volatile int *state; + + if (!rx) { + pr_err("%s rx data NULL", __func__); + pthread_exit(NULL); + } + + cd = &rx->cd; + state = &cd->state; + + if (lock_mutex(cd, __func__) == 0) { + if (*state != THREAD_NOT_STARTED) { + pr_err("rx wrong state on %s", cd->name); + goto unlock_out; + } + } else { + goto out; + } + + pr_debug("rx thread started on port %s", cd->name); + *state = THREAD_STARTED; + while (*state == THREAD_STARTED) { + rx_act(rx); + unlock_mutex(cd, __func__); + usleep(cd->heartbeat_usec); + if (lock_mutex(cd, __func__) != 0) { + goto out; + } + }; + +unlock_out: + unlock_mutex(cd, __func__); +out: + *state = (*state == THREAD_STOPPING) ? THREAD_STOPPED : *state; + pr_debug("rx thread exit state %d=%s port %s", *state, + *state == THREAD_STOPPED ? "OK" : "failed", cd->name); + pthread_exit(NULL); +} + +static int tx_init(struct synce_port_tx *tx, int heartbeat_msec, + int extended_tlv, struct synce_transport *transport, + char *name) +{ + struct thread_common_data *cd; + + if (!tx) { + pr_err("%s tx NULL", __func__); + return -EFAULT; + } + + if (!transport) { + pr_err("%s transport NULL", __func__); + return -EFAULT; + } + + if (!name) { + pr_err("%s name NULL", __func__); + return -EFAULT; + } + + cd = &tx->cd; + memset(tx, 0, sizeof(*tx)); + + if (extended_tlv) { + memset(&cd->ext_ql, 0, sizeof(cd->ext_ql)); + cd->extended = extended_tlv; + } + cd->heartbeat_usec = MSEC_TO_USEC(heartbeat_msec); + cd->name = name; + cd->pdu = synce_msg_create(cd->name); + cd->transport = transport; + cd->state = THREAD_NOT_STARTED; + tx->rebuild_tlv = 0; + cd->enabled = 0; + if (pthread_mutex_init(&cd->lock, NULL)) { + pr_err("%s: TX thread mutex init failure", name); + return -EFAULT; + } + + return 0; +} + +static void free_allowed_qls(struct allowed_qls_head *head) +{ + struct ql *q; + + while ((q = STAILQ_FIRST(head))) { + STAILQ_REMOVE_HEAD(head, list); + free(q); + } +} + +#define QL_STR_MAX_LEN 256 +#define QL_STR_BASE 10 +static int init_ql_str(struct allowed_qls_head *qls_stailq_head, + const char *allowed_qls) +{ + unsigned int allowed_qls_len; + char *endptr = NULL; + const char *ptr; + + if (allowed_qls == NULL) { + return 0; + } + + allowed_qls_len = strnlen(allowed_qls, QL_STR_MAX_LEN); + if (allowed_qls_len == QL_STR_MAX_LEN) { + pr_err("QLs list string too long (max %i)", QL_STR_MAX_LEN); + return -E2BIG; + } + + ptr = allowed_qls; + while (ptr <= allowed_qls + allowed_qls_len && *ptr) { + unsigned long value; + struct ql *newql; + + value = strtoul(ptr, &endptr, QL_STR_BASE); + if (endptr == ptr) { + pr_err("QL list item read failed - please verify"); + free_allowed_qls(qls_stailq_head); + return -EINVAL; + } + if (value > UCHAR_MAX) { + pr_err("QL list item outside of range - please verify"); + free_allowed_qls(qls_stailq_head); + return -EINVAL; + } + + newql = malloc(sizeof(struct ql)); + if (!newql) { + pr_err("could not alloc ql"); + return -EINVAL; + } + + newql->value = value; + STAILQ_INSERT_HEAD(qls_stailq_head, newql, list); + + ptr = endptr + 1; + } + + if (endptr != allowed_qls + allowed_qls_len) { + pr_err("QL list malformed - not all read - please verify"); + free_allowed_qls(qls_stailq_head); + return -EINVAL; + } + + return 0; +} + +static int init_allowed_qls(struct synce_port_rx *rx, struct config *cfg, + const char *name) +{ + const char *allowed_qls; + + STAILQ_INIT(&rx->allowed_qls); + + allowed_qls = config_get_string(cfg, name, "allowed_qls"); + if (allowed_qls == NULL) { + pr_warning("No allowed QLs list found - filtering disabled"); + return 0; + } + + return init_ql_str(&rx->allowed_qls, allowed_qls); +} + +static int init_allowed_ext_qls(struct synce_port_rx *rx, struct config *cfg, + const char *name) +{ + const char *allowed_qls; + + STAILQ_INIT(&rx->allowed_ext_qls); + + allowed_qls = config_get_string(cfg, name, "allowed_ext_qls"); + + if (allowed_qls == NULL) { + pr_warning("No allowed ext_QLs list found - filtering disabled"); + return 0; + } + + return init_ql_str((struct allowed_qls_head *)&rx->allowed_ext_qls, + allowed_qls); +} + +static int rx_init(struct synce_port_rx *rx, int heartbeat_msec, + int extended_tlv, int recover_time, + struct synce_transport *transport, char *name, + struct config *cfg, int network_option) +{ + struct thread_common_data *cd; + + if (!rx) { + pr_err("%s rx NULL", __func__); + return -EFAULT; + } + + if (!transport) { + pr_err("%s transport NULL", __func__); + return -EFAULT; + } + + if (!name) { + pr_err("%s name NULL", __func__); + return -EFAULT; + } + + cd = &rx->cd; + memset(rx, 0, sizeof(*rx)); + + if (extended_tlv) { + memset(&cd->ext_ql, 0, sizeof(cd->ext_ql)); + memcpy(&rx->last_ext_ql, &cd->ext_ql, sizeof(rx->last_ext_ql)); + cd->extended = extended_tlv; + } + cd->heartbeat_usec = MSEC_TO_USEC(heartbeat_msec); + cd->name = name; + cd->pdu = synce_msg_create(cd->name); + cd->transport = transport; + cd->state = THREAD_NOT_STARTED; + rx->ql_dnu_val = synce_get_dnu_value(network_option, false); + rx->ext_ql_dnu_val = synce_get_dnu_value(network_option, true); + rx->last_ql = rx->ql_dnu_val; + memset(&rx->last_recv_ts, 0, sizeof(rx->last_recv_ts)); + memset(&rx->first_valid_ts, 0, sizeof(rx->first_valid_ts)); + rx->n_recv = 0; + rx->recover_time = recover_time; + cd->enabled = 1; + if (pthread_mutex_init(&cd->lock, NULL)) { + pr_err("%s: RX thread mutex init failure", name); + return -EFAULT; + } + + + init_allowed_qls(rx, cfg, name); + init_allowed_ext_qls(rx, cfg, name); + + return 0; +} + +static int thread_stop_wait(struct thread_common_data *cd) +{ + int cnt = (cd->heartbeat_usec / THREAD_STOP_SLEEP_USEC) + 1; + int ret = lock_mutex(cd, __func__); + + if (ret) + return ret; + + if (cd->state == THREAD_STARTED) { + cd->state = THREAD_STOPPING; + } else { + unlock_mutex(cd, __func__); + return -ESRCH; + } + + do { + unlock_mutex(cd, __func__); + usleep(THREAD_STOP_SLEEP_USEC); + ret = lock_mutex(cd, __func__); + if (ret) { + return ret; + } + } while (cnt-- && cd->state != THREAD_STOPPED); + + ret = (cd->state == THREAD_STOPPED ? 0 : -ENXIO); + unlock_mutex(cd, __func__); + + return ret; +} + +static int thread_start_wait(struct thread_common_data *cd) +{ + int cnt = (cd->heartbeat_usec / THREAD_START_SLEEP_USEC) + 1; + int ret = lock_mutex(cd, __func__); + + if (ret) + return ret; + + if (cd->state == THREAD_STARTED) { + pr_debug("THREAD_STARTED"); + unlock_mutex(cd, __func__); + return 0; + } + + do { + unlock_mutex(cd, __func__); + usleep(THREAD_START_SLEEP_USEC); + ret = lock_mutex(cd, __func__); + if (ret) { + return ret; + } + } while (cnt-- && cd->state != THREAD_STARTED); + + ret = (cd->state == THREAD_STARTED ? 0 : -ESRCH); + unlock_mutex(cd, __func__); + + if (ret) { + pr_err("THREAD_FAILED"); + } else { + pr_debug("THREAD_STARTED"); + } + + return ret; +} + +static int synce_port_ctrl_thread_create(char *name, void *data, int tx, + pthread_t *thread_id) +{ + char thread_name[TASK_COMM_LEN]; + pthread_attr_t attr; + int err; + + err = pthread_attr_init(&attr); + if (err) { + pr_err("init %s thread attr failed for %s", + tx ? "tx" : "rx", name); + goto err_attr; + } + + err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (err) { + pr_err("set %s thread detached failed for %s err=%d", + tx ? "tx" : "rx", name, err); + goto err_attr; + } + + err = pthread_attr_setstacksize(&attr, SYNCE_THREAD_STACK_SIZE); + if (err) { + pr_err("set %s thread stack failed for %s err=%d", + tx ? "tx" : "rx", name, err); + goto err_attr; + } + + if (tx) { + err = pthread_create(thread_id, &attr, tx_thread, data); + } else { + err = pthread_create(thread_id, &attr, rx_thread, data); + } + if (err) { + pr_err("create %s thread failed for %s err=%d", + tx ? "tx" : "rx", name, err); + goto err_attr; + } + + snprintf(thread_name, TASK_COMM_LEN, "%s-%s", + tx ? "tx" : "rx", name); + err = pthread_setname_np(*thread_id, thread_name); + if (err) { + pr_info("failed to set %s thread's name for %s", + tx ? "tx" : "rx", name); + } + + pthread_attr_destroy(&attr); + return 0; + +err_attr: + pthread_attr_destroy(&attr); + return -ECHILD; +} + +static uint16_t get_ql_priority(struct synce_port_ctrl *pc) +{ + if (pc->rx.cd.extended) { + return QL_PRIORITY(pc->rx.cd.ql, + pc->rx.cd.ext_ql.enhancedSsmCode); + } else { + return QL_PRIORITY(pc->rx.cd.ql, + QL_OTHER_CLOCK_TYPES_ENHSSM); + } +} + +static struct synce_port_ctrl *is_valid_source(struct synce_port_ctrl *pc) +{ + uint16_t ql_priority; + int i, err; + + if (!pc) { + pr_debug("pc is NULL"); + return NULL; + } + + ql_priority = get_ql_priority(pc); + + err = lock_mutex(&pc->rx.cd, __func__); + if (err) { + pr_err("mutex fatal error on %s", pc->name); + return NULL; + } + + if (pc->rx.n_recv > 0 && !pc->rx.ql_failed) { + for (i = 0; i < pc->priority_list_count; i++) { + if (ql_priority == pc->priority_list[i]) { + unlock_mutex(&pc->rx.cd, __func__); + return pc; + } + } + } + unlock_mutex(&pc->rx.cd, __func__); + pr_info("not valid source: %s", pc->name); + + return NULL; +} + +int synce_port_ctrl_running(struct synce_port_ctrl *pc) +{ + int state, ret; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return -EFAULT; + } + + ret = lock_mutex(&pc->tx.cd, __func__); + if (ret) + return ret; + + state = (pc->tx.cd.state == THREAD_STARTED); + unlock_mutex(&pc->tx.cd, __func__); + + if (pc->rx.cd.enabled) { + ret = lock_mutex(&pc->rx.cd, __func__); + if (ret) + return ret; + + state = state && (pc->rx.cd.state == THREAD_STARTED); + unlock_mutex(&pc->rx.cd, __func__); + } + + return state; +} + +int synce_port_ctrl_destroy(struct synce_port_ctrl *pc) +{ + if (!pc) { + pr_err("%s pc is NULL", __func__); + return -EFAULT; + } + pr_debug("%s on %s", __func__, pc->name); + + thread_stop_wait(&pc->tx.cd); + if (pc->tx.cd.pdu) { + synce_msg_delete(pc->tx.cd.pdu); + } + pthread_mutex_destroy(&pc->tx.cd.lock); + + if (pc->rx.cd.enabled) { + thread_stop_wait(&pc->rx.cd); + if (pc->tx.cd.pdu) { + synce_msg_delete(pc->rx.cd.pdu); + } + pthread_mutex_destroy(&pc->rx.cd.lock); + + free_allowed_qls(&pc->rx.allowed_qls); + free_allowed_qls((struct allowed_qls_head *)&pc->rx.allowed_ext_qls); + } + + if (pc->transport) { + synce_transport_delete(pc->transport); + } + memset(pc, 0, sizeof(*pc)); + + return 0; +} + +int synce_port_ctrl_rx_ql_failed(struct synce_port_ctrl *pc) +{ + int ret = -EFAULT; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return ret; + } + + ret = lock_mutex(&pc->rx.cd, __func__); + if (ret) + return ret; + + ret = (pc->rx.ql_failed != 0); + unlock_mutex(&pc->rx.cd, __func__); + + return ret; +} + +int synce_port_ctrl_rx_dnu(struct synce_port_ctrl *pc, uint8_t dnu_val) +{ + int ret = -EFAULT; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return ret; + } + + ret = lock_mutex(&pc->rx.cd, __func__); + if (ret) + return ret; + + ret = pc->rx.n_recv && !pc->rx.ql_failed ? + pc->rx.cd.ql == dnu_val : -EAGAIN; + unlock_mutex(&pc->rx.cd, __func__); + + return ret; +} + +int synce_port_ctrl_rx_ql_changed(struct synce_port_ctrl *pc) +{ + struct thread_common_data *cd; + int ret = -EFAULT; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return ret; + } + + cd = &pc->rx.cd; + ret = lock_mutex(cd, __func__); + if (ret) + return ret; + + if (!pc->rx.ext_tlv_recvd) { + ret = (cd->ql != pc->rx.last_ql); + pc->rx.last_ql = cd->ql; + } else { + ret = (cd->ql != pc->rx.last_ql) || + (memcmp(&cd->ext_ql, + &pc->rx.last_ext_ql, + sizeof(cd->ext_ql)) != 0); + pc->rx.last_ql = cd->ql; + memcpy(&pc->rx.last_ext_ql, &cd->ext_ql, sizeof(pc->rx.last_ext_ql)); + } + unlock_mutex(cd, __func__); + + if (ret) { + pr_debug("%s on %s", __func__, pc->name); + } + + return ret; +} + +int synce_port_ctrl_rx_ext_tlv(struct synce_port_ctrl *pc) +{ + int ret = -EFAULT; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return ret; + } + + ret = lock_mutex(&pc->rx.cd, __func__); + if (ret) + return ret; + + ret = pc->rx.ext_tlv_recvd; + unlock_mutex(&pc->rx.cd, __func__); + + return ret; +} + +int synce_port_ctrl_get_rx_ql(struct synce_port_ctrl *pc, uint8_t *ql) +{ + int ret = -EFAULT; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return ret; + } + + if (!ql) { + pr_err("%s ql is NULL", __func__); + return ret; + } + + ret = lock_mutex(&pc->rx.cd, __func__); + if (ret) + return ret; + + *ql = pc->rx.cd.ql; + unlock_mutex(&pc->rx.cd, __func__); + + return ret; +} + +int synce_port_ctrl_get_rx_ext_ql(struct synce_port_ctrl *pc, + struct synce_msg_ext_ql *ext_ql) +{ + int ret = -EFAULT; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return ret; + } + + if (!ext_ql) { + pr_err("%s ext_ql is NULL", __func__); + return ret; + } + + if (!pc->rx.cd.extended) { + pr_err("ext_ql was not enabled for %s", pc->name); + return ret; + } + + ret = lock_mutex(&pc->rx.cd, __func__); + if (ret) + return ret; + + memcpy(ext_ql, &pc->rx.cd.ext_ql, sizeof(*ext_ql)); + unlock_mutex(&pc->rx.cd, __func__); + + return ret; +} + +int synce_port_ctrl_set_tx_ql(struct synce_port_ctrl *pc, uint8_t ql) +{ + if (!pc) { + pr_err("%s pc is NULL", __func__); + return -EFAULT; + } + + pc->tx.cd.ql = ql; + + return 0; +} + +int synce_port_ctrl_set_tx_ext_ql(struct synce_port_ctrl *pc, + struct synce_msg_ext_ql *ext_ql) +{ + if (!pc) { + pr_err("%s pc is NULL", __func__); + return -EFAULT; + } + + if (!ext_ql) { + pr_err("%s ext_ql is NULL", __func__); + return -EFAULT; + } + + memcpy(&pc->tx.cd.ext_ql, ext_ql, sizeof(pc->tx.cd.ext_ql)); + + return 0; +} + +int synce_port_ctrl_rebuild_tx(struct synce_port_ctrl *pc) +{ + int ret = -EFAULT; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return ret; + } + + ret = lock_mutex(&pc->tx.cd, __func__); + if (ret) + return ret; + + pc->tx.rebuild_tlv = 1; + unlock_mutex(&pc->tx.cd, __func__); + + return ret; +} + +int synce_port_ctrl_enable_tx(struct synce_port_ctrl *pc) +{ + int ret; + + if (!pc) { + pr_err("%s pc is NULL", __func__); + return -EFAULT; + } + + ret = lock_mutex(&pc->tx.cd, __func__); + if (ret) + return ret; + + pc->tx.cd.enabled = 1; + unlock_mutex(&pc->tx.cd, __func__); + + return 0; +} + +struct synce_port_ctrl * +synce_port_ctrl_compare_ql(struct synce_port_ctrl *left, + struct synce_port_ctrl *right) +{ + uint16_t left_ql_priority, right_ql_priority; + struct synce_port_ctrl *best = NULL; + int i; + + if (!left && !right) { + pr_err("%s both left and right are NULL", __func__); + goto out; + } + + left = is_valid_source(left); + right = is_valid_source(right); + + if (!left && !right) { + pr_err("both left and right are invalid"); + goto out; + } else if (!left != !right) { + best = left ? left : right; + pr_debug("only one valid source %s QL=%u", + best->name, best->rx.cd.ql); + goto out; + } + + left_ql_priority = get_ql_priority(left); + right_ql_priority = get_ql_priority(right); + + /* the left and right lists should be the same */ + if (left->priority_list != right->priority_list || + left->priority_list_count != right->priority_list_count) { + pr_err("priority lists on compared ports are different"); + return NULL; + } + /* we can use either left or right priority list */ + for (i = 0; i < left->priority_list_count; i++) { + if (left->priority_list[i] == left_ql_priority) { + best = left; + goto out; + } + if (left->priority_list[i] == right_ql_priority) { + best = right; + goto out; + } + } + + pr_debug("didn't found neither of QLs on priorities list"); +out: + if (!best) { + pr_debug("no valid source found"); + } + return best; +} + +int synce_port_ctrl_init(struct synce_port_ctrl *pc, struct config *cfg, + int rx_enabled, int extended_tlv, int recover_time, + int network_option) +{ + if (!pc) { + pr_err("%s pc is NULL", __func__); + return -ENODEV; + } + + if (!cfg) { + pr_err("%s cfg is NULL", __func__); + return -ENXIO; + } + + pc->transport = synce_transport_create(pc->name); + if (!pc->transport) { + pr_err("init synce_transport failed for port %s", pc->name); + return -ENXIO; + } + + tx_init(&pc->tx, + config_get_int(cfg, pc->name, "tx_heartbeat_msec"), + extended_tlv, pc->transport, pc->name); + if (synce_port_ctrl_thread_create(pc->tx.cd.name, &pc->tx, TX_THREAD, + &pc->tx_thread_id)) { + pr_err("tx thread create failed on port %s", pc->name); + goto transport_err; + } + + if (thread_start_wait(&pc->tx.cd)) { + pr_err("tx thread start wait failed for %s", pc->name); + goto transport_err; + } + + switch (network_option) { + case SYNCE_NETWORK_OPT_1: + pc->priority_list = O1N_priority; + pc->priority_list_count = O1N_PRIORITY_COUNT; + break; + case SYNCE_NETWORK_OPT_2: + pc->priority_list = O2N_priority; + pc->priority_list_count = O2N_PRIORITY_COUNT; + break; + default: + pr_err("wrong network option - only 1 and 2 supported"); + goto transport_err; + } + + if (rx_enabled) { + rx_init(&pc->rx, + config_get_int(cfg, pc->name, "rx_heartbeat_msec"), + extended_tlv, recover_time, pc->transport, pc->name, + cfg, network_option); + + if (synce_port_ctrl_thread_create(pc->rx.cd.name, + &pc->rx, RX_THREAD, + &pc->rx_thread_id)) { + pr_err("rx thread create failed on port %s", pc->name); + goto rx_err; + } + if (thread_start_wait(&pc->rx.cd)) { + pr_err("rx thread start wait failed for %s", pc->name); + goto rx_err; + } + } else { + pc->rx.cd.enabled = 0; + pr_debug("rx thread not needed on port %s", pc->name); + } + + return 0; +rx_err: + thread_stop_wait(&pc->tx.cd); +transport_err: + synce_transport_delete(pc->transport); + + return -ECHILD; +} + +struct synce_port_ctrl *synce_port_ctrl_create(const char *name) +{ + struct synce_port_ctrl *p = NULL; + + if (!name) { + pr_err("name not profided in %s", __func__); + return NULL; + } + + p = malloc(sizeof(struct synce_port_ctrl)); + if (!p) { + pr_err("could not alloc synce_port_ctrl for %s", name); + return NULL; + } + + memcpy(p->name, name, sizeof(p->name)); + + return p; +} + +void synce_port_ctrl_invalidate_rx_ql(struct synce_port_ctrl *pc) +{ + if (lock_mutex(&pc->rx.cd, __func__) != 0) + return; + + pc->rx.last_ql = pc->rx.ql_dnu_val; + pc->rx.cd.ql = pc->rx.ql_dnu_val; + if (pc->rx.cd.extended) { + pc->rx.cd.ext_ql.enhancedSsmCode = pc->rx.ext_ql_dnu_val; + pc->rx.last_ext_ql.enhancedSsmCode = pc->rx.ext_ql_dnu_val; + } + unlock_mutex(&pc->rx.cd, __func__); +} diff --git a/synce_port_ctrl.h b/synce_port_ctrl.h new file mode 100644 index 0000000..cae363d --- /dev/null +++ b/synce_port_ctrl.h @@ -0,0 +1,165 @@ +/** + * @file synce_port_ctrl.h + * @brief Interface between synce port and socket handling theads, used + * for controling data on the wire. Allows acquire incoming data and + * submit new outgoing data. + * TX thread is always present, RX only if required (internal_input mode). + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNC_PORT_CTRL_H +#define HAVE_SYNC_PORT_CTRL_H +#include <stdint.h> + +/* Opaque types */ +struct synce_port_ctrl; +struct config; +struct synce_msg_ext_ql; + +/** + * Check if created threads are running. + * + * @param pc Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_ctrl_running(struct synce_port_ctrl *pc); + +/** + * Stop threads, deinit given instance. + * + * @param pc Managed instance + * @return 0 on success, otherwise fault + */ +int synce_port_ctrl_destroy(struct synce_port_ctrl *pc); + +/** + * Check if QL-failed condition is present. + * + * @param pc Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_ctrl_rx_ql_failed(struct synce_port_ctrl *pc); + +/** + * Check if Do Not Use QL is present on a port. + * + * @param pc Questioned instance + * @param dnu Value to compare against + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_ctrl_rx_dnu(struct synce_port_ctrl *pc, uint8_t dnu); + +/** + * Check if QL has changed on RX. + * + * @param pc Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_ctrl_rx_ql_changed(struct synce_port_ctrl *pc); + +/** + * Check if extended TLV was acquired on RX wire. + * + * @param pc Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_ctrl_rx_ext_tlv(struct synce_port_ctrl *pc); + +/** + * Acquire last QL on the RX wire. + * + * @param pc Questioned instance + * @param ql Returned QL + * @return 0 on success, negative on failure + */ +int synce_port_ctrl_get_rx_ql(struct synce_port_ctrl *pc, uint8_t *ql); + +/** + * Acquire last extended QL on the RX wire. + * + * @param pc Questioned instance + * @param ext_ql Returned extended QL struct + * @return 0 on success, negative on failure + */ +int synce_port_ctrl_get_rx_ext_ql(struct synce_port_ctrl *pc, + struct synce_msg_ext_ql *ext_ql); + +/** + * Set QL for TX thread. + * + * @param pc Managed instance + * @param ql QL to be sent + * @return 0 on success, negative on failure + */ +int synce_port_ctrl_set_tx_ql(struct synce_port_ctrl *pc, uint8_t ql); + +/** + * Set extended QL for TX thread. + * + * @param pc Managed instance + * @param ext_ql Extended QL to be sent + * @return 0 on success, negative on failure + */ +int synce_port_ctrl_set_tx_ext_ql(struct synce_port_ctrl *pc, + struct synce_msg_ext_ql *ext_ql); +/** + * Whenever new QL was set for TX thread, rebuild must be invoked explicitly. + * + * @param pc Managed instance + * @return 0 on success, negative on failure + */ +int synce_port_ctrl_rebuild_tx(struct synce_port_ctrl *pc); + +/** + * Explicit start sending QL that was set for TX thread, used once init and set + * QL are finished. + * + * @param pc Managed instance + * @return 0 on success, negative on failure + */ +int synce_port_ctrl_enable_tx(struct synce_port_ctrl *pc); + +/** + * Check if sources given port sources are valid, than compare them, + * choose the one with higher priority in terms of its received QL. + * + * @param left Port instance for comparison + * @param right Port instance for comparison + * @return Pointer to a higher quality input port instance, + * NULL on failure or equal + */ +struct synce_port_ctrl +*synce_port_ctrl_compare_ql(struct synce_port_ctrl *left, + struct synce_port_ctrl *right); + +/** + * Initialize given instance with the given config. + * + * @param pc Instance to be initialized + * @param cfg Configuration of SYNCE type + * @param rx_enabled If RX thread shall also start + * @param extended_tlv If extended tlv was enabled + * @param recover_time What time was set for recovery [s] + * @param network_option Network option, either 0 or 1 + * @return 0 on success, otherwise fail + */ +int synce_port_ctrl_init(struct synce_port_ctrl *pc, struct config *cfg, + int rx_enabled, int extended_tlv, int recover_time, + int network_option); + +/** + * Create instance and set name of its port. + * + * @param name Port name + * @return Pointer to allocated instance + */ +struct synce_port_ctrl *synce_port_ctrl_create(const char *name); + +/** + * Invalidate QL received in the past. + * + * @param pc Port control instance + */ +void synce_port_ctrl_invalidate_rx_ql(struct synce_port_ctrl *pc); + +#endif /* HAVE_SYNC_PORT_CTRL_H */ -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:28
|
From: Arkadiusz Kubalewski <ark...@in...> Used by synce_dev to acquire current state of its DPLL. Co-developed-by: Anatolii Gerasymenko <ana...@in...> Signed-off-by: Anatolii Gerasymenko <ana...@in...> Co-developed-by: Piotr Kwapulinski <pio...@in...> Signed-off-by: Piotr Kwapulinski <pio...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - changed order of patch in patch-series - changed naming 'Sync-E' -> 'SyncE' - change 'DPLL' -> 'EEC' to be more HW agnostic v3: rebase patch series v2: updated license headers synce_dev_ctrl.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ synce_dev_ctrl.h | 64 +++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 synce_dev_ctrl.c create mode 100644 synce_dev_ctrl.h diff --git a/synce_dev_ctrl.c b/synce_dev_ctrl.c new file mode 100644 index 0000000..6285714 --- /dev/null +++ b/synce_dev_ctrl.c @@ -0,0 +1,153 @@ +/** + * @file synce_dev_ctrl.c + * @brief Interface for acquiring SyncE capable device EEC state changes + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <errno.h> +#include <stdlib.h> +#include "print.h" +#include "synce_dev_ctrl.h" + +#define EEC_STATE_STR_RETURN_SIZE 0xff +struct synce_dev_ctrl { + const char *eec_get_state_cmd; + struct eec_state_str ess; +}; + +static int eec_str_state_to_enum(struct synce_dev_ctrl *dc, char *str_state, + enum eec_state *enum_state) +{ + if (!strncmp(str_state, dc->ess.holdover, EEC_STATE_STR_RETURN_SIZE)) { + *enum_state = EEC_HOLDOVER; + } else if (!strncmp(str_state, dc->ess.locked_ho, + EEC_STATE_STR_RETURN_SIZE)) { + *enum_state = EEC_LOCKED_HO_ACQ; + } else if (!strncmp(str_state, dc->ess.locked, + EEC_STATE_STR_RETURN_SIZE)) { + *enum_state = EEC_LOCKED; + } else if (!strncmp(str_state, dc->ess.freerun, + EEC_STATE_STR_RETURN_SIZE)) { + *enum_state = EEC_FREERUN; + } else if (!strncmp(str_state, dc->ess.invalid, + EEC_STATE_STR_RETURN_SIZE)) { + *enum_state = EEC_INVALID; + } else { + *enum_state = EEC_UNKNOWN; + } + + if (*enum_state == EEC_UNKNOWN) { + pr_err("eec state missing for str_state: '%s'", str_state); + return -EINVAL; + } + + return 0; +} + +static int get_eec_state_from_cmd(struct synce_dev_ctrl *dc, + enum eec_state *state) +{ + char buf[EEC_STATE_STR_RETURN_SIZE] = {0}, *c; + FILE *fp; + int ret; + + fp = popen(dc->eec_get_state_cmd, "r"); + if (!fp) { + pr_err("failed open process: '%s'", dc->eec_get_state_cmd); + return EEC_UNKNOWN; + } + + if (!fgets(buf, sizeof(buf), fp)) { + pr_err("failed read process output: '%s'", dc->eec_get_state_cmd); + goto out; + } + + c = buf; + while (*c != '\0') { + if (*(++c) == '\n') { + *c = '\0'; + } + } + +out: + ret = pclose(fp); + if (ret) { + pr_err("process '%s' exit status: %d", + dc->eec_get_state_cmd, ret); + } + + return ret ? EEC_UNKNOWN : eec_str_state_to_enum(dc, buf, state); +} + +int synce_dev_ctrl_get_state(struct synce_dev_ctrl *dc, + enum eec_state *state) +{ + int ret = -EINVAL; + + if (!dc) { + pr_err("%s: dc is NULL", __func__); + return ret; + } + + if (!state) { + pr_err("%s: state is NULL", __func__); + return ret; + } + + if (!dc->eec_get_state_cmd) { + pr_err("%s: dc->eec_get_state_cmd is NULL", __func__); + return ret; + } + + ret = get_eec_state_from_cmd(dc, state); + if (ret || *state < EEC_INVALID || *state > EEC_HOLDOVER) { + return ret; + } + + return 0; +} + +struct synce_dev_ctrl *synce_dev_ctrl_create(void) +{ + struct synce_dev_ctrl *dc = malloc(sizeof(struct synce_dev_ctrl)); + return dc; +} + +int synce_dev_ctrl_init(struct synce_dev_ctrl *dc, const char *dev_name, + const char *eec_get_state_cmd, + struct eec_state_str *ess) +{ + if (!dc || !dev_name) { + return -ENODEV; + } + + if (!eec_get_state_cmd) { + pr_err("failure: eec_get_state_cmd is NULL on %s", dev_name); + return -ENXIO; + } + if (!ess->holdover) { + pr_err("failure: ess.holdover is NULL on %s", dev_name); + return -ENXIO; + } + if (!ess->locked_ho) { + pr_err("failure: ess.locked_ho is NULL on %s", dev_name); + return -ENXIO; + } + if (!ess->locked) { + pr_err("failure: ess.locked is NULL on %s", dev_name); + return -ENXIO; + } + if (!ess->freerun) { + pr_err("failure: ess.freerun is NULL on %s", dev_name); + return -ENXIO; + } + if (!ess->invalid) { + pr_err("failure: ess.invalid is NULL on %s", dev_name); + return -ENXIO; + } + + dc->eec_get_state_cmd = eec_get_state_cmd; + dc->ess = *ess; + + return 0; +} diff --git a/synce_dev_ctrl.h b/synce_dev_ctrl.h new file mode 100644 index 0000000..2523153 --- /dev/null +++ b/synce_dev_ctrl.h @@ -0,0 +1,64 @@ +/** + * @file synce_dev_ctrl.h + * @brief Interface for acquiring SyncE capable device EEC state changes + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_DEV_CTRL_H +#define HAVE_SYNCE_DEV_CTRL_H + +/* possible EEC states */ +enum eec_state { + EEC_UNKNOWN = -1, + EEC_INVALID, + EEC_FREERUN, + EEC_LOCKED, + EEC_LOCKED_HO_ACQ, + EEC_HOLDOVER, +}; + +/* possibe EEC state strings */ +struct eec_state_str { + const char *holdover; + const char *locked_ho; + const char *locked; + const char *freerun; + const char *invalid; +}; + +/* Opaque type */ +struct synce_dev_ctrl; + +/** + * Acquire current state of EEC of SyncE capable device. + * + * @param dc Instance of EEC device controller + * @param state State acquired from the device + * @return Zero on success, non-zero if failure + */ +int synce_dev_ctrl_get_state(struct synce_dev_ctrl *dc, + enum eec_state *state); + +/** + * Initialize EEC device controller instance. + * + * @param dc Instance of EEC device controller to be + * initialized + * @param dev_name Name of device + * @param eec_get_state_cmd A command to obtain current eec state + * @param ess Pointer to a struct holding valid eec state + strings + * @return Zero on success, non-zero if failure + */ +int synce_dev_ctrl_init(struct synce_dev_ctrl *dc, const char *dev_name, + const char *eec_get_state_cmd, + struct eec_state_str *ess); + +/** + * Allocate memory for a single EEC device controller instance. + * + * @return Pointer to allocated instance or NULL if allocation failed + */ +struct synce_dev_ctrl *synce_dev_ctrl_create(void); + +#endif /* HAVE_SYNCE_DEV_CTRL_H */ -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:27
|
From: Arkadiusz Kubalewski <ark...@in...> Used by synce_dev to interact with its ports. Co-developed-by: Anatolii Gerasymenko <ana...@in...> Signed-off-by: Anatolii Gerasymenko <ana...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - zero-init synce_port struct on creation - fix typo in if statement of synce_port_enable_recover_clock function - changed order of patch in patch-series - remove 'sync' parameter and it's helpers v3: rebase patch series v2: updated license headers synce_port.c | 471 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ synce_port.h | 174 ++++++++++++++++++++++ 2 files changed, 645 insertions(+) create mode 100644 synce_port.c create mode 100644 synce_port.h diff --git a/synce_port.c b/synce_port.c new file mode 100644 index 0000000..a3b0127 --- /dev/null +++ b/synce_port.c @@ -0,0 +1,471 @@ +/** + * @file synce_port.c + * @brief Interface between synce device and port controller module. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <stdlib.h> +#include <errno.h> +#include <sys/queue.h> +#include <net/if.h> +#include <linux/limits.h> + +#include "synce_port.h" +#include "print.h" +#include "config.h" +#include "synce_port_ctrl.h" +#include "synce_msg.h" + +enum port_mode { + NON_SYNC_MODE = 0, + SYNC_MODE, +}; + +enum port_state { + PORT_UNKNOWN = 0, + PORT_CREATED, + PORT_INITED, + PORT_RUNNING, + PORT_FAILED, + PORT_NOT_USED, +}; + +static int init_ext_tlv(struct synce_port *port, struct synce_msg_ext_ql *msg) +{ + memset(msg, 0, sizeof(*msg)); + + if (generate_clock_identity(&msg->clockIdentity, port->name)) { + pr_err("failed to generate a clock identity"); + return -ENXIO; + } + + return 0; +} + +static int ext_ql_msg_start_chain(struct synce_msg_ext_ql *ext_ql_msg) +{ + if (!ext_ql_msg) { + pr_err("ext_ql_msg is NULL"); + return -EFAULT; + } + + /* This is first node in chain */ + ext_ql_msg->cascaded_EEcs = 0; + ext_ql_msg->cascaded_eEEcs = 1; + + return 0; +} + +static int ext_ql_msg_update_chain(struct synce_port *port) +{ + int rx_ext_tlv = synce_port_ctrl_rx_ext_tlv(port->pc); + + if (rx_ext_tlv == 1) { + /* if extended tlv came on best port just increase eEEC by one */ + port->ext_ql_msg.cascaded_eEEcs++; + } else if (rx_ext_tlv == 0) { + /* If extended tlv was not on the wire, the chain has just started. + * Previous known number of EEC is 1. + * This node is first eEEC in the new chain. + * The flags set accordingly. + * This behavior is described in the SyncE specification. + */ + port->ext_ql_msg.cascaded_EEcs = 1; + port->ext_ql_msg.cascaded_eEEcs = 1; + port->ext_ql_msg.flag |= (MIXED_EEC_CHAIN_FLAG | + PARTIAL_EEC_CHAIN_FLAG); + } else { + pr_err("failed rx_ext_tlv on %s", port->name); + return -ENODEV; + } + + return 0; +} + +static int init_port_ql_val(struct synce_port *port, uint8_t forced_ql, + int network_option) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + port->ql_forced = forced_ql; + port->ql_dnu = synce_get_dnu_value(network_option, false); + port->ql = port->ql_dnu; + + return 0; +} + +static int init_port_ext_ql(struct synce_port *port, uint8_t forced_ext_ql, + int network_option) +{ + int ret; + + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + ret = init_ext_tlv(port, &port->ext_ql_msg_dnu); + if (ret) { + pr_err("init ext_ql_msg_dnu failed on %s", port->name); + return ret; + } + + port->ext_ql_msg_dnu.enhancedSsmCode = + synce_get_dnu_value(network_option, true); + ret = ext_ql_msg_start_chain(&port->ext_ql_msg_dnu); + if (ret) { + pr_err("start chain failed on %s", port->name); + return ret; + } + + memcpy(&port->ext_ql_msg, &port->ext_ql_msg_dnu, + sizeof(port->ext_ql_msg)); + memcpy(&port->ext_ql_msg_forced, &port->ext_ql_msg, + sizeof(port->ext_ql_msg_forced)); + + port->ext_ql_msg_forced.enhancedSsmCode = forced_ext_ql; + + return ret; +} + +static int new_tx_ql_and_rebuild(struct synce_port *port, uint8_t ql, + struct synce_msg_ext_ql *ext_ql_msg) +{ + int ret = synce_port_ctrl_set_tx_ql(port->pc, ql); + + if (ret) { + pr_err("set QL on %s failed", port->name); + return ret; + } + + if (ext_ql_msg) { + ret = synce_port_ctrl_set_tx_ext_ql(port->pc, + ext_ql_msg); + if (ret) { + pr_err("set ext QL on %s failed", port->name); + return ret; + } + } + + ret = synce_port_ctrl_rebuild_tx(port->pc); + if (ret) { + pr_err("set rebuild tx on %s failed", port->name); + } + + return ret; +} + +struct synce_port *synce_port_create(const char *port_name) +{ + struct synce_port *p = NULL; + + if (!port_name) { + pr_err("%s failed - port_name not provided", __func__); + return NULL; + } + + p = malloc(sizeof(struct synce_port)); + if (!p) { + pr_err("%s failed", __func__); + return NULL; + } + memset(p, 0, sizeof(struct synce_port)); + memcpy(p->name, port_name, sizeof(p->name)); + p->state = PORT_CREATED; + + return p; +} + +int synce_port_init(struct synce_port *port, struct config *cfg, + int network_option, int is_extended, + int rx_enabled, int recovery_time, + uint8_t forced_ql, uint8_t forced_ext_ql) +{ + int ret; + + if (!port) { + pr_err("%s port is NULL", __func__); + return -ENODEV; + } + + if (port->state != PORT_CREATED) { + goto err_port; + } + + if (rx_enabled) { + port->recover_clock_enable_cmd = + config_get_string(cfg, port->name, + "recover_clock_enable_cmd"); + if (!port->recover_clock_enable_cmd) { + pr_err("recover_clock_enable_cmd config not provided for %s", + port->name); + goto err_port; + } + port->recover_clock_disable_cmd = + config_get_string(cfg, port->name, + "recover_clock_disable_cmd"); + if (!port->recover_clock_disable_cmd) { + pr_err("recover_clock_disable_cmd config not provided for %s", + port->name); + goto err_port; + } + } else { + port->recover_clock_enable_cmd = NULL; + port->recover_clock_disable_cmd = NULL; + } + + port->pc = synce_port_ctrl_create(port->name); + if (!port->pc) { + pr_err("port_ctrl create failed on %s", port->name); + goto err_port; + } + + ret = init_port_ql_val(port, forced_ql, network_option); + if (ret) { + pr_err("init port QL values failed on %s", port->name); + return ret; + } + + if (is_extended) { + ret = init_port_ext_ql(port, forced_ext_ql, network_option); + if (ret) { + pr_err("init port ext QL values failed on %s", + port->name); + return ret; + } + } + + ret = synce_port_ctrl_init(port->pc, cfg, rx_enabled, is_extended, + recovery_time, network_option); + if (ret) { + pr_err("port_ctrl init failed on port %s", port->name); + return ret; + } + + ret = synce_port_set_tx_ql_dnu(port, is_extended); + if (ret) { + pr_err("tlv init failed on port %s", port->name); + return ret; + } + + ret = synce_port_ctrl_enable_tx(port->pc); + if (ret) { + pr_err("enabled tx failed on port %s", port->name); + return ret; + } + + port->state = PORT_INITED; + + return ret; +err_port: + port->state = PORT_FAILED; + return -ENODEV; +} + +void synce_port_destroy(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return; + } + + if (port->pc) { + synce_port_ctrl_destroy(port->pc); + free(port->pc); + } else { + pr_warning("%s pc is NULL", __func__); + } +} + +int synce_port_thread_running(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_running(port->pc); +} + +int synce_port_rx_ql_failed(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_rx_ql_failed(port->pc); +} + +int synce_port_rx_ql_changed(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_rx_ql_changed(port->pc); +} + +int synce_port_set_tx_ql_dnu(struct synce_port *port, int extended) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return new_tx_ql_and_rebuild(port, port->ql_dnu, + extended ? + &port->ext_ql_msg_dnu : NULL); +} + +int synce_port_set_tx_ql_forced(struct synce_port *port, int extended) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return new_tx_ql_and_rebuild(port, port->ql_forced, + extended ? + &port->ext_ql_msg_forced : NULL); +} + +int synce_port_set_tx_ql_from_best_input(struct synce_port *port, + struct synce_port *best_p, + int extended) +{ + struct synce_msg_ext_ql rx_ext_ql_msg; + int ret = -EFAULT; + uint8_t rx_ql; + + if (!port) { + pr_err("%s port is NULL", __func__); + return ret; + } + + if (!best_p) { + pr_err("%s best_p is NULL", __func__); + return ret; + } + + ret = synce_port_ctrl_get_rx_ql(best_p->pc, &rx_ql); + if (ret) { + pr_err("get rx QL failed on %s", best_p->name); + return ret; + } + port->ql = rx_ql; + + if (extended) { + ret = synce_port_ctrl_get_rx_ext_ql(best_p->pc, + &rx_ext_ql_msg); + if (ret) { + pr_err("get ext rx QL failed on %s", best_p->name); + return ret; + } + memcpy(&port->ext_ql_msg, &rx_ext_ql_msg, + sizeof(port->ext_ql_msg)); + ret = generate_clock_identity(&port->ext_ql_msg.clockIdentity, + port->name); + if (ret) { + pr_err("failed to generate clock identity on %s", + port->name); + return ret; + } + + ret = ext_ql_msg_update_chain(port); + if (ret) { + pr_err("failed to update chain on %s", + port->name); + return ret; + } + } + ret = new_tx_ql_and_rebuild(port, port->ql, + extended ? &port->ext_ql_msg : NULL); + + return ret; +} + +int synce_port_is_rx_dnu(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EFAULT; + } + + return synce_port_ctrl_rx_dnu(port->pc, port->ql_dnu); +} + +struct synce_port *synce_port_compare_ql(struct synce_port *left, + struct synce_port *right) +{ + struct synce_port_ctrl *best; + + best = synce_port_ctrl_compare_ql(left ? left->pc : NULL, + right ? right->pc : NULL); + if (!best) { + return NULL; + } + + if (left && best == left->pc) { + return left; + } else if (right && best == right->pc) { + return right; + } + + return NULL; +} + +const char *synce_port_get_name(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return NULL; + } + + return port->name; +} + +int synce_port_enable_recover_clock(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EINVAL; + } + + if (!port->recover_clock_enable_cmd) { + pr_err("recover_clock_enable_cmd is null on %s", port->name); + return -EINVAL; + } + + pr_debug("using recover_clock_enable_cmd: %s on %s", + port->recover_clock_enable_cmd, port->name); + + return system(port->recover_clock_enable_cmd); +} + +int synce_port_disable_recover_clock(struct synce_port *port) +{ + if (!port) { + pr_err("%s port is NULL", __func__); + return -EINVAL; + } + + if (!port->recover_clock_disable_cmd) { + pr_err("recover_clock_disable_cmd is null on %s", port->name); + return -EINVAL; + } + + pr_debug("using recover_clock_disable_cmd: %s on %s", + port->recover_clock_disable_cmd, port->name); + + return system(port->recover_clock_disable_cmd); +} + +void synce_port_invalidate_rx_ql(struct synce_port *port) +{ + synce_port_ctrl_invalidate_rx_ql(port->pc); +} diff --git a/synce_port.h b/synce_port.h new file mode 100644 index 0000000..b88d96e --- /dev/null +++ b/synce_port.h @@ -0,0 +1,174 @@ +/** + * @file synce_port.h + * @brief Interface between synce device and port controller module. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_PORT_H +#define HAVE_SYNCE_PORT_H +#include <stdint.h> +#include "synce_msg.h" + +struct synce_dev; +struct config; + +struct synce_port { + LIST_ENTRY(synce_port) list; + int sync_mode; + int state; + struct synce_port_ctrl *pc; + uint8_t ql; + uint8_t ql_dnu; + uint8_t ql_forced; + struct synce_msg_ext_ql ext_ql_msg; + struct synce_msg_ext_ql ext_ql_msg_dnu; + struct synce_msg_ext_ql ext_ql_msg_forced; + char name[IF_NAMESIZE]; + char *recover_clock_enable_cmd; + char *recover_clock_disable_cmd; +}; + +/** + * Alloc memory for a single synce_port instance. + * + * @param port_name Human readable name of a port + * @return Pointer to allocated instance + */ +struct synce_port *synce_port_create(const char *port_name); + +/** + * Initialize synce device capable port after port was created. + * + * @param port synce_port instance to be initialized + * @param cfg Configuration struct based on SYNCE type, + * must hold properities of the configured port. + * @param network_option Network option that shall be used on the device + * @param is_extended If extended tlv support is on + * @param rx_enabled If rx of ESMC shall start + * @param recovery_time Seconds for period of recovering from QL-failed + * state. + * @param forced_ql Value of QL when QL is forced for the device, + * used in external input mode + * @param forced_ext_ql Value of ext QL when QL is forced for the + * device, used in external input mode + * @return 0 on success, failure otherwise + */ +int synce_port_init(struct synce_port *port, struct config *cfg, + int network_option, int is_extended, + int rx_enabled, int recovery_time, + uint8_t forced_ql, uint8_t forced_ext_ql); + +/** + * Free resource under the synce_port instance. Caller shall free the passed + * pointer afterwards. + * + * @param port Pointer to the port being released + */ +void synce_port_destroy(struct synce_port *port); + +/** + * Check if port ctrl threads are running. + * + * @param port Questioned port + * @return 0 if false, otherwise true + */ +int synce_port_thread_running(struct synce_port *port); + +/** + * Check if QL-failed condition is present. + * + * @param port Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_rx_ql_failed(struct synce_port *port); + +/** + * Check if QL has changed on RX. + * + * @param port Questioned instance + * @return 1 if true, 0 if false, negative on failure + */ +int synce_port_rx_ql_changed(struct synce_port *port); + +/** + * Set QL-DNU on TX TLV of associated port. + * + * @param port Managed port + * @param extended If new extended TLV shall be created + * @return 0 on success, negative otherwise + */ +int synce_port_set_tx_ql_dnu(struct synce_port *port, int extended); + +/** + * Set QL from config file on TX TLV of associated port. Useful in + * external_input scenario. + * + * + * @param port Managed port + * @param extended If new extended TLV shall be created + * @return 0 on success, negative otherwise + */ +int synce_port_set_tx_ql_forced(struct synce_port *port, int extended); + +/** + * Set QL for TX thread - but copy QL from best port. + * + * @param port Managed instance + * @param best_p Best port instance + * @param extended If new extended TLV shall be created + * @return 0 on success, negative on failure + */ +int synce_port_set_tx_ql_from_best_input(struct synce_port *port, + struct synce_port *best_p, + int extended); + +/** + * Check if given port has Do Not Use QL. + * + * @param port Questioned instance + * @return 1 if DNU is present, 0 if not, negative on failure + */ +int synce_port_is_rx_dnu(struct synce_port *port); + +/** + * Compare left with right port, which has higher incoming Quality Level. + * + * @param left Port instance for comparison + * @param righ Port instance for comparison + * @return Pointer to best QL instance, NULL on failure or equal + */ +struct synce_port *synce_port_compare_ql(struct synce_port *left, + struct synce_port *right); + +/** + * Get name of a port. + * + * @param port Questioned instance + * @return Name of a port + */ +const char *synce_port_get_name(struct synce_port *port); + +/** + * Enable recover clock on a port. + * + * @param port Questioned instance + * @return 0 on success, negative on failure + */ +int synce_port_enable_recover_clock(struct synce_port *port); + +/** + * Enable recover clock on a port. + * + * @param port Questioned instance + * @return 0 on success, negative on failure + */ +int synce_port_disable_recover_clock(struct synce_port *port); + +/** + * Invalidate QL received on the port in the past. + * + * @param port Questioned instance + */ +void synce_port_invalidate_rx_ql(struct synce_port *port); + +#endif /* HAVE_SYNCE_PORT_H */ -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:29
|
From: Arkadiusz Kubalewski <ark...@in...> synce_dev interface allows creation, initialization, stepping and destroying of a single SyncE device. Newly created device must be given a device name (same as in config file). After creation the SyncE device is initialized with config struct, where device-level configuration for this device must exists. All ports belonging to this device are also initialized (at least one port configured is required). Once initialized SyncE device is ready for stepping. Each step will: - verify if device is in running state - acquire current state of the DPLL - performs actual step of a device, either as internal or external powered frequency provider for all the ports confgiured - remove 'internal_mode'/'external_input' mode parameters and change them to string "input_mode" - remove 'sync' parameter and it's helpers Destroying the SyncE device releases all its resources. Co-developed-by: Anatolii Gerasymenko <ana...@in...> Signed-off-by: Anatolii Gerasymenko <ana...@in...> Co-developed-by: Piotr Kwapulinski <pio...@in...> Signed-off-by: Piotr Kwapulinski <pio...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - changed order of patch in patch-series - changed naming 'Sync-E' -> 'SyncE' - change 'DPLL' -> 'EEC' to be more HW agnostic - remove 'sync' parameter synce_dev.c | 624 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ synce_dev.h | 64 +++++++ 2 files changed, 688 insertions(+) create mode 100644 synce_dev.c create mode 100644 synce_dev.h diff --git a/synce_dev.c b/synce_dev.c new file mode 100644 index 0000000..538addf --- /dev/null +++ b/synce_dev.c @@ -0,0 +1,624 @@ +/** + * @file synce_dev.c + * @brief Interface for handling SyncE capable devices and its ports + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <stdlib.h> +#include <sys/queue.h> +#include <net/if.h> +#include <errno.h> +#include "synce_dev.h" +#include "print.h" +#include "config.h" +#include "util.h" +#include "synce_port.h" +#include "missing.h" +#include "synce_dev_ctrl.h" +#include "synce_msg.h" + +struct interface { + STAILQ_ENTRY(interface) list; +}; + +struct synce_dev_ops { + int (*update_ql)(struct synce_dev *dev); + int (*step)(struct synce_dev *dev); +}; + +enum synce_dev_state { + DEVICE_UNKNOWN, + DEVICE_CREATED, + DEVICE_INITED, + DEVICE_RUNNING, + DEVICE_FAILED, +}; + +enum synce_input_mode { + INPUT_MODE_LINE, + INPUT_MODE_EXTERNAL +}; + +struct synce_dev { + LIST_ENTRY(synce_dev) list; + enum synce_dev_state state; + char name[IF_NAMESIZE]; + LIST_HEAD(synce_ports_head, synce_port) ports; + struct synce_port *best_source; + int num_ports; + int input_mode; + int network_option; + uint8_t ql; + uint8_t ext_ql; + int extended_tlv; + int recover_time; + enum eec_state d_state; + enum eec_state last_d_state; + struct synce_dev_ctrl *dc; + struct synce_dev_ops ops; +}; + +static int add_port(struct synce_dev *dev, struct synce_port *port) +{ + struct synce_port *port_iter, *last_port = NULL; + + LIST_FOREACH(port_iter, &dev->ports, list) { + last_port = port_iter; + } + + if (last_port) { + LIST_INSERT_AFTER(last_port, port, list); + } else { + LIST_INSERT_HEAD(&dev->ports, port, list); + } + return 0; +} + +static int rx_enabled(struct synce_dev *dev) +{ + return dev->input_mode == INPUT_MODE_LINE; +} + +static void destroy_ports(struct synce_dev *dev) +{ + struct synce_port *port, *tmp; + + LIST_FOREACH_SAFE(port, &dev->ports, list, tmp) { + synce_port_destroy(port); + LIST_REMOVE(port, list); + free(port); + } + dev->num_ports = 0; +} + +static void destroy_dev_ctrl(struct synce_dev *dev) +{ + free(dev->dc); + dev->dc = NULL; +} + +static int init_ports(int *count, struct synce_dev *dev, struct config *cfg) +{ + struct interface *iface; + struct synce_port *port; + const char *port_name; + + *count = 0; + + STAILQ_FOREACH(iface, &cfg->interfaces, list) { + /* given device takes care only of its child ports */ + if (interface_se_has_parent_dev(iface) && + (strncmp(interface_se_get_parent_dev_label(iface), + dev->name, sizeof(dev->name)) == 0)) { + port_name = interface_name(iface); + + port = synce_port_create(port_name); + if (!port) { + pr_err("failed to create port %s on device %s", + port_name, dev->name); + continue; + } + + if (synce_port_init(port, cfg, dev->network_option, + dev->extended_tlv, rx_enabled(dev), + dev->recover_time, dev->ql, + dev->ext_ql)) { + pr_err("failed to configure port %s on device %s", + port_name, dev->name); + synce_port_destroy(port); + continue; + } + + if (add_port(dev, port)) { + pr_err("failed to add port %s to device %s", + port_name, dev->name); + synce_port_destroy(port); + continue; + } else { + (*count)++; + pr_debug("port %s added on device %s addr %p", + port_name, dev->name, port); + } + } + } + + if (*count == 0) { + pr_err("device %s has no ports configured", dev->name); + return -ENODEV; + } + + return 0; +} + +static void update_dev_state(struct synce_dev *dev) +{ + struct synce_port *p; + int count = 0; + + LIST_FOREACH(p, &dev->ports, list) { + if (synce_port_thread_running(p)) { + count++; + } + } + + if (dev->num_ports == count) { + dev->state = DEVICE_RUNNING; + } else { + pr_warning("found %d ports running - %d configured on %s", + count, dev->num_ports, dev->name); + } +} + +static int port_set_dnu(struct synce_port *p, int extended_tlv) +{ + int ret; + + if (!p) { + pr_err("%s port is NULL", __func__); + ret = -EFAULT; + return ret; + } + + ret = synce_port_set_tx_ql_dnu(p, extended_tlv); + if (ret) { + pr_err("set tx DNU fail on %s", synce_port_get_name(p)); + return ret; + } + + return ret; +} + +static int port_set_ql_external_input(struct synce_port *p, int extended) +{ + int ret = synce_port_set_tx_ql_forced(p, extended); + + if (ret) { + pr_err("set QL external failed"); + return ret; + } + + return ret; +} + +static int update_ql_external_input(struct synce_dev *dev) +{ + struct synce_port *p; + int ret = 0; + + LIST_FOREACH(p, &dev->ports, list) { + if (dev->d_state == EEC_HOLDOVER) { + ret = port_set_dnu(p, dev->extended_tlv); + } else if (dev->d_state == EEC_LOCKED || + dev->d_state == EEC_LOCKED_HO_ACQ) { + ret = port_set_ql_external_input(p, dev->extended_tlv); + } + + if (ret) { + pr_err("update QL failed d_state %d, err:%d on %s", + dev->d_state, ret, dev->name); + break; + } + + } + + return ret; +} + +static int port_set_ql_line_input(struct synce_dev *dev, + struct synce_port *p, + struct synce_port *best_p) +{ + int ret = synce_port_set_tx_ql_from_best_input(p, best_p, + dev->extended_tlv); + + if (ret) { + pr_err("set QL failed"); + return ret; + } + + if (!ret) { + pr_debug("%s on %s", __func__, dev->name); + } + + return ret; +} + +static int update_ql_line_input(struct synce_dev *dev) +{ + struct synce_port *p, *best_p = dev->best_source; + int ret = 0; + + LIST_FOREACH(p, &dev->ports, list) { + if (dev->d_state == EEC_HOLDOVER) { + pr_debug("act on EEC_HOLDOVER for %s", + synce_port_get_name(p)); + ret = port_set_dnu(p, dev->extended_tlv); + if (ret) { + pr_err("%s set DNU failed on %s", + __func__, dev->name); + return ret; + } + } else if ((dev->d_state == EEC_LOCKED || + dev->d_state == EEC_LOCKED_HO_ACQ) && best_p) { + pr_debug("act on EEC_LOCKED/EEC_LOCKED_HO_ACQ for %s", + synce_port_get_name(p)); + /* on best port send DNU, all the others + * propagate what came from best source + */ + if (p != best_p) { + ret = port_set_ql_line_input(dev, p, + best_p); + } else { + ret = port_set_dnu(p, dev->extended_tlv); + } + + if (ret) { + pr_err("%s set failed on %s", + __func__, dev->name); + return ret; + } + } else { + pr_debug("nothing to do for %s d_state %d, best_p %p", + synce_port_get_name(p), dev->d_state, best_p); + } + } + + return ret; +} + +static void detach_port_eec(struct synce_port *port, struct synce_dev *dev) +{ + int ret = synce_port_disable_recover_clock(port); + + if (ret) { + pr_err("disable recover clock cmd failed on %s", dev->name); + return; + } +} + +static void force_all_eecs_detach(struct synce_dev *dev) +{ + enum eec_state state; + struct synce_port *p; + + LIST_FOREACH(p, &dev->ports, list) { + pr_debug("trying to detach EEC RCLK for %s", + synce_port_get_name(p)); + detach_port_eec(p, dev); + } + + if (synce_dev_ctrl_get_state(dev->dc, &state)) { + pr_err("failed getting EEC state"); + dev->last_d_state = EEC_UNKNOWN; + dev->d_state = EEC_UNKNOWN; + } else { + dev->last_d_state = state; + dev->d_state = state; + } +}; + +static void dev_update_ql(struct synce_dev *dev) +{ + if (dev->ops.update_ql(dev)) { + pr_err("update QL fail on %s", dev->name); + } +} + +static int rx_ql_changed(struct synce_dev *dev) +{ + struct synce_port *p; + int ret = 0; + + LIST_FOREACH(p, &dev->ports, list) { + ret = synce_port_rx_ql_changed(p); + if (ret) { + break; + } + } + + return ret; +} + +static struct synce_port *find_dev_best_source(struct synce_dev *dev) +{ + struct synce_port *p, *best_p = dev->best_source; + + LIST_FOREACH(p, &dev->ports, list) { + if (best_p != p) { + if (synce_port_compare_ql(best_p, p) == p) { + pr_debug("old best %s replaced by %s on %s", + synce_port_get_name(best_p), + synce_port_get_name(p), dev->name); + best_p = p; + } + } + } + + if (best_p) { + if (synce_port_is_rx_dnu(best_p)) { + return NULL; + } + } + + return best_p; +} + +static int set_input_source(struct synce_dev *dev, + struct synce_port *new_best_source) +{ + const char *best_name = synce_port_get_name(new_best_source); + int ret; + + if (!best_name) { + pr_err("get best input name failed on %s", dev->name); + return -ENXIO; + } + + ret = synce_port_enable_recover_clock(new_best_source); + if (ret) { + pr_err("enable recover clock cmd failed on %s", dev->name); + return ret; + } + + return ret; +} + +static int act_on_d_state(struct synce_dev *dev) +{ + int ret = 0; + + if (dev->d_state != dev->last_d_state) { + ret = dev->ops.update_ql(dev); + if (ret) { + pr_err("update QL fail on %s", dev->name); + } else { + dev->last_d_state = dev->d_state; + pr_debug("%s QL updated on %s", __func__, dev->name); + } + } + + return ret; +} + +static int dev_step_external_input(struct synce_dev *dev) +{ + return act_on_d_state(dev); +} + +static void choose_best_source(struct synce_dev *dev) +{ + struct synce_port *new_best; + + new_best = find_dev_best_source(dev); + if (!new_best) { + pr_info("best source not found on %s", dev->name); + force_all_eecs_detach(dev); + dev_update_ql(dev); + dev->best_source = NULL; + } else if (new_best != dev->best_source) { + force_all_eecs_detach(dev); + if (set_input_source(dev, new_best)) { + pr_err("set best source failed on %s", + dev->name); + } else { + /* if input source is changing + * current input is invalid, send DNU and wait + * for EEC being locked in further dev_step + */ + dev_update_ql(dev); + /* EEC was invalidated we can now set new + * best_source for further use + */ + dev->best_source = new_best; + } + } else { + pr_info("clock source has not changed on %s", dev->name); + /* no port change, just update QL on TX */ + dev_update_ql(dev); + + } +} + +static int dev_step_line_input(struct synce_dev *dev) +{ + int ret; + + ret = act_on_d_state(dev); + if (ret) { + pr_err("act on d_state fail on %s", dev->name); + return ret; + } + + if (rx_ql_changed(dev)) { + choose_best_source(dev); + } else if (dev->best_source) { + if (synce_port_rx_ql_failed(dev->best_source)) { + synce_port_invalidate_rx_ql(dev->best_source); + force_all_eecs_detach(dev); + dev_update_ql(dev); + dev->best_source = NULL; + choose_best_source(dev); + } + } + + return ret; +} + +static void init_ops(struct synce_dev *dev) +{ + if (rx_enabled(dev)) { + dev->ops.update_ql = &update_ql_line_input; + dev->ops.step = &dev_step_line_input; + } else { + dev->ops.update_ql = &update_ql_external_input; + dev->ops.step = &dev_step_external_input; + } +} + +#define INPUT_MODE_LINE_STRING "line" +#define INPUT_MODE_EXTERNAL_STRING "external" +int synce_dev_init(struct synce_dev *dev, struct config *cfg) +{ + const char *eec_get_state_cmd, *input_mode; + struct eec_state_str ess; + int count, ret; + + if (dev->state != DEVICE_CREATED) { + goto err; + } + + LIST_INIT(&dev->ports); + input_mode = config_get_string(cfg, dev->name, "input_mode"); + dev->ql = config_get_int(cfg, dev->name, "external_input_QL"); + dev->ext_ql = config_get_int(cfg, dev->name, "external_input_ext_QL"); + dev->extended_tlv = config_get_int(cfg, dev->name, "extended_tlv"); + dev->network_option = config_get_int(cfg, dev->name, "network_option"); + dev->recover_time = config_get_int(cfg, dev->name, "recover_time"); + dev->best_source = NULL; + eec_get_state_cmd = config_get_string(cfg, dev->name, "eec_get_state_cmd"); + ess.holdover = config_get_string(cfg, dev->name, "eec_holdover_value"); + ess.locked_ho = config_get_string(cfg, dev->name, "eec_locked_ho_value"); + ess.locked = config_get_string(cfg, dev->name, "eec_locked_value"); + ess.freerun = config_get_string(cfg, dev->name, "eec_freerun_value"); + ess.invalid = config_get_string(cfg, dev->name, "eec_invalid_value"); + dev->dc = synce_dev_ctrl_create(); + if (!dev->dc) { + pr_err("device_ctrl create fail on %s", dev->name); + goto err; + } + + if (!strncmp(input_mode, INPUT_MODE_LINE_STRING, + sizeof(INPUT_MODE_LINE_STRING))) { + dev->input_mode = INPUT_MODE_LINE; + } else if (!strncmp(input_mode, INPUT_MODE_EXTERNAL_STRING, + sizeof(INPUT_MODE_EXTERNAL_STRING))) { + dev->input_mode = INPUT_MODE_EXTERNAL; + } else { + pr_err("input_mode not supported for %s", dev->name); + goto err; + } + + ret = synce_dev_ctrl_init(dev->dc, dev->name, eec_get_state_cmd, &ess); + if (ret) { + pr_err("synce_dev_ctrl init ret %d on %s", ret, dev->name); + goto err; + } + + if (init_ports(&count, dev, cfg)) + goto err; + + init_ops(dev); + dev->num_ports = count; + dev->state = DEVICE_INITED; + + dev->d_state = EEC_HOLDOVER; + dev->ops.update_ql(dev); + + /* in case somebody manually set recovered clock before */ + if (dev->input_mode == INPUT_MODE_LINE) { + force_all_eecs_detach(dev); + } + pr_info("inited num_ports %d for %s", count, dev->name); + + return 0; + +err: + dev->state = DEVICE_FAILED; + pr_err("%s failed for %s", __func__, dev->name); + return -ENODEV; +} + +struct synce_dev *synce_dev_create(const char *dev_name) +{ + struct synce_dev *dev = NULL; + + if (!dev_name) { + return NULL; + } + + dev = malloc(sizeof(struct synce_dev)); + if (!dev) { + return NULL; + } + + memcpy(dev->name, dev_name, sizeof(dev->name)); + dev->state = DEVICE_CREATED; + dev->d_state = EEC_UNKNOWN; + dev->last_d_state = EEC_UNKNOWN; + pr_debug("created %s", dev->name); + + return dev; +} + +int synce_dev_step(struct synce_dev *dev) +{ + int ret = -EFAULT; + + if (!dev) { + pr_err("%s dev is NULL", __func__); + return ret; + } + + update_dev_state(dev); + if (dev->state != DEVICE_RUNNING) { + pr_err("dev %s is not running", dev->name); + return ret; + } + + ret = synce_dev_ctrl_get_state(dev->dc, &dev->d_state); + if (ret) { + pr_warning("could not acquire eec state on %s", dev->name); + return ret; + } + + ret = dev->ops.step(dev); + + return ret; +} + +const char *synce_dev_name(struct synce_dev *dev) +{ + return dev->name; +} + +int synce_dev_is_running(struct synce_dev *dev) +{ + update_dev_state(dev); + + return !!(dev->state & DEVICE_RUNNING); +} + +void synce_dev_destroy(struct synce_dev *dev) +{ + if (!dev) { + pr_err("%s dev is NULL", __func__); + return; + } + + if (dev->input_mode == INPUT_MODE_LINE && dev->state != DEVICE_FAILED) { + force_all_eecs_detach(dev); + } + + destroy_ports(dev); + destroy_dev_ctrl(dev); +} diff --git a/synce_dev.h b/synce_dev.h new file mode 100644 index 0000000..5b76306 --- /dev/null +++ b/synce_dev.h @@ -0,0 +1,64 @@ +/** + * @file synce_dev.h + * @brief Interface for handling SyncE capable devices and its ports + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_DEV_H +#define HAVE_SYNCE_DEV_H + +#include <stdint.h> + +struct config; +struct synce_dev; + +/** + * Initialize SyncE device and its ports after device was created. + * + * @param dev Device to be initialized + * @param cfg Configuration struct based on SYNCE type, must hold + * properities of configured device ports + * @return 0 on success, failure otherwise + */ +int synce_dev_init(struct synce_dev *dev, struct config *cfg); + +/** + * Alloc memory for a single SyncE device. + * + * @param dev_name Human readable name of a device + * @return 0 on success, failure otherwise + */ +struct synce_dev *synce_dev_create(const char *dev_name); + +/** + * Step a SyncE device. Probe for events, changes and act on them. + * + * @param dev Device to be stepped + * @return 0 on success, failure otherwise + */ +int synce_dev_step(struct synce_dev *dev); + +/** + * Acquire SyncE device name. + * + * @param dev Questioned SyncE device instance + * @return The device name + */ +const char *synce_dev_name(struct synce_dev *dev); + +/** + * Clean-up on memory allocated for device and its ports. + * + * @param dev SyncE device to be cleared + */ +void synce_dev_destroy(struct synce_dev *dev); + +/** + * Check if SyncE device is running. + * + * @param dev Questioned SyncE device + * @return 0 if false, 1 if true + */ +int synce_dev_is_running(struct synce_dev *dev); + +#endif -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:30
|
From: Arkadiusz Kubalewski <ark...@in...> Allow config interface to parse SyncE related config files. Co-developed-by: Piotr Kwapulinski <pio...@in...> Signed-off-by: Piotr Kwapulinski <pio...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - changed order of patch in patch-series - remove 'internal_mode'/'external_input' mode parameters and change them to string parameter 'input_mode' - change 'DPLL' -> 'EEC' to be more HW agnostic - remove 'sync' parameter v3: rebase patch series v2: updated license headers config.c | 195 ++++++++++++++++++++++++++++++++++++++++++------------ config.h | 8 +++ configs/synce.cfg | 174 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+), 42 deletions(-) create mode 100644 configs/synce.cfg diff --git a/config.c b/config.c index 6ba9996..fa99118 100644 --- a/config.c +++ b/config.c @@ -45,6 +45,7 @@ enum config_section { GLOBAL_SECTION, UC_MTAB_SECTION, PORT_SECTION, + DEVICE_SECTION, UNKNOWN_SECTION, }; @@ -72,6 +73,7 @@ typedef union { #define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */ #define CFG_ITEM_PORT (1 << 2) /* item may appear in port sections */ #define CFG_ITEM_DYNSTR (1 << 4) /* string value dynamically allocated */ +#define CFG_ITEM_DEVICE (1 << 8) /* item may appear in device sections */ struct config_item { char label[CONFIG_LABEL_SIZE]; @@ -83,12 +85,18 @@ struct config_item { any_t max; }; -#define N_CONFIG_ITEMS (sizeof(config_tab) / sizeof(config_tab[0])) +#define N_CONFIG_ITEMS_PTP (sizeof(config_tab_ptp) / sizeof(config_tab_ptp[0])) +#define N_CONFIG_ITEMS_SYNCE ((sizeof(config_tab_synce) / \ + sizeof(config_tab_synce[0]))) + +#define PORT_TO_FLAG(_port) (_port == PORT_SECTION ? CFG_ITEM_PORT : \ + _port == DEVICE_SECTION ? CFG_ITEM_DEVICE : \ + CFG_ITEM_STATIC) #define CONFIG_ITEM_DBL(_label, _port, _default, _min, _max) { \ .label = _label, \ .type = CFG_TYPE_DOUBLE, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .val.d = _default, \ .min.d = _min, \ .max.d = _max, \ @@ -96,14 +104,14 @@ struct config_item { #define CONFIG_ITEM_ENUM(_label, _port, _default, _table) { \ .label = _label, \ .type = CFG_TYPE_ENUM, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .tab = _table, \ .val.i = _default, \ } #define CONFIG_ITEM_INT(_label, _port, _default, _min, _max) { \ .label = _label, \ .type = CFG_TYPE_INT, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .val.i = _default, \ .min.i = _min, \ .max.i = _max, \ @@ -111,33 +119,39 @@ struct config_item { #define CONFIG_ITEM_STRING(_label, _port, _default) { \ .label = _label, \ .type = CFG_TYPE_STRING, \ - .flags = _port ? CFG_ITEM_PORT : 0, \ + .flags = PORT_TO_FLAG(_port), \ .val.s = _default, \ } #define GLOB_ITEM_DBL(label, _default, min, max) \ - CONFIG_ITEM_DBL(label, 0, _default, min, max) + CONFIG_ITEM_DBL(label, GLOBAL_SECTION, _default, min, max) #define GLOB_ITEM_ENU(label, _default, table) \ - CONFIG_ITEM_ENUM(label, 0, _default, table) + CONFIG_ITEM_ENUM(label, GLOBAL_SECTION, _default, table) #define GLOB_ITEM_INT(label, _default, min, max) \ - CONFIG_ITEM_INT(label, 0, _default, min, max) + CONFIG_ITEM_INT(label, GLOBAL_SECTION, _default, min, max) #define GLOB_ITEM_STR(label, _default) \ - CONFIG_ITEM_STRING(label, 0, _default) + CONFIG_ITEM_STRING(label, GLOBAL_SECTION, _default) #define PORT_ITEM_DBL(label, _default, min, max) \ - CONFIG_ITEM_DBL(label, 1, _default, min, max) + CONFIG_ITEM_DBL(label, PORT_SECTION, _default, min, max) #define PORT_ITEM_ENU(label, _default, table) \ - CONFIG_ITEM_ENUM(label, 1, _default, table) + CONFIG_ITEM_ENUM(label, PORT_SECTION, _default, table) #define PORT_ITEM_INT(label, _default, min, max) \ - CONFIG_ITEM_INT(label, 1, _default, min, max) + CONFIG_ITEM_INT(label, PORT_SECTION, _default, min, max) #define PORT_ITEM_STR(label, _default) \ - CONFIG_ITEM_STRING(label, 1, _default) + CONFIG_ITEM_STRING(label, PORT_SECTION, _default) + +#define DEV_ITEM_INT(label, _default, min, max) \ + CONFIG_ITEM_INT(label, DEVICE_SECTION, _default, min, max) + +#define DEV_ITEM_STR(label, _default) \ + CONFIG_ITEM_STRING(label, DEVICE_SECTION, _default) static struct config_enum clock_servo_enu[] = { { "pi", CLOCK_SERVO_PI }, @@ -224,7 +238,7 @@ static struct config_enum bmca_enu[] = { { NULL, 0 }, }; -struct config_item config_tab[] = { +struct config_item config_tab_ptp[] = { PORT_ITEM_INT("announceReceiptTimeout", 3, 2, UINT8_MAX), PORT_ITEM_ENU("asCapable", AS_CAPABLE_AUTO, as_capable_enu), GLOB_ITEM_INT("assume_two_step", 0, 0, 1), @@ -347,7 +361,35 @@ struct config_item config_tab[] = { GLOB_ITEM_INT("write_phase_mode", 0, 0, 1), }; +struct config_item config_tab_synce[] = { + GLOB_ITEM_INT("logging_level", LOG_INFO, PRINT_LEVEL_MIN, PRINT_LEVEL_MAX), + GLOB_ITEM_STR("message_tag", NULL), + GLOB_ITEM_INT("use_syslog", 1, 0, 1), + GLOB_ITEM_STR("userDescription", ""), + GLOB_ITEM_INT("verbose", 0, 0, 1), + DEV_ITEM_STR("input_mode", "line"), + DEV_ITEM_INT("external_input_QL", 0, 0, 15), + DEV_ITEM_INT("external_input_ext_QL", 0, 0, 255), + DEV_ITEM_INT("extended_tlv", 0, 0, 1), + DEV_ITEM_INT("network_option", 1, 1, 2), + DEV_ITEM_INT("recover_time", 300, 10, 720), + DEV_ITEM_STR("eec_get_state_cmd", NULL), + DEV_ITEM_STR("eec_holdover_value", NULL), + DEV_ITEM_STR("eec_locked_ho_value", NULL), + DEV_ITEM_STR("eec_locked_value", NULL), + DEV_ITEM_STR("eec_freerun_value", NULL), + DEV_ITEM_STR("eec_invalid_value", NULL), + PORT_ITEM_STR("allowed_qls", NULL), + PORT_ITEM_STR("allowed_ext_qls", NULL), + PORT_ITEM_STR("recover_clock_enable_cmd", NULL), + PORT_ITEM_STR("recover_clock_disable_cmd", NULL), + PORT_ITEM_INT("tx_heartbeat_msec", 1000, 100, 3000), + PORT_ITEM_INT("rx_heartbeat_msec", 50, 10, 500), +}; + static struct unicast_master_table *current_uc_mtab; +static struct interface *__config_create_interface(const char *name, struct config *cfg, + const char *type); static enum parser_result parse_fault_interval(struct config *cfg, const char *section, @@ -398,6 +440,8 @@ static struct config_item *config_item_alloc(struct config *cfg, } strncpy(ci->label, name, CONFIG_LABEL_SIZE - 1); ci->type = type; + ci->val.s = NULL; + ci->flags = 0; snprintf(buf, sizeof(buf), "%s.%s", section, ci->label); if (hash_insert(cfg->htab, buf, ci)) { @@ -412,8 +456,11 @@ static struct config_item *config_item_alloc(struct config *cfg, static void config_item_free(void *ptr) { struct config_item *ci = ptr; - if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR) + if (ci->type == CFG_TYPE_STRING && ci->flags & CFG_ITEM_DYNSTR + && ci->val.s != NULL) { free(ci->val.s); + ci->val.s = NULL; + } if (ci->flags & CFG_ITEM_STATIC) return; free(ci); @@ -518,10 +565,13 @@ static enum parser_result parse_section_line(char *s, enum config_section *secti current_uc_mtab = NULL; } else if (s[0] == '[') { char c; - *section = PORT_SECTION; - /* Replace square brackets with white space. */ + if (s[1] == '<') + *section = DEVICE_SECTION; + else + *section = PORT_SECTION; + /* Replace brackets with white space. */ while (0 != (c = *s)) { - if (c == '[' || c == ']') + if (c == '[' || c == ']' || c == '<' || c == '>') *s = ' '; s++; } @@ -579,7 +629,8 @@ static enum parser_result parse_item(struct config *cfg, } if (section) { - if (!(cgi->flags & CFG_ITEM_PORT)) { + if (!(cgi->flags & CFG_ITEM_PORT) && + !(cgi->flags & CFG_ITEM_DEVICE)) { return NOT_PARSED; } /* Create or update this port specific item. */ @@ -607,8 +658,9 @@ static enum parser_result parse_item(struct config *cfg, dst->val.d = df; break; case CFG_TYPE_STRING: - if (dst->flags & CFG_ITEM_DYNSTR) { + if (dst->flags & CFG_ITEM_DYNSTR && dst->val.s != NULL) { free(dst->val.s); + dst->val.s = NULL; } dst->val.s = strdup(value); if (!dst->val.s) { @@ -734,18 +786,25 @@ static void check_deprecated_options(const char **option) } } -static struct option *config_alloc_longopts(void) +static struct option *config_alloc_longopts(enum feature_type type) { - struct config_item *ci; + struct config_item *ci, *ci_tab; struct option *opts; - int i; + int i, n_items; - opts = calloc(1, (1 + N_CONFIG_ITEMS) * sizeof(*opts)); + if (type == PTP) { + ci_tab = &config_tab_ptp[0]; + n_items = N_CONFIG_ITEMS_PTP; + } else { + ci_tab = &config_tab_synce[0]; + n_items = N_CONFIG_ITEMS_SYNCE; + } + opts = calloc(1, (1 + n_items) * sizeof(*opts)); if (!opts) { return NULL; } - for (i = 0; i < N_CONFIG_ITEMS; i++) { - ci = &config_tab[i]; + for (i = 0; i < n_items; i++) { + ci = &ci_tab[i]; opts[i].name = ci->label; opts[i].has_arg = required_argument; /* Avoid bug in detection of ambiguous options in glibc */ @@ -763,6 +822,8 @@ int config_read(const char *name, struct config *cfg) char buf[1024], *line, *c; const char *option, *value; struct interface *current_port = NULL; + struct interface *current_device = NULL; + bool is_synce = (cfg->type == SYNCE); int line_num; fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r"); @@ -793,6 +854,7 @@ int config_read(const char *name, struct config *cfg) if (parse_section_line(line, ¤t_section) == PARSED_OK) { if (current_section == PORT_SECTION) { char port[17]; + if (1 != sscanf(line, " %16s", port)) { fprintf(stderr, "could not parse port name on line %d\n", line_num); @@ -801,6 +863,27 @@ int config_read(const char *name, struct config *cfg) current_port = config_create_interface(port, cfg); if (!current_port) goto parse_error; + if (is_synce) { + if (current_device) { + interface_se_set_parent_dev(current_port, + interface_name(current_device)); + } else { + goto parse_error; + } + } + } else if (current_section == DEVICE_SECTION) { + /* clear port on new device found in config */ + current_port = NULL; + char device[17]; + + if (sscanf(line, " %16s", device) != 1) { + fprintf(stderr, "could not parse device name on line %d\n", + line_num); + goto parse_error; + } + current_device = __config_create_interface(device, cfg, "device"); + if (!current_device) + goto parse_error; } continue; } @@ -820,14 +903,16 @@ int config_read(const char *name, struct config *cfg) if (parse_setting_line(line, &option, &value)) { fprintf(stderr, "could not parse line %d in %s section\n", line_num, current_section == GLOBAL_SECTION ? - "global" : interface_name(current_port)); + "global" : interface_name(current_port ? + current_port : current_device)); goto parse_error; } check_deprecated_options(&option); parser_res = parse_item(cfg, 0, current_section == GLOBAL_SECTION ? - NULL : interface_name(current_port), + NULL : interface_name(current_port ? + current_port : current_device), option, value); switch (parser_res) { case PARSED_OK: @@ -836,7 +921,8 @@ int config_read(const char *name, struct config *cfg) fprintf(stderr, "unknown option %s at line %d in %s section\n", option, line_num, current_section == GLOBAL_SECTION ? "global" : - interface_name(current_port)); + interface_name(current_port ? + current_port : current_device)); goto parse_error; case BAD_VALUE: fprintf(stderr, "%s is a bad value for option %s at line %d\n", @@ -862,7 +948,7 @@ parse_error: return -2; } -struct interface *config_create_interface(const char *name, struct config *cfg) +struct interface *__config_create_interface(const char *name, struct config *cfg, const char *type) { struct interface *iface; const char *ifname; @@ -876,7 +962,7 @@ struct interface *config_create_interface(const char *name, struct config *cfg) iface = interface_create(name); if (!iface) { - fprintf(stderr, "cannot allocate memory for a port\n"); + fprintf(stderr, "cannot allocate memory for a %s\n", type); return NULL; } STAILQ_INSERT_TAIL(&cfg->interfaces, iface, list); @@ -885,12 +971,17 @@ struct interface *config_create_interface(const char *name, struct config *cfg) return iface; } -struct config *config_create(void) +struct interface *config_create_interface(const char *name, struct config *cfg) +{ + return __config_create_interface(name, cfg, "port"); +} + +struct config *__config_create(enum feature_type type) { char buf[CONFIG_LABEL_SIZE + 8]; - struct config_item *ci; + struct config_item *ci, *ci_tab; struct config *cfg; - int i; + int i, end; cfg = calloc(1, sizeof(*cfg)); if (!cfg) { @@ -899,7 +990,7 @@ struct config *config_create(void) STAILQ_INIT(&cfg->interfaces); STAILQ_INIT(&cfg->unicast_master_tables); - cfg->opts = config_alloc_longopts(); + cfg->opts = config_alloc_longopts(type); if (!cfg->opts) { free(cfg); return NULL; @@ -912,9 +1003,18 @@ struct config *config_create(void) return NULL; } + cfg->type = type; + if (type == PTP) { + ci_tab = &config_tab_ptp[0]; + end = N_CONFIG_ITEMS_PTP; + } else { + ci_tab = &config_tab_synce[0]; + end = N_CONFIG_ITEMS_SYNCE; + } + /* Populate the hash table with global defaults. */ - for (i = 0; i < N_CONFIG_ITEMS; i++) { - ci = &config_tab[i]; + for (i = 0; i < end; i++) { + ci = &ci_tab[i]; ci->flags |= CFG_ITEM_STATIC; snprintf(buf, sizeof(buf), "global.%s", ci->label); if (hash_insert(cfg->htab, buf, ci)) { @@ -924,12 +1024,12 @@ struct config *config_create(void) } /* Perform a Built In Self Test.*/ - for (i = 0; i < N_CONFIG_ITEMS; i++) { - ci = &config_tab[i]; + for (i = 0; i < end; i++) { + ci = &ci_tab[i]; ci = config_global_item(cfg, ci->label); - if (ci != &config_tab[i]) { + if (ci != &ci_tab[i]) { fprintf(stderr, "config BIST failed at %s\n", - config_tab[i].label); + ci_tab[i].label); goto fail; } } @@ -941,6 +1041,16 @@ fail: return NULL; } +struct config *config_create(void) +{ + return __config_create(PTP); +} + +struct config *config_create_synce(void) +{ + return __config_create(SYNCE); +} + void config_destroy(struct config *cfg) { struct unicast_master_address *address; @@ -1143,8 +1253,9 @@ int config_set_string(struct config *cfg, const char *option, return -1; } ci->flags |= CFG_ITEM_LOCKED; - if (ci->flags & CFG_ITEM_DYNSTR) { + if (ci->flags & CFG_ITEM_DYNSTR && ci->val.s != NULL) { free(ci->val.s); + ci->val.s = NULL; } ci->val.s = strdup(val); if (!ci->val.s) { diff --git a/config.h b/config.h index 14d2f64..a32e007 100644 --- a/config.h +++ b/config.h @@ -32,10 +32,16 @@ #include "servo.h" #include "sk.h" +enum feature_type { + PTP = 0, + SYNCE +}; + struct config { /* configured interfaces */ STAILQ_HEAD(interfaces_head, interface) interfaces; int n_interfaces; + enum feature_type type; /* for parsing command line options */ struct option *opts; @@ -55,6 +61,8 @@ void config_destroy(struct config *cfg); struct config *config_create(void); +struct config *config_create_synce(void); + double config_get_double(struct config *cfg, const char *section, const char *option); diff --git a/configs/synce.cfg b/configs/synce.cfg new file mode 100644 index 0000000..b368876 --- /dev/null +++ b/configs/synce.cfg @@ -0,0 +1,174 @@ +# Global section is for debuging mostly +[global] +# +# Runtime options +# +logging_level 7 +use_syslog 0 +verbose 1 +message_tag [synce4l] + + +# +# Device section +# Per-device configuration +# +# User defined name of a one logical device configured for SyncE in the system. +# All the ports configured after this section will be a part of this device +# (until next device section). +[<synce1>] + +# +# Mode for synce4l operation. +# There are two currently supported modes: +# - line +# - external +# +# Input mode "line" mean the inputs recovered from the PHY's. +# The ports configured (in the port-sections [<dev name>], under the device +# section) will be monitored for the QL (Quality Level). +# QL is sent by the peer connected to the port and represents the Holdover +# performance of the peer. +# The best QL is selected and frequency recovered on that port shall be used +# to feed its frequency to all the other ports. +# +# The "external" input mode are either 1PPS from built-in GPS module or 1PPS +# from the on-board SMA connectors. Device must be pre-confiured to use this +# setting. # Before running the application, one of the external inputs shall be +# selected. This is done through the interface supplied by the NIC vendor. +# +# In this mode synce4l application is only responsible for sending +# the QL to the peers on all configured ports (where "sync = 1") +# The QL value that is sent equals configured "external_input_QL" +# (and "external_input_ext_QL" in case of "extended_tlv=1") +# +input_mode line +# input_mode external + +# +# These values are sent to the peers on configured ports ONLY when 'external' +# mode is enabled. +# Valid values are defined in Table 11-7 and Table 11-8 of recommendation +# ITU-T G.8264. +# They shall be configured appropriately so they are understood by the peer. +# +# external_input_QL corresponds to the SSM code column. +# +# external_input_ext_QL corresponds to the Enhanced SSM code column +# (is used only if "extended_tlv = 1") +# +external_input_QL 2 +external_input_ext_QL 255 + +# +# If extended TLV shall be supported on the device. +# 0 if no extended tlv shall be supported +# 1 if extended tlv shall be supported +# default: 0 +# +# In case of 0: +# - the port will always TX the non-extended TLV, for RX only +# non-extended TLV will be processed for reference signal selection +# In case of 1: +# - If port is configured with external_input=1, the TX will always use +# extended TLV (no RX is required in this case) +# - If port is configured with external_input=0 and internal_input=1, the +# TX version of TLV will be propagated from the port that was chosen as +# candidate for frequency synchronization +# +extended_tlv 1 + +# +# Which network option shall be supported +# +# 1 or 2 as defined in T-REC-G.8264 +# default: 1 +# +# This is rather per-network option, all device in SyncE network +# shall have this configured for the same value +# +network_option 1 + +# +# Seconds indicating minimum time to recover from the QL-failed state on the +# port. +# Range: 10-720 +# Default: 300 +# +# If valid QL was not received from one of the source ports within 5 seconds +# the port is no longer a valid source (marked as QL-failed) +# +# Valid QL must be received for more then "recover_time" seconds on that port +# to use its PHY recovered signal again as a valid source +# +recover_time 10 + +# +# Shell command to be executed in order to obtain current EEC status of a +# device. +# +eec_get_state_cmd cat /sys/class/net/enp1s0f0/device/cgu_state + +# +# EEC state values, must equal to values produced by stdout of +# "eec_get_state_cmd" command +# +eec_holdover_value 4 +eec_locked_ho_value 3 +eec_locked_value 2 +eec_freerun_value 1 +eec_invalid_value 0 + +# +# Port section(s) +# +# It starts per-port configuration. +# Each port (of the device) that is used for SyncE, shall have its own section +# with at least sync = 1 (which defines port as synchronous mode) +# +[enp1s0f0] + +# +# msec resolution of TX the QL from this port to the peer +# [100-3000], default:1000 (1000 = 1 second is expected by the standard) +# +# As the standard expects 1 sec, it is not recommended to use different +# than a 1000. +# +tx_heartbeat_msec 2000 + +# +# recovered PHY signal can be lost at anytime, this is msec resolution of +# reading the socket, acting on signal lost shall be done just after +# [10-500], default:50 +# +rx_heartbeat_msec 500 + +# +# Shell commands for enabling/disabling this port as main recovered clock on a +# device. +# +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f0/device/phy/synce +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f0/device/phy/synce + +# +# next configured interface for the device +# +[enp1s0f1] +sync 1 +recover_clock_enable_cmd echo 1 0 > /sys/class/net/enp1s0f1/device/phy/synce +recover_clock_disable_cmd echo 0 0 > /sys/class/net/enp1s0f1/device/phy/synce + + +############################################################ +# +# next SyncE device section +# +#[<synce2>] +#internal_input 0 +#external_input 1 + +# +# new port belonging to the "new" device +# +#[enp7s0f0] -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:36
|
From: Arkadiusz Kubalewski <ark...@in...> synce_clock interface allows creation, polling and destruction of SyncE clock, designed to step and drive SyncE logic on all configured SyncE devices. SyncE clock is created with a SyncE-type configuration, where SyncE-type configuration is prepared by parsing SyncE config file. Poll of a SyncE clock, will step each configured SyncE device. Destruction of SyncE clock releases all resources obtained by SyncE clock and devices. Co-developed-by: Piotr Kwapulinski <pio...@in...> Signed-off-by: Piotr Kwapulinski <pio...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - changed order of patch in patch-series - renamed create_clock() to create_synce_clock() - changed naming 'Sync-E' -> 'SyncE' v3: - rebase patch series v2: - changed strnlen/strncpy to snprintf for increased readability - added buffer size for recieving ESMC frames - updated license headers synce_clock.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ synce_clock.h | 39 ++++++++ 2 files changed, 323 insertions(+) create mode 100644 synce_clock.c create mode 100644 synce_clock.h diff --git a/synce_clock.c b/synce_clock.c new file mode 100644 index 0000000..bf94a55 --- /dev/null +++ b/synce_clock.c @@ -0,0 +1,284 @@ +/** + * @file synce_clock.c + * @brief Implements a SyncE clock interface. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/queue.h> + +#include "synce_clock.h" +#include "interface.h" +#include "synce_dev.h" +#include "print.h" +#include "config.h" +#include "missing.h" + +#define SYNCE_CLOCK_DELAY_USEC 20000 +#define SYNCE_CLOCK_INIT_DELAY_USEC 200000 +#define SYNCE_CLOCK_INIT_N_TRIES 10 + +struct interface { + STAILQ_ENTRY(interface) list; +}; + +struct synce_dev { + LIST_ENTRY(synce_dev) list; +}; + +enum synce_clock_state { + SYNCE_CLK_UNKNOWN = 0, + SYNCE_CLK_INITED, + SYNCE_CLK_DEV_RDY, + SYNCE_CLK_DEV_INITED, + SYNCE_CLK_RUNNING, + SYNCE_CLK_FAILED, +}; + +struct synce_clock { + int num_devices; + int state; + LIST_HEAD(devices_head, synce_dev) devices; +}; + +static struct synce_clock *create_synce_clock(void) +{ + static struct synce_clock clk; + + if (clk.state != SYNCE_CLK_UNKNOWN) { + synce_clock_destroy(&clk); + pr_info("old synce_clock destroyed"); + } + clk.state = SYNCE_CLK_INITED; + + return &clk; +} + +static void add_device(struct synce_clock *clk, struct synce_dev *dev) +{ + struct synce_dev *dev_iter, *last_dev = NULL; + + LIST_FOREACH(dev_iter, &clk->devices, list) { + last_dev = dev_iter; + } + + if (last_dev) { + LIST_INSERT_AFTER(last_dev, dev, list); + } else { + LIST_INSERT_HEAD(&clk->devices, dev, list); + } +} + +static int create_synce_devices(struct synce_clock *clk, struct config *cfg) +{ + struct interface *iface; + struct synce_dev *dev; + const char *dev_name; + int count = 0; + + if (clk->state != SYNCE_CLK_INITED) { + goto err; + } + + LIST_INIT(&clk->devices); + STAILQ_FOREACH(iface, &cfg->interfaces, list) { + /* only parent devices shall be addresed */ + if (!interface_se_has_parent_dev(iface)) { + dev_name = interface_name(iface); + dev = synce_dev_create(dev_name); + if (!dev) { + pr_err("failed to create device %s", dev_name); + continue; + } + + pr_debug("device init %s addr %p", dev_name, dev); + add_device(clk, dev); + count++; + } + } + + if (!count) { + pr_err("no devices created"); + goto err; + } + + pr_info("created num_devices: %d", count); + clk->num_devices = count; + clk->state = SYNCE_CLK_DEV_RDY; + + return 0; +err: + clk->state = SYNCE_CLK_FAILED; + return -EINVAL; +} + +static int init_synce_devices(struct synce_clock *clk, struct config *cfg) +{ + struct synce_dev *dev, *tmp; + int count = 0; + + if (clk->state != SYNCE_CLK_DEV_RDY) { + goto err; + } + + LIST_FOREACH_SAFE(dev, &clk->devices, list, tmp) { + /* Each parent device will init its ports */ + if (synce_dev_init(dev, cfg)) { + pr_err("failed to init device %s", + synce_dev_name(dev)); + synce_dev_destroy(dev); + LIST_REMOVE(dev, list); + free(dev); + continue; + } else { + pr_debug("device inited %s", synce_dev_name(dev)); + count++; + } + } + + if (count == 0) { + pr_err("no SyncE devices initialized"); + goto err; + } else if (count != clk->num_devices) { + pr_warning("initialized only %d from %d SyncE devices", + count, clk->num_devices); + clk->num_devices = count; + } + clk->state = SYNCE_CLK_DEV_INITED; + + return 0; +err: + clk->state = SYNCE_CLK_FAILED; + return -EINVAL; +} + +static void remove_failed_devices(struct synce_clock *clk) +{ + struct synce_dev *dev, *tmp; + int failed_cnt = 0; + + LIST_FOREACH_SAFE(dev, &clk->devices, list, tmp) { + if (!synce_dev_is_running(dev)) { + synce_dev_destroy(dev); + LIST_REMOVE(dev, list); + free(dev); + failed_cnt++; + } + } + clk->num_devices -= failed_cnt; + pr_warning("Found dead devices: %d", failed_cnt); + pr_info("devices still running: %d", clk->num_devices); +} + +static int verify_clock_state(struct synce_clock *clk) +{ + int i, running, timeout = SYNCE_CLOCK_INIT_N_TRIES; + struct synce_dev *dev; + + if (clk->state < SYNCE_CLK_DEV_INITED) { + return -ENODEV; + } + + /* let threads get running */ + for (i = 0; i < timeout; ++i) { + running = 0; + LIST_FOREACH(dev, &clk->devices, list) { + if (synce_dev_is_running(dev)) + running++; + } + + if (running == clk->num_devices) { + clk->state = SYNCE_CLK_RUNNING; + break; + } + usleep(SYNCE_CLOCK_INIT_DELAY_USEC); + } + + pr_debug("running num_devices %d configured %d", + running, clk->num_devices); + + /* If at least one dev is running we leave clock running + * while removing failed devices. + * Previous traces shall indicate which ones have failed. + */ + if (!running) { + pr_err("no device is running"); + return -ENODEV; + } else if (running != clk->num_devices) { + remove_failed_devices(clk); + } + + return 0; +} + +struct synce_clock *synce_clock_create(struct config *cfg) +{ + struct synce_clock *clk; + int err; + + if (!cfg) { + pr_err("%s cfg is NULL", __func__); + return NULL; + } + + clk = create_synce_clock(); + if (!clk) { + return NULL; + } + err = create_synce_devices(clk, cfg); + if (err) { + goto destroy; + } + err = init_synce_devices(clk, cfg); + if (err) { + goto destroy; + } + err = verify_clock_state(clk); + if (err) { + goto destroy; + } + + return clk; + +destroy: + synce_clock_destroy(clk); + + return NULL; +} + +void synce_clock_destroy(struct synce_clock *clk) +{ + struct synce_dev *dev, *tmp; + + pr_debug("%s", __func__); + + LIST_FOREACH_SAFE(dev, &clk->devices, list, tmp) { + synce_dev_destroy(dev); + LIST_REMOVE(dev, list); + free(dev); + } + clk->num_devices = 0; + clk->state = SYNCE_CLK_UNKNOWN; + + return; +} + +int synce_clock_poll(struct synce_clock *clk) +{ + struct synce_dev *dev; + int ret = -ENODEV; + + if (clk->state == SYNCE_CLK_RUNNING) { + LIST_FOREACH(dev, &clk->devices, list) { + ret = synce_dev_step(dev); + if (ret) { + pr_err("dev_step fail"); + } + } + } + usleep(SYNCE_CLOCK_DELAY_USEC); + + return ret; +} diff --git a/synce_clock.h b/synce_clock.h new file mode 100644 index 0000000..23931d6 --- /dev/null +++ b/synce_clock.h @@ -0,0 +1,39 @@ +/** + * @file synce_clock.h + * @brief Implements a SyncE clock interface. + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef HAVE_SYNCE_CLOCK_H +#define HAVE_SYNCE_CLOCK_H + +#include "config.h" + +/* Opaque type */ +struct synce_clock; + +/** + * Create a SyncE clock instance. + * + * @param cfg Pointer to the SYNCE-type configuration database + * @return Pointer to the single global SyncE clock instance + */ +struct synce_clock *synce_clock_create(struct config *cfg); + +/** + * Destroy resources associated with the synce clock. + * + * @param clk Pointer to synce_clock instance + */ +void synce_clock_destroy(struct synce_clock *clk); + +/** + * Poll for synce events and dispatch them. + * + * @param clk A pointer to a synce_clock instance obtained with + * synce_clock_create(). + * @return Zero on success, non-zero otherwise + */ +int synce_clock_poll(struct synce_clock *clk); + +#endif -- 2.9.5 |
From: Michal M. <mic...@in...> - 2022-07-05 13:38:38
|
From: Arkadiusz Kubalewski <ark...@in...> Add main code of synce4l - user space application implementing standard - Recommendation ITU-T G.8264/Y.1364. On init the application parses SyncE config file and initializes a synce_clock with SyncE devices and configured ports. synce_clock is later polled until application termination. Add documentation in form of Linux manual and 'synce4l_README.md'. Add synce4l to the linuxptp makefile. Co-developed-by: Andrzej Sawula <and...@in...> Signed-off-by: Andrzej Sawula <and...@in...> Co-developed-by: Piotr Kwapulinski <pio...@in...> Signed-off-by: Piotr Kwapulinski <pio...@in...> Co-developed-by: Michal Michalik <mic...@in...> Signed-off-by: Michal Michalik <mic...@in...> Signed-off-by: Arkadiusz Kubalewski <ark...@in...> --- v4: - changed order of patch in patch-series - change default heartbeat to 1 second in example config - remove 'internal_mode'/'external_input' mode parameters and change - remove 'sync' parameter them to string parameter 'input_mode' - documentation change - change 'DPLL' -> 'EEC' to be more HW agnostic in documentation v3: - rebase patch series - fix typo in 'usage' - add synce4l to .gitignore v2: - fix duplicated entry in synce4l.8 - updated license headers .gitignore | 1 + README.org | 2 + configs/synce.cfg | 2 +- makefile | 9 ++- synce4l.8 | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ synce4l.c | 132 ++++++++++++++++++++++++++++++++ synce4l_README.md | 202 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 567 insertions(+), 3 deletions(-) create mode 100644 synce4l.8 create mode 100644 synce4l.c create mode 100644 synce4l_README.md diff --git a/.gitignore b/.gitignore index 1e7d1ca..8c65e47 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,6 @@ /ptp4l /phc_ctl /snmp4lptp +/synce4l /timemaster /ts2phc diff --git a/README.org b/README.org index 063b542..ab0d5a0 100644 --- a/README.org +++ b/README.org @@ -51,6 +51,8 @@ - Supports bonded, IPoIB, and vlan interfaces. + - Supports Synchronous Ethernet (Recommendation ITU-T G.8264/Y.1364). + * Getting the Code You can download the latest released version at Source Forge. diff --git a/configs/synce.cfg b/configs/synce.cfg index 64c5846..3b5a91d 100644 --- a/configs/synce.cfg +++ b/configs/synce.cfg @@ -136,7 +136,7 @@ eec_invalid_value 0 # As the standard expects 1 sec, it is not recommended to use different # than a 1000. # -tx_heartbeat_msec 2000 +tx_heartbeat_msec 1000 # # recovered PHY signal can be lost at anytime, this is msec resolution of diff --git a/makefile b/makefile index 5295b60..b7263a9 100644 --- a/makefile +++ b/makefile @@ -22,12 +22,14 @@ CC = $(CROSS_COMPILE)gcc VER = -DVER=$(version) CFLAGS = -Wall $(VER) $(incdefs) $(DEBUG) $(EXTRA_CFLAGS) LDLIBS = -lm -lrt -pthread $(EXTRA_LDFLAGS) -PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc +PRG = ptp4l hwstamp_ctl nsm phc2sys phc_ctl pmc timemaster ts2phc synce4l FILTERS = filter.o mave.o mmedian.o SERVOS = linreg.o ntpshm.o nullf.o pi.o servo.o TRANSP = raw.o transport.o udp.o udp6.o uds.o 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 +SYNCE = esmc_socket.o synce_clock.o synce_dev.o synce_dev_ctrl.o \ + synce_msg.o synce_port.o synce_port_ctrl.o synce_transport.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) \ @@ -35,7 +37,7 @@ OBJ = bmc.o clock.o clockadj.o clockcheck.o config.o designated_fsm.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 \ - pmc_common.o sysoff.o timemaster.o $(TS2PHC) + pmc_common.o $(SYNCE) sysoff.o timemaster.o $(TS2PHC) SRC = $(OBJECTS:.o=.c) DEPEND = $(OBJECTS:.o=.d) srcdir := $(dir $(lastword $(MAKEFILE_LIST))) @@ -50,6 +52,9 @@ man8dir = $(mandir)/man8 all: $(PRG) +synce4l: config.o hash.o interface.o phc.o print.o sk.o $(SYNCE) util.o \ + version.o + ptp4l: $(OBJ) nsm: config.o $(FILTERS) hash.o interface.o msg.o nsm.o phc.o print.o \ diff --git a/synce4l.8 b/synce4l.8 new file mode 100644 index 0000000..9b84acd --- /dev/null +++ b/synce4l.8 @@ -0,0 +1,222 @@ +.\" Manpage for synce4l. +.\" Contact lin...@li... to correct errors or typos. +.TH man 8 "21 Apr 2022" "1.0" "synce4l man page" + +.SH NAME +synce4l \- Synchronous Ethernet (SyncE) controller application + +.SH SYNOPSIS +synce4l -f [file] [-l <num>] [-m] [-q] [-v] [-h] + +.SH DESCRIPTION +synce4l is an implementation of the Synchronous Ethernet (SyncE) protocol +according to ITU-T Rec. G.8264. The design goal is to provide logic to supported +hardware by processing Ethernet Synchronization Messaging Channel (ESMC) and +control Ethernet Equipment Clock (EEC) on Network Card Interface (NIC). +.P +Application can operate in two mutually exclusive modes: line or external. + +.TP +External input mode +If `synce4l` is configured to run in external input mode then EEC needs to +have external 1PPS source attached (GPS or other generator). +In this scenario `synce4l` always broadcasts clock quality level (QL) defined +in configuration file. Additionally, for external input mode incoming SyncE +frames do not participate in best source selection algorithm for EEC. + +.TP +Line input mode +In line input mode incoming SyncE frames are processed and best clock source is +extracted from the link having the best quality level. +`synce4l` configures such "best quality" port as a source to recover clock for +EEC. The recovered QL is broadcasted to all other interfaces then. +An external clock source cannot be used in this mode. + +.SH OPTIONS +.TP +.B \-f [file] +read configuration from 'file' (config file takes precedence over +command line arguments) +.TP +.B \-l [num] +set the logging level to 'num' (0: least detailed, 7: most detailed) +.TP +.B \-m +print messages to stdout +.TP +.B \-q +do not print messages to the syslog +.TP +.B \-v +print synce4l version and exit +.TP +.B \-h +print this message and exit + +.SH CONFIGURATION FILE +Configuration file contains three sections: +.IP - +global, +.IP - +device, +.IP - +port. + +.SS Global section +This section starts with `[global]` keyword. It sets the logging options. +.RS +Available options: +.IP logging_level +Minimum log level required to appear in a log. +.P +.RS +Defaults to 6, valid values are 0 - 7 +.RE +.IP message_tag +Tag reported in a log. +.IP use_syslog +Set to 1 if `syslog` should be used. +.P +.RS +Defaults to 1, valid values are 0 or 1 +.RE +.IP verbose +Set to 1 to log extra information. +.P +.RS +Defaults to 0, valid values are 0 or 1 +.RE +.RE + +.SS Device section +This section specifies the configuration of a one logical device e.g. 'synce1'. +The name is defined by the user. The name has no impact for any functionality +except traces. +The name must be enclosed in extra angle bracket when defining new device +section e.g. [<synce1>]. +All ports defined by port sections after the device section will create one +SyncE device (until next device section). +.RS +Available options: +.IP input_mode +Set to "line" to enable line input mode, set "external" for external input mode. +.P +.RS +Defaults to "line", valid values are "line" or "external" +.RE +.IP external_input_QL +Quality Level (QL) for "external input" mode. +.P +.RS +Valid values specified in ITU-T Recommendations +.RE +.IP external_input_ext_QL +Extended Quality Level for "external input" mode. +.P +.RS +Valid values specified in ITU-T Recommendations +.RE +.IP extended_tlv +Set to 1 to enable extended QL. +.P +.RS +Defaults to 0, valid values are 0 or 1 +.RE +.IP network_option +Network option according to T-REC-G.8264. All devices in SyncE domain +should have the same option configured. +.P +.RS +Defaults to 1, valid values are 1 or 2 +.RE +.IP recover_time +Seconds indicating the minimum time to recover from the QL-failed state on the port. +.P +.RS +Defaults to 60, valid values are 10 - 720 +.RE +.IP get_eec_state_cmd +Defines a shell command which will be executed by synce4l to acquire current +state of a SyncE EEC on the device. Separated command must be provided by the +user for each device confgured for frequency synchronization. The command shall +output current state of EEC to stdout, expected values are defined (also by the +user) with following configuration items: +eec_holdover_value, eec_locked_ho_value, eec_locked_value, +eec_freerun_value, eec_invalid_value. +.RS +.RE +.IP eec_holdover_value +Defines a string value expected on stdout stream when EEC is in HOLDOVER state. +.P +.RS +.RE +.IP eec_locked_ho_value +Defines a string value expected on stdout stream when EEC is in LOCKED HOLDOVER +state. +.P +.RS +.RE +.IP eec_locked_value +Defines a string value expected on stdout stream when EEC is in LOCKED state. +.P +.RS +.RE +.IP eec_freerun_value +Defines a string value expected on stdout stream when EEC is in FREERUN state. +.P +.RS +.RE +.IP eec_invalid_value +Defines a string value expected on stdout stream when EEC is in INVALID state. +.P +.RE + +.SS Port section +Any other section not starting with `<` (e.g. [eth0]) is the port section. +Multiple port sections are allowed. Each port participates in SyncE +communication. +.RS +Available options: +.IP tx_heartbeat_msec +Interval between consecutive SyncE frame transmissions (1000ms recommended). +.P +.RS +Defaults to 1000, valid values are 100 - 3000 +.RE +.IP rx_heartbeat_msec +Interval between consecutive SyncE socket polls (frame receive). +.P +.RS +Defaults to 50, valid values are 10 - 500 +.RE +.IP recover_clock_enable_cmd +A shell command which enables PHY port pointed by this Port section as a source +of frequency for the SyncE EEC on this device (required only in +"internal input" mode). +.RS +.RE +.IP recover_clock_disable_cmd +A shell command which disables PHY port pointed by this Port section as a source +of frequency for the SyncE EEC on this device (required only in +"internal input" mode). +.IP allowed_qls +List of integers containing allowed SSM QLs separated by comma (`,`), other +received ones would be discarded. If parameter is not provided, all QLs will be +accepted. +.P +.IP allowed_ext_qls +List of integers containing allowed extended SSM QLs separated by comma (`,`), +other received ones would be discarded. If parameter is not provided, all QLs will +be accepted. +.P +.RE + +.SH BUGS +No known bugs. + +.SH AUTHOR +Arkadiusz Kubalewski <ark...@in...> +.P +Michal Michalik <mic...@in...> +.P +Piotr Kwapulinski <pio...@in...> diff --git a/synce4l.c b/synce4l.c new file mode 100644 index 0000000..552ed87 --- /dev/null +++ b/synce4l.c @@ -0,0 +1,132 @@ +/** + * @file synce4l.c + * @brief Synchronous Ethernet (SyncE) userspace client + * @note SPDX-FileCopyrightText: Copyright 2022 Intel Corporation + * @note SPDX-License-Identifier: GPL-2.0+ + */ +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/queue.h> +#include <errno.h> + +#include "synce_clock.h" +#include "config.h" +#include "print.h" +#include "version.h" + +static void usage(char *progname) +{ + fprintf(stderr, + "\nusage: %s [options]\n\n" + " \n\n" + " options:\n\n" + " -f [file] configuration file path (required)\n" + " (command line arguments takes precedence over config file)\n" + " -l [num] set the logging level to 'num'\n" + " (%d: least detailed, %d: most detailed)\n" + " -m print messages to stdout\n" + " -q do not print messages to the syslog\n" + " -v print synce4l version and exit\n" + " -h print this message and exit\n" + "\n", + progname, PRINT_LEVEL_MIN, PRINT_LEVEL_MAX); +} + +static void synce4l_cleanup(struct config *cfg) +{ + if (cfg) + config_destroy(cfg); +} + +int main(int argc, char *argv[]) +{ + int c, err = -EACCES, index, print_level; + char *config = NULL, *progname; + struct synce_clock *clock; + struct option *opts; + struct config *cfg; + + if (handle_term_signals()) + return -EPERM; + + cfg = config_create_synce(); + if (!cfg) { + return -EINVAL; + } + opts = config_long_options(cfg); + + /* Process the command line arguments. */ + progname = strrchr(argv[0], '/'); + progname = progname ? 1+progname : argv[0]; + while (EOF != (c = getopt_long(argc, argv, "f:l:mqvh", + opts, &index))) { + switch (c) { + case 'f': + config = optarg; + break; + case 'l': + if (get_arg_val_i(c, optarg, &print_level, + PRINT_LEVEL_MIN, PRINT_LEVEL_MAX)) + goto out; + config_set_int(cfg, "logging_level", print_level); + break; + case 'm': + config_set_int(cfg, "verbose", 1); + break; + case 'q': + config_set_int(cfg, "use_syslog", 0); + break; + case 'v': + version_show(stdout); + synce4l_cleanup(cfg); + return 0; + case 'h': + usage(progname); + synce4l_cleanup(cfg); + return 0; + case '?': + usage(progname); + goto out; + default: + usage(progname); + goto out; + } + } + + if (config && (c = config_read(config, cfg))) { + synce4l_cleanup(cfg); + return c; + } + + print_set_progname(progname); + print_set_tag(config_get_string(cfg, NULL, "message_tag")); + print_set_verbose(config_get_int(cfg, NULL, "verbose")); + print_set_syslog(config_get_int(cfg, NULL, "use_syslog")); + print_set_level(config_get_int(cfg, NULL, "logging_level")); + + if (STAILQ_EMPTY(&cfg->interfaces)) { + fprintf(stderr, "%s", "no interface specified\n"); + usage(progname); + goto out; + } + + clock = synce_clock_create(cfg); + if (!clock) { + fprintf(stderr, "%s", "failed to create a synce clock\n"); + goto out; + } + + while (is_running()) + if (synce_clock_poll(clock)) + break; + + synce_clock_destroy(clock); + + err = 0; +out: + synce4l_cleanup(cfg); + return err; +} diff --git a/synce4l_README.md b/synce4l_README.md new file mode 100644 index 0000000..79bfc83 --- /dev/null +++ b/synce4l_README.md @@ -0,0 +1,202 @@ +# Table of contents + +1. Introduction +2. Compilation +3. Installation +4. Usage +5. Config + +--- + +## 1\. Introduction + +`synce4l` is a software implementation of Synchronous Ethernet (SyncE) according +to ITU-T Recommendation G.8264. The design goal is to provide logic to supported +hardware by processing Ethernet Synchronization Messaging Channel (ESMC) and +control Ethernet Equipment Clock (EEC) on Network Card Interface (NIC). + +Application can operate in two mutually exclusive input modes: line or external. +Both modes are described in next paragraphs. + +The best source selection is done according to ITU-T Recommendations G.781 and +G.8264. Two network options are supported: option 1 and option 2. + +Table showing priority of quality levels (QLs) in option 1 networks: +| Quality Level | Priority* | SSM | Extended SSM** | +| ------------- | --------- | --- | -------------- | +| ePRTC | 0 | 0x2 | 0x21 | +| PRTC | 1 | 0x2 | 0x20 | +| PRC | 2 | 0x2 | 0xFF | +| SSU-A | 3 | 0x4 | 0xFF | +| SSU-B | 4 | 0x8 | 0xFF | + +Table showing priority of quality levels (QLs) in option 2 networks: +| Quality Level | Priority* | SSM | Extended SSM** | +| ------------- | --------- | --- | -------------- | +| ePRTC | 0 | 0x1 | 0x21 | +| PRTC | 1 | 0x1 | 0x20 | +| PRS | 2 | 0x1 | 0xFF | +| STU | 3 | 0x0 | 0xFF | +| ST2 | 4 | 0x7 | 0xFF | +| TNC | 5 | 0x4 | 0xFF | +| ST3E | 6 | 0xD | 0xFF | +| PROV | 7 | 0xE | 0xFF | + +> *Remark:* *Lower number means higher priority + +> *Remark:* **If extended SSM is not enabled, it's implicitly assumed as `0xFF` + +### External input mode + +If `synce4l` is configured to run in external input mode then EEC needs to have +external 1PPS source attached (GPS or other generator). +In this scenario `synce4l` always broadcasts clock quality level (QL) defined +in configuration file. Additionally, for “external” mode incoming SyncE +frames do not participate in best source selection algorithm for EEC. + +### Line input mode + +In line input mode incoming SyncE frames are processed and best clock source is +extracted from the link having the best quality level. +`synce4l` configures such "best quality" port as a source to recover clock +for EEC. The recovered QL is broadcasted to all other interfaces then. +An external clock cannot be used in this mode. + +--- + +## 2\. Compilation + +Makefile rules are included in linuxptp `makefile` and compilation is done using +the command: +``` +make synce4l +``` + +--- + +## 3\. Installation + +Use `make install` target to install `synce4l` in a system. + +--- + +## 4\. Usage + +Use `-h` command line argument to print the help page: +``` +$? ./synce4l -h +usage: synce4l [options] + + ... + + Other Options + + -f [file] read configuration from 'file' + (config file takes precedence over command line arguments) + -l [num] set the logging level to 'num' + (0: least detailed, 7: most detailed) + -m print messages to stdout + -q do not print messages to the syslog + -v print synce4l version and exit + -h print this message and exit +``` + +> *Remark:* `synce4l` requires root privileges since it opens raw sockets. + +--- + +## 5\. Config + +### Configuration file contains three sections: + +- global, +- device, +- port. + +### Global section + +This section starts with `[global]` keyword. It sets the logging options. + +| Parameter | Default | Valid values | Description | +| --------------- | ------- | ------------ | ---------------------------------------------- | +| `logging_level` | `6` | `0-7` | Minimum log level required to appear in a log. | +| `message_tag` | None | string | Tag reported in a log. | +| `use_syslog` | `1` | `0`, `1` | Set to 1 if `syslog` should be used. | +| `verbose` | `0` | `0`, `1` | Set to 1 to log extra information. | + +### Device section + +This section specifies the configuration of a one logical device e.g. 'synce1'. +The name is defined by the user. The name has no impact for any functionality +except traces. +The name must be enclosed in extra angle bracket when defining new device +section e.g. [<synce1>]. +All ports defined by port sections after the device section will create one +SyncE device (until next device section). + +| Parameter | Default | Valid values | Description | +| ----------------------- | ------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `input_mode` | `line` | `line`, `external` | Set to "line" to enable line input mode, set "external" for external input mode. | +| `external_input_QL` | `0` | `0-15` | Quality Level (QL) for "external input" mode. | +| `external_input_ext_QL` | `0` | `0-255` | Extended Quality Level for "external input" mode. | +| `extended_tlv` | `0` | `0`, `1` | Set to 1 to enable extended QL. | +| `network_option` | `1` | `1`, `2` | Network option according to T-REC-G.8264. All devices in SyncE domain should have the same option configured. | +| `recover_time` | `60` | `10-720` | Seconds indicating the minimum time to recover from the QL-failed state on the port. | +| `get_eec_state_cmd` | None | string | Shell command which will be executed by synce4l to acquire current state of a SyncE EEC on the device. The command shall output current state of EEC to stdout. | +| `eec_holdover_value` | None | string | Value expected on stdout stream when EEC is in HOLDOVER state, after calling command defined in get_eec_state_cmd | +| `eec_locked_ho_value` | None | string | Value expected on stdout stream when EEC is in LOCKED HOLDOVER state, after calling command defined in get_eec_state_cmd | +| `eec_locked_value` | None | string | Value expected on stdout stream when EEC is in LOCKED state, after calling command defined in get_eec_state_cmd | +| `eec_freerun_value` | None | string | Value expected on stdout stream when EEC is in FREERUN state, after calling command defined in get_eec_state_cmd | +| `eec_invalid_value` | None | string | Value expected on stdout stream when EEC is in INVALID state, after calling command defined in get_eec_state_cmd | + +### Port section + +Any other section not starting with `<` (e.g. [eth0]) is the port section. +Multiple port sections are allowed. Each port participates in SyncE +communication. + +| Parameter | Default | Valid values | Description | +| --------------------------- | ------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `tx_heartbeat_msec` | `1000` | `100-3000` | Interval between consecutive SyncE frame transmissions (1000ms is recommended). | +| `rx_heartbeat_msec` | `50` | `10-500` | Interval between consecutive SyncE socket polls (frame receive). | +| `recover_clock_enable_cmd` | None | string | Shell command which enables PHY port pointed by this Port section as a source of frequency for the SyncE EEC on this device (required only in "internal_input" mode). | +| `recover_clock_disable_cmd` | None | string | Shell command which disables PHY port pointed by this Port section as a source of frequency for the SyncE EEC on this device (required only in "internal_input" mode). | +| `allowed_qls` | None | string | List of integers containing allowed SSM QLs separated by comma (`,`), other received ones would be discarded - if parameter is not provided, all QLs will be accepted | +| `allowed_ext_qls` | None | string | List of integers containing allowed extended SSM QLs separated by comma (`,`), other received ones would be discarded - if parameter is not provided, all QLs will be accepted | + +> *Remark:* Please do not use backslashes in config file in 'string' fields - for example do not use it like this: `"/sys/kernel/debug/ice/0000\:5e\:00\.0/cgu_state"` + +### Config example + +``` +[global] +logging_level 7 +use_syslog 0 +verbose 1 +message_tag [synce4l] + +[<synce1>] +input_mode line +network_option 1 +external_input_QL 11 +external_input_ext_QL 33 +extended_tlv 1 +recover_time 20 +eec_get_state_cmd cat /sys/class/net/eth0/device/cgu_state +eec_holdover_value 4 +eec_locked_ho_value 3 +eec_locked_value 2 +eec_freerun_value 1 +eec_invalid_value 0 + +[eth0] +tx_heartbeat_msec 1000 +rx_heartbeat_msec 500 +recover_clock_enable_cmd echo 1 0 > /sys/class/net/eth0/device/phy/synce +recover_clock_disable_cmd echo 0 0 > /sys/class/net/eth0/device/phy/synce +allowed_qls 3,4,7 +allowed_ext_qls 20,21 + +``` + +--- -- 2.9.5 |
From: Greg A. <gre...@re...> - 2022-07-20 04:22:07
|
As I highlighted earlier, I, along with other users of ptp4l, don't feel the linuxptp project is the right place for the submission of the ITU-T ESMC Protocol (which is based on recommendations G.8274 & G.781). There are no dependencies on ESMC with the 1588 protocol or the Linux PHC, nor is the linuxptp maintainer wanting to be a bottleneck for contributions to the ESMC protocol (as highlighted below from Richard). >My main concern is the fact that my time is limited to work on >linuxptp, and I already have a back log of patches. If synce will have >rapid development, new features, etc, then I don't want to be the >bottleneck. To address this concern, Renesas has submitted our open implementation of the ESMC protocol to github, also under the same name: https://github.com/renesas/synce4l. This is an open project under GPLv2, and we welcome contributions to it. The implementation not only addresses the ESMC protocol per G.8264, but also includes some additional features from G.781. We realize there will be a need to provide QL and other information from synce4l to ptp4l, per applicable ITU-T 1588 Profiles, and look forward to collaborating with the linuxptp community to produce these interfaces. We will also be working with the Network Time Foundation to see if they would be willing to oversee synce4l project like the other Linux projects they already manage, including linuxptp. We have also already shared synce4l project with other MAC/PHY/SoC vendors looking for an ESMC protocol solution. Regards, Greg Greg Armstrong Principal System Architect, Timing Products Division Renesas Electronics Canada Limited Mobile: 1-613-218-9373 -----Original Message----- From: Miroslav Lichvar <mli...@re...> Sent: Thursday, June 2, 2022 6:23 AM To: Erez <ere...@gm...> Cc: Greg Armstrong <gre...@re...>; pio...@in...; and...@in...; lin...@li...; ana...@in... Subject: Re: [Linuxptp-devel] [PATCH 00/11] synce4l: add software for Synchronous Ethernet On Wed, Jun 01, 2022 at 05:37:10PM +0200, Erez wrote: > I do hope my project > https://jpn01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fsf.net%2Fp%2Flibptpmgmt%2F&data=05%7C01%7Cgreg.armstrong.uw%40renesas.com%7C08ec39f69e6347d0a63d08da4481ecaa%7C53d82571da1947e49cb4625a166a4a2a%7C0%7C0%7C637897622057580969%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Ud1Te2%2BA87xSSj9z1qkhgPyQ4MoLr5tHmUvB58XygW0%3D&reserved=0, does help to fill this gap and helps with reducing the overhead from this project. > phc2sys can be implemented with libptpmgmt. > I already cloned pmc with libptpmgmt. > > Maybe it is time that this project focuses on ptp4l and let other > applications have their own projects. phc2sys shares quite a lot of code with ptp4l, it's not just the PTP management support. It it was a separate project, it would need to duplicate the code, or linuxptp would need to provide and maintain a library. If some components had a different maintainer, it doesn't mean it has to be a separate project or repository. -- Miroslav Lichvar |
From: Michalik, M. <mic...@in...> - 2022-07-29 16:24:59
|
Hello Greg, Much thanks for your valuable input - we appreciate it a lot. Please see my answers inline. Have a great day, Michał Michalik > As I highlighted earlier, I, along with other users of ptp4l, don't feel the linuxptp project is the right place for the submission of the ITU-T ESMC Protocol (which is based on recommendations G.8274 & G.781). There are no dependencies on ESMC with the 1588 protocol or the Linux PHC, nor is the linuxptp maintainer wanting to be a bottleneck for contributions to the ESMC protocol (as highlighted below from Richard). If you don't mind, please let me ask you a question - I am definitely missing something. Whom are you talking about while using expression "along with other users of ptp4l"? To my best understanding right now we have a situation, where: - you are against including it into linuxptp, - Miroslav Lichvar is for including it, - Erez is for including it, - if I understand Richard communicate well, he did not say firm "no" - just raised some concerns about his bandwidth, - we (as Intel) are good with both ways - we just need to know. In which point my understanding is faulty? Richard - since you obviously have the last word, would you mind to state your final decision clearly to avoid any further interpretations of your answer? > > >My main concern is the fact that my time is limited to work on >linuxptp, and I already have a back log of patches. If synce will have >rapid development, new features, etc, then I don't want to be the >bottleneck. > > To address this concern, Renesas has submitted our open implementation of the ESMC protocol to github, also under the same name: https://github.com/renesas/synce4l. This is an open project under GPLv2, and we welcome contributions to it. The implementation not only addresses the ESMC protocol per G.8264, but also includes some additional features from G.781. We realize there will be a need to provide QL and other information from synce4l to ptp4l, per applicable ITU-T 1588 Profiles, and look forward to collaborating with the linuxptp community to produce these interfaces. Since working in community should be based on honesty, let me be honest here. I feel we have a little awkward situation we need to solve somehow together. Some facts about the issue I'm thinking about: We have pushed first patches of synce4l to linuxptp newsletter on 26th May. Then we see that Renesas have pushed one initial commit with only empty README mentioning the synce4l name (8th Jul) and second commit with full code implementation (15th Jul) - both commits were uploaded few weeks after our initial submission using synce4l name. I am perfectly aware that neither of us has any trademark rights to the name, so technically Renesas did nothing wrong. Still, we feel a little bad about the situation where we wanted to be fair and waited for final linuxptp community decision before pushing the synce4l somewhere else and in the meantime see other project started with the same exact name. Let me be perfectly clear here - I assume no bad intentions, we just have had a unfortunate coincidence here. I assume you also have been working on this application for months and this rush in publishing it means absolutely nothing. Let me now explain why this name matters for us. We've been using this name internally for some time now - it would be quite tiresome to change it in lots of documentation and communication. Additionally, caring for best quality of our submission to linuxptp - we have dropped our customers an early sample of synce4l last year so they could provide us with a valuable feedback on what we could improve. Our customers are familiar with the name synce4l and seeing other project named the same might confuse them. Kind question - would you please at least consider changing a project name at this very early stage? Before you answer that - please let me state our position. We are all part of community and sometimes we need to find a solution somehow acceptable to all. We are definitely open to changing our application name (within or outside linuxptp project), even knowing that it would bring quite a big amount of additional work. Btw. We also considering contributing your project, if that gets more traction and would at some time look more promising. What I am concerned about right now looking at it, it seems really hardware specific. We aimed to avoid that at all cost - our application is not connected to Intel HW at all - it can be used with NIC of any vendor. Are Renesas eventually is going to do the same? > > We will also be working with the Network Time Foundation to see if they would be willing to oversee synce4l project like the other Linux projects they already manage, including linuxptp. We have also already shared synce4l project with other MAC/PHY/SoC vendors looking for an ESMC protocol solution. I understand - maybe a good idea is that both Intel and Renesas should change the name, so we would not confuse anybody about 'which synce4l is which'? > > Regards, > Greg > > Greg Armstrong > Principal System Architect, Timing Products Division > Renesas Electronics Canada Limited > Mobile: 1-613-218-9373 > |
From: Greg A. <gre...@re...> - 2022-08-03 14:40:06
|
Hi Michał, Please see my responses inline. Regards, Greg -----Original Message----- From: Michalik, Michal <mic...@in...> Sent: Friday, July 29, 2022 12:25 PM To: Greg Armstrong <gre...@re...>; ric...@gm... Cc: lin...@li...; Kubalewski, Arkadiusz <ark...@in...>; Miroslav Lichvar <mli...@re...>; Erez <ere...@gm...> Subject: RE: [Linuxptp-devel] [PATCH 00/11] synce4l: add software for Synchronous Ethernet Hello Greg, Much thanks for your valuable input - we appreciate it a lot. Please see my answers inline. Have a great day, Michał Michalik > As I highlighted earlier, I, along with other users of ptp4l, don't feel the linuxptp project is the right place for the submission of the ITU-T ESMC Protocol (which is based on recommendations G.8274 & G.781). There are no dependencies on ESMC with the 1588 protocol or the Linux PHC, nor is the linuxptp maintainer wanting to be a bottleneck for contributions to the ESMC protocol (as highlighted below from Richard). If you don't mind, please let me ask you a question - I am definitely missing something. Whom are you talking about while using expression "along with other users of ptp4l"? **GA. We have been working with a number of PHY/SoC partners and have received the feedback once they saw the submission to linuxptp (as they were confused). They felt this looked like an "Intel-implementation" and should not be called synce4l. They feel an implementation of synce4l should use a defined standard interface pushed into the Linux kernel and not specific ones within the scope of ptp4l. This has been the direction Renesas has been taking with our synce4l implementation. To my best understanding right now we have a situation, where: - you are against including it into linuxptp, - Miroslav Lichvar is for including it, - Erez is for including it, - if I understand Richard communicate well, he did not say firm "no" - just raised some concerns about his bandwidth, - we (as Intel) are good with both ways - we just need to know. In which point my understanding is faulty? **GA. I don't think there is any fault. My concern is that an implementation submitted to the linuxptp project may mistakenly use interfaces with the scope of ptp4l, not allowing it to be a stand-alone protocol (which it is, as this is an ITU-T defined protocol with no dependencies on PTP). We have several customers who only need the ESMC protocol for their equipment. Richard - since you obviously have the last word, would you mind to state your final decision clearly to avoid any further interpretations of your answer? > > >My main concern is the fact that my time is limited to work on >linuxptp, and I already have a back log of patches. If synce will have >rapid development, new features, etc, then I don't want to be the >bottleneck. > > To address this concern, Renesas has submitted our open implementation of the ESMC protocol to github, also under the same name: https://jpn01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Frenesas%2Fsynce4l&data=05%7C01%7Cgreg.armstrong.uw%40renesas.com%7C0c14b6faa12f4fd3f67b08da717ee2ec%7C53d82571da1947e49cb4625a166a4a2a%7C0%7C0%7C637947087036059280%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=jHoJi7jEC4ZxQ5fUbqVe15UPwpkmFjud2%2B%2F29RYglVQ%3D&reserved=0. This is an open project under GPLv2, and we welcome contributions to it. The implementation not only addresses the ESMC protocol per G.8264, but also includes some additional features from G.781. We realize there will be a need to provide QL and other information from synce4l to ptp4l, per applicable ITU-T 1588 Profiles, and look forward to collaborating with the linuxptp community to produce these interfaces. Since working in community should be based on honesty, let me be honest here. I feel we have a little awkward situation we need to solve somehow together. Some facts about the issue I'm thinking about: We have pushed first patches of synce4l to linuxptp newsletter on 26th May. Then we see that Renesas have pushed one initial commit with only empty README mentioning the synce4l name (8th Jul) and second commit with full code implementation (15th Jul) - both commits were uploaded few weeks after our initial submission using synce4l name. I am perfectly aware that neither of us has any trademark rights to the name, so technically Renesas did nothing wrong. Still, we feel a little bad about the situation where we wanted to be fair and waited for final linuxptp community decision before pushing the synce4l somewhere else and in the meantime see other project started with the same exact name. Let me be perfectly clear here **GA. Again, an implementation of "synce4l" should not concern the linuxptp community. However, yes, we've been working on our implementation for some time, and have already shared it with several PHY/SoC vendors as "sycne4l". We were aware of Intel doing their own implementation as well, but when contacted, they did not want to collaborate for what ever reason. Since this did not seem to be "open", we continued with our development and plan to release an open implementation of the ESMC protocol, under the name synce4l. - I assume no bad intentions, we just have had a unfortunate coincidence here. I assume you also have been working on this application for months and this rush in publishing it means absolutely nothing. Let me now explain why this name matters for us. We've been using this name internally for some time now **GA. The "rush" was to counter your attempt to submit a patch to linuxptp. To show there is an alternate implementation, we had to speed up our release. Our intent was to release through the Network Time Foundation and to also have a standard SyncE Hardware interface pushed into the Linux kernel (to more away from any "proprietary" interface). - it would be quite tiresome to change it in lots of documentation and communication. Additionally, caring for best quality of our submission to linuxptp - we have dropped our customers an early sample of synce4l last year so they could provide us with a valuable feedback on what we could improve. Our customers are familiar with the name synce4l and seeing other project named the same might confuse them. Kind question - would you please at least consider changing a project name at this very early stage? **GA. We are likely in the same boat here, however, like I said, our other MAC/PHY/SoC partners see your submission as an Intel implementation (since they did not see it until you submitted the patch to linuxptp). Before you answer that - please let me state our position. We are all part of community and sometimes we need to find a solution somehow acceptable to all. We are definitely open to changing our application name (within or outside linuxptp project), even knowing that it would bring quite a big amount of additional work. Btw. We also considering contributing your project, if that gets more traction and would at some time look more promising. What I am concerned about right now looking at it, it seems really hardware specific. We aimed to avoid that at all cost - our application is not connected to Intel HW at all - it can be used with NIC of any vendor. Are Renesas eventually is going to do the same? **GA. This is our intent as well, but you cannot have an implementation without HW access to the SETS IC - and it was easier to sytart with interfaces known to be used by Tier1 manufacturers when designing in our timing silicon. The sync control block is a major piece of the full G.8264/G.781 solution. As I mentioned above, our intent was to push "open" interfaces into the Linux kernel so that the SyncE hardware was fully abstracted, similar to (but not using) the PTP Hardware Clock. This would allow our partners to submit their own drivers (and other timing silicon vendors to do the same). > > We will also be working with the Network Time Foundation to see if they would be willing to oversee synce4l project like the other Linux projects they already manage, including linuxptp. We have also already shared synce4l project with other MAC/PHY/SoC vendors looking for an ESMC protocol solution. I understand - maybe a good idea is that both Intel and Renesas should change the name, so we would not confuse anybody about 'which synce4l is which'? **GA. We can discuss with our partners and customers, but there is no confusion from our end since our development started as an implementation of ESMC protocol for Linux (nothing to do with PTP). Thus, synce4l fit with our customers (as we have some customers who only need synce4l to manage their SyncE clock). We also were open with our partners and customers up front, sharing the implementation as we developed. Again, we tried this with Intel and there was no interest (Intel finally shared their implementation with Renesas earlier this year, but we were already near the end of our own first release). > > Regards, > Greg > > Greg Armstrong > Principal System Architect, Timing Products Division Renesas > Electronics Canada Limited > Mobile: 1-613-218-9373 > |
From: Erez <ere...@gm...> - 2022-07-30 00:01:27
|
On Fri, 29 Jul 2022 at 18:24, Michalik, Michal <mic...@in...> wrote: > Hello Greg, > > Much thanks for your valuable input - we appreciate it a lot. > Please see my answers inline. > > Have a great day, > Michał Michalik > > > As I highlighted earlier, I, along with other users of ptp4l, don't feel > the linuxptp project is the right place for the submission of the ITU-T > ESMC Protocol (which is based on recommendations G.8274 & G.781). There are > no dependencies on ESMC with the 1588 protocol or the Linux PHC, nor is the > linuxptp maintainer wanting to be a bottleneck for contributions to the > ESMC protocol (as highlighted below from Richard). > > If you don't mind, please let me ask you a question - I am definitely > missing > something. Whom are you talking about while using expression "along with > other > users of ptp4l"? To my best understanding right now we have a situation, > where: > - you are against including it into linuxptp, > - Miroslav Lichvar is for including it, > - Erez is for including it, > I did not suggest it. I do not oppose adding the new tool to the linuxptp project. But it is Richard's call. - if I understand Richard communicate well, he did not say firm "no" - just > raised > some concerns about his bandwidth, > - we (as Intel) are good with both ways - we just need to know. > In which point my understanding is faulty? > > Richard - since you obviously have the last word, would you mind to state > your > final decision clearly to avoid any further interpretations of your answer? > > > > > >My main concern is the fact that my time is limited to work on > >linuxptp, and I already have a back log of patches. If synce will have > >rapid development, new features, etc, then I don't want to be the > >bottleneck. > > > > To address this concern, Renesas has submitted our open implementation > of the ESMC protocol to github, also under the same name: > https://github.com/renesas/synce4l. This is an open project under GPLv2, > and we welcome contributions to it. The implementation not only addresses > the ESMC protocol per G.8264, but also includes some additional features > from G.781. We realize there will be a need to provide QL and other > information from synce4l to ptp4l, per applicable ITU-T 1588 Profiles, and > look forward to collaborating with the linuxptp community to produce these > interfaces. > > Since working in community should be based on honesty, let me be honest > here. > I feel we have a little awkward situation we need to solve somehow > together. > Some facts about the issue I'm thinking about: We have pushed first > patches of > synce4l to linuxptp newsletter on 26th May. Then we see that Renesas have > pushed > one initial commit with only empty README mentioning the synce4l name > (8th Jul) > and second commit with full code implementation (15th Jul) - both commits > were > uploaded few weeks after our initial submission using synce4l name. I am > perfectly aware that neither of us has any trademark rights to the name, > so > technically Renesas did nothing wrong. Still, we feel a little bad about > the > situation where we wanted to be fair and waited for final linuxptp > community > decision before pushing the synce4l somewhere else and in the meantime see > other project started with the same exact name. Let me be perfectly clear > here > - I assume no bad intentions, we just have had a unfortunate coincidence > here. > I assume you also have been working on this application for months and > this > rush in publishing it means absolutely nothing. Let me now explain why > this > name matters for us. We've been using this name internally for some time > now > - it would be quite tiresome to change it in lots of documentation and > communication. Additionally, caring for best quality of our submission to > linuxptp - we have dropped our customers an early sample of synce4l last > year > so they could provide us with a valuable feedback on what we could > improve. > Our customers are familiar with the name synce4l and seeing other project > named > the same might confuse them. Kind question - would you please at least > consider > changing a project name at this very early stage? > > Before you answer that - please let me state our position. We are all part > of > community and sometimes we need to find a solution somehow acceptable to > all. We > are definitely open to changing our application name (within or outside > linuxptp > project), even knowing that it would bring quite a big amount of > additional work. > > Btw. We also considering contributing your project, if that gets more > traction > and would at some time look more promising. What I am concerned about > right now > looking at it, it seems really hardware specific. We aimed to avoid that > at all > cost - our application is not connected to Intel HW at all - it can be > used with NIC of > any vendor. Are Renesas eventually is going to do the same? > > > > > We will also be working with the Network Time Foundation to see if they > would be willing to oversee synce4l project like the other Linux projects > they already manage, including linuxptp. We have also already shared > synce4l project with other MAC/PHY/SoC vendors looking for an ESMC protocol > solution. > > I understand - maybe a good idea is that both Intel and Renesas should > change > the name, so we would not confuse anybody about 'which synce4l is which'? > > > > > Regards, > > Greg > > > > Greg Armstrong > > Principal System Architect, Timing Products Division > > Renesas Electronics Canada Limited > > Mobile: 1-613-218-9373 > > > |
From: Michalik, M. <mic...@in...> - 2022-08-02 09:04:10
|
Dear Erez, Please excuse me – I must have confused your view on this topic. Appreciate you have straighten this up. Have a great day! BR, MM From: Erez <ere...@gm...> Sent: Saturday, July 30, 2022 2:01 AM To: Michalik, Michal <mic...@in...> Cc: Greg Armstrong <gre...@re...>; ric...@gm...; lin...@li...; Kubalewski, Arkadiusz <ark...@in...>; Miroslav Lichvar <mli...@re...> Subject: Re: [Linuxptp-devel] [PATCH 00/11] synce4l: add software for Synchronous Ethernet On Fri, 29 Jul 2022 at 18:24, Michalik, Michal <mic...@in...<mailto:mic...@in...>> wrote: Hello Greg, Much thanks for your valuable input - we appreciate it a lot. Please see my answers inline. Have a great day, Michał Michalik > As I highlighted earlier, I, along with other users of ptp4l, don't feel the linuxptp project is the right place for the submission of the ITU-T ESMC Protocol (which is based on recommendations G.8274 & G.781). There are no dependencies on ESMC with the 1588 protocol or the Linux PHC, nor is the linuxptp maintainer wanting to be a bottleneck for contributions to the ESMC protocol (as highlighted below from Richard). If you don't mind, please let me ask you a question - I am definitely missing something. Whom are you talking about while using expression "along with other users of ptp4l"? To my best understanding right now we have a situation, where: - you are against including it into linuxptp, - Miroslav Lichvar is for including it, - Erez is for including it, I did not suggest it. I do not oppose adding the new tool to the linuxptp project. But it is Richard's call. - if I understand Richard communicate well, he did not say firm "no" - just raised some concerns about his bandwidth, - we (as Intel) are good with both ways - we just need to know. In which point my understanding is faulty? Richard - since you obviously have the last word, would you mind to state your final decision clearly to avoid any further interpretations of your answer? > > >My main concern is the fact that my time is limited to work on >linuxptp, and I already have a back log of patches. If synce will have >rapid development, new features, etc, then I don't want to be the >bottleneck. > > To address this concern, Renesas has submitted our open implementation of the ESMC protocol to github, also under the same name: https://github.com/renesas/synce4l. This is an open project under GPLv2, and we welcome contributions to it. The implementation not only addresses the ESMC protocol per G.8264, but also includes some additional features from G.781. We realize there will be a need to provide QL and other information from synce4l to ptp4l, per applicable ITU-T 1588 Profiles, and look forward to collaborating with the linuxptp community to produce these interfaces. Since working in community should be based on honesty, let me be honest here. I feel we have a little awkward situation we need to solve somehow together. Some facts about the issue I'm thinking about: We have pushed first patches of synce4l to linuxptp newsletter on 26th May. Then we see that Renesas have pushed one initial commit with only empty README mentioning the synce4l name (8th Jul) and second commit with full code implementation (15th Jul) - both commits were uploaded few weeks after our initial submission using synce4l name. I am perfectly aware that neither of us has any trademark rights to the name, so technically Renesas did nothing wrong. Still, we feel a little bad about the situation where we wanted to be fair and waited for final linuxptp community decision before pushing the synce4l somewhere else and in the meantime see other project started with the same exact name. Let me be perfectly clear here - I assume no bad intentions, we just have had a unfortunate coincidence here. I assume you also have been working on this application for months and this rush in publishing it means absolutely nothing. Let me now explain why this name matters for us. We've been using this name internally for some time now - it would be quite tiresome to change it in lots of documentation and communication. Additionally, caring for best quality of our submission to linuxptp - we have dropped our customers an early sample of synce4l last year so they could provide us with a valuable feedback on what we could improve. Our customers are familiar with the name synce4l and seeing other project named the same might confuse them. Kind question - would you please at least consider changing a project name at this very early stage? Before you answer that - please let me state our position. We are all part of community and sometimes we need to find a solution somehow acceptable to all. We are definitely open to changing our application name (within or outside linuxptp project), even knowing that it would bring quite a big amount of additional work. Btw. We also considering contributing your project, if that gets more traction and would at some time look more promising. What I am concerned about right now looking at it, it seems really hardware specific. We aimed to avoid that at all cost - our application is not connected to Intel HW at all - it can be used with NIC of any vendor. Are Renesas eventually is going to do the same? > > We will also be working with the Network Time Foundation to see if they would be willing to oversee synce4l project like the other Linux projects they already manage, including linuxptp. We have also already shared synce4l project with other MAC/PHY/SoC vendors looking for an ESMC protocol solution. I understand - maybe a good idea is that both Intel and Renesas should change the name, so we would not confuse anybody about 'which synce4l is which'? > > Regards, > Greg > > Greg Armstrong > Principal System Architect, Timing Products Division > Renesas Electronics Canada Limited > Mobile: 1-613-218-9373 > |
From: Miroslav L. <mli...@re...> - 2022-08-04 09:08:02
|
On Wed, Jul 20, 2022 at 03:50:04AM +0000, Greg Armstrong wrote: > To address this concern, Renesas has submitted our open implementation of the ESMC protocol to github, also under the same name: https://github.com/renesas/synce4l. This is an open project under GPLv2, and we welcome contributions to it. I looked at the project to better understand the differences between this and the Intel implementation. Here are my observations. The code doesn't build on its own. It requires "clockmatrix api", which doesn't seem to be available without registration. There are some HW-specific ioctls. I don't see an abstraction which would allow one binary to support different types of hardware. About 20% of the code comes from linuxptp (per copyright). -- Miroslav Lichvar |
From: Greg A. <gre...@re...> - 2022-09-02 17:23:50
|
Hi Arkadiusz, >>I assume that silence on this thread (from the rest of the members), same as >>silence on any other community mailing list thread, is actually a quiet >>approval (or at least no one has any objections). **GA. I do not feel this way - lack oresponse is likely an indication of lack of expertise to review impact to linuxptp - or if your implementation even is compliant to G.8264/G.781 (feel free to contact me outsider of this if you want feedback based on our understanding of G.8264 & G.781). You are adding a new feature/protocol to the linuxptp project, which is overseen by the Network Time Foundation. Thus, I will let them comment if they will allow this. >>Greg, >>You are saying that your project is open source, and you would welcome >>contribution to it, but at the same time seems that your code: >>- requires third party proprietary module for compilation, **GA. This has already been fixed. However, this is not the right forum to debate ESMC projects - my objection to your patch is an implementation of the ESMC protocol does not belong in the linuxptp project; as it has nothing to do with the 1588 standard. The focus on this project should be on PTP, it's Profiles, and any application that makes use of the PHC (i.e. ts2phc, pmc, phc2sys) - none of this applies to ESMC protocol. >>- uses HW specific i2c to configure your NIC/timecard. >>Is that true? **GA. Of course you need an interface to HW, how else are you allowing the ESMC protocol to "manage" the SyncE clock as an ITU-T compliant node. That is the limitation of your implementation, as you make a lot of assumptions of a "fixed" configuration of the HW - if the HW is fixed, why would you need a protocol to manage it? **GA. Again, no one is arguing you need a HW abstraction for the ESMC protocol to manage the local SyncE clock node. All ESMC implementation I have seen so far have not addressed this in the linux Kernel - but they all claim that is the long-term goal; which is good. What I don't want is someone thinking it should be added to the PHC, as the PHC is "not" for SyncE HW clock. The only applicable interface I could envision for the PHC is to allow for the Local Clock section for the Local PTP Clock (i.e. XO/XTAL vs SyncE). >>If so, do you think that it is proper way to cooperate with an open source >>community? **GA. Again, this is off topic so I will not address here. Please feel free to send me an email directly. >>Excuses me but I hardly understand what you would like to achieve except >>blocking delivery of this solution. As I see it, your only argument is that >>SyncE is not a part of 1588. Well sure, SyncE is only mentioned in >>appendices, but you know, Telecom Profiles are not part of 1588, they are only >>mentioned in appendices. **GA. It's unfortunate you feel this way. Renesas has been a long-time member of ITU-T and also of OCP. Yes, we are blocking a patch to add an unrelated protocol to an implementation of a PTP protocol, as it does not belong in this project - as most members of the linuxptp community do not have the background or expertise to review contributions to an ESMC implementation. However, adding support for 1588 Profiles, as allowed by the 1588 standard, makes complete sense. It also makes sense to put in supporting SW of "PTP", since it's also interfacing the PHC (i.e. ts2phc, phc2sys). I don't think anyone is suggesting not allowing Profile support to be patch to the linuxptp project. >>Should support for them be removed from linuxptp? >>In my humble opinion, it wouldn't make any sense. **GA. No, it would not make sense to remove an interface between PTP and ESMC, but it should be just that - an interface. If you want to focus your patch to this interface, then please submit that to the linuxptp project. >>Maybe SyncE without PTP does make some sense, but for telecoms it makes most >>sense to use them both (if hardware supports it). **GA. And I have arguments of only needed ESMC. Again, not saying there will not be an interface between the two protocols, but contributions to either protocol as part of the implementation are mutually exclusive - as they are applicable to independent clocks as defined by the ITU-T. >>Following this idea, it makes most sense to have both in the same package, as >>for full support of "Annex L" (1588), the SyncE daemon shall provide data to >>the PTP daemon. It is easiest to maintain both applications in one repository, >>this prevents any incompatibility issues between shared interfaces. **GA. An argument of code reuse is not valid to submit code to an unrelated project. The ESMC protocol has absolutely no dependencies on PTP or the PHC. The Annex was put in to address the high accuracy profile, which was new in 1588-2019. Again, it does not suggest using the ITU-T concept of SyncE (in fact, HA Profile recommends using a coherent, congruent physical layer clock managed by PTP itself - referenced as L1Sync). **GA. You are correct the ITU-T G.8275.1 Profile recommends the use of physical layer "assistance" (and G.8275.2, as optional) - but to be clear, it's assisting the local PTP clock, it is not managing the PTP clock nor does the PTP protocol have any influence on the ESMC protocol/SyncE clock. Thus, Annex L in 1588 is not applicable for the ITU-T Profiles (nor is it referenced by either profile) - as ITU-T assumes the physical layer clock is managed by the ESMC protocol (not PTP, so does not use L1Sync). **GA. Again, my push back is that any implementation of the ESMC protocol does not belong in the linuxptp project. Instead, the community should focus on how to interface to the ESMC to meet the recommendations of the applicable 1588 Profiles (i.e. ITU-T). And for those profiles that use L1Sync (i.e. HA Profile), by all means, this belongs in linuxptp project as it's part of PTP (and in this case, the L1Sync interface is likely going through PHC) - but please don't confuse this with ITU-T's SyncE as they are not related. Regards, Greg Greg Armstrong Principal System Architect, Timing Products Division Renesas Electronics Canada Limited Mobile: 1-613-218-9373 -----Original Message----- From: Kubalewski, Arkadiusz <ark...@in...> Sent: Wednesday, August 31, 2022 5:23 PM To: Miroslav Lichvar <mli...@re...>; Greg Armstrong <gre...@re...>; Richard Cochran <ric...@gm...>; Michalik, Michal <mic...@in...> Cc: Kwapulinski, Piotr <pio...@in...>; Sawula, Andrzej <and...@in...>; lin...@li...; Gerasymenko, Anatolii <ana...@in...> Subject: RE: [Linuxptp-devel] [PATCH 00/11] synce4l: add software for Synchronous Ethernet >-----Original Message----- >From: Miroslav Lichvar mli...@re... >Sent: Thursday, August 4, 2022 11:08 AM > >On Wed, Jul 20, 2022 at 03:50:04AM +0000, Greg Armstrong wrote: >> To address this concern, Renesas has submitted our open implementation of the ESMC protocol to github, also under the same name: https://jpn01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Frenesas%2Fsynce4l&data=05%7C01%7Cgreg.armstrong.uw%40renesas.com%7C225abee1857f447103e708da8b970d52%7C53d82571da1947e49cb4625a166a4a2a%7C0%7C0%7C637975778118848397%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=y90x7DrMn4mLlZGxMCE4ja4oT3OCudPWhVozrAO9zuw%3D&reserved=0. This is an open project under GPLv2, and we welcome contributions to it. > >I looked at the project to better understand the differences between >this and the Intel implementation. Here are my observations. > >The code doesn't build on its own. It requires "clockmatrix api", which >doesn't seem to be available without registration. > >There are some HW-specific ioctls. > >I don't see an abstraction which would allow one binary to support >different types of hardware. > >About 20% of the code comes from linuxptp (per copyright). > >-- >Miroslav Lichvar > Thanks Miroslav! I assume that silence on this thread (from the rest of the members), same as silence on any other community mailing list thread, is actually a quiet approval (or at least no one has any objections). Greg, You are saying that your project is open source, and you would welcome contribution to it, but at the same time seems that your code: - requires third party proprietary module for compilation, - uses HW specific i2c to configure your NIC/timecard. Is that true? If so, do you think that it is proper way to cooperate with an open source community? Excuses me but I hardly understand what you would like to achieve except blocking delivery of this solution. As I see it, your only argument is that SyncE is not a part of 1588. Well sure, SyncE is only mentioned in appendices, but you know, Telecom Profiles are not part of 1588, they are only mentioned in appendices. Should support for them be removed from linuxptp? In my humble opinion, it wouldn't make any sense. Maybe SyncE without PTP does make some sense, but for telecoms it makes most sense to use them both (if hardware supports it). Following this idea, it makes most sense to have both in the same package, as for full support of "Annex L" (1588), the SyncE daemon shall provide data to the PTP daemon. It is easiest to maintain both applications in one repository, this prevents any incompatibility issues between shared interfaces. Richard, I know our solution in its current state is far from being perfect. It communicates with EEC by shell commands specified by the user, but at least it is not specific for any particular hardware. Although it doesn't configure EEC, as it shall be preconfigured before using our solution. Our next steps would be: - introduce in-kernel interface which allows control of EEC, - implement a user-space tool for configuration of EEC using new in-kernel interface, - add support for EEC "get lock status"/"set source" in our solution and Replace shell commands - add support for getting EEC info for Telecom profiles in our solution. Could you please decide if there is a room for this solution in linuxptp, assuming we will review and ACK all the future patches on the SyncE part, and help to maintain it as long as it needs support? We are planning to help extending ptp4l with the interface to interoperate with synce4l (and other similar tools, including Renesas implementation). Also, if you think that it would be better to have it in separated project, please let us know, as we need to upstream it somewhere due to customer requests. Thank you, Arkadiusz |
From: Kubalewski, A. <ark...@in...> - 2022-09-04 16:19:09
|
>-----Original Message----- >From: Greg Armstrong <gre...@re...> >Sent: Friday, September 2, 2022 7:08 PM >To: Kubalewski, Arkadiusz <ark...@in...>; Miroslav Lichvar <mli...@re...>; Richard Cochran <ric...@gm...>; Michalik, Michal <mic...@in...> >Cc: Kwapulinski, Piotr <pio...@in...>; Sawula, Andrzej <and...@in...>; lin...@li...; Gerasymenko, Anatolii <ana...@in...> >Subject: RE: [Linuxptp-devel] [PATCH 00/11] synce4l: add software for Synchronous Ethernet > >Hi Arkadiusz, > >>>I assume that silence on this thread (from the rest of the members), same as >>>silence on any other community mailing list thread, is actually a quiet >>>approval (or at least no one has any objections). >**GA. I do not feel this way - lack oresponse is likely an indication of lack of expertise to review impact to linuxptp - or if your implementation even is compliant to G.8264/G.781 (feel free to contact me outsider of this if you want feedback based on our understanding of G.8264 & G.781). You are adding a new feature/protocol to the linuxptp project, which is overseen by the Network Time Foundation. Thus, I will let them comment if they will allow this. This is not about feelings, but about open source community cooperation. It is initial implementation, It would be great to have this compliant with G.8264/G.781 in 100%, but in time it will get so. I am sure you have heared about iterative development. Why do you say that linuxptp community doesn't have expertise to review the impact on linuxptp? Who else would have? Network Time Foundation supports this project, but it doesn't own it? Not sure why you are trying to imply a narration, where they would have agree on this. Community interested in linuxptp development must agree on this, basing on their expertise in Linux, Timesync domain, tools usage and their development. Protocol of 1588 is not changed with our patches, and it is not your sole decision where the SyncE protocol belongs. Everyone interested in this thread already knows your opinion, the question is if others backup this opinion or not. > >>>Greg, >>>You are saying that your project is open source, and you would welcome >>>contribution to it, but at the same time seems that your code: >>>- requires third party proprietary module for compilation, >**GA. This has already been fixed. However, this is not the right forum to debate ESMC projects - my objection to your patch is an implementation of the ESMC protocol does not belong in the linuxptp project; as it has nothing to do with the 1588 standard. The focus on this project should be on PTP, it's Profiles, and any application that makes use of the PHC (i.e. ts2phc, pmc, phc2sys) - none of this applies to ESMC protocol. > >>>- uses HW specific i2c to configure your NIC/timecard. >>>Is that true? >**GA. Of course you need an interface to HW, how else are you allowing the ESMC protocol to "manage" the SyncE clock as an ITU-T compliant node. That is the limitation of your implementation, as you make a lot of assumptions of a "fixed" configuration of the HW - if the HW is fixed, why would you need a protocol to manage it? > >**GA. Again, no one is arguing you need a HW abstraction for the ESMC protocol to manage the local SyncE clock node. All ESMC implementation I have seen so far have not addressed this in the linux Kernel - but they all claim that is the long-term goal; which is good. What I don't want is someone thinking it should be added to the PHC, as the PHC is "not" for SyncE HW clock. The only applicable interface I could envision for the PHC is to allow for the Local Clock section for the Local PTP Clock (i.e. XO/XTAL vs SyncE). Protocol is mostly about handling ESMC frames and reacting on it. We managed to do such HW abstraction, right now we are also working to make synce4l control all the available sources (together with external ones). I understand your implementation works only with your device, our with any device. Not saying about future, it works right now. > >>>If so, do you think that it is proper way to cooperate with an open source >>>community? >**GA. Again, this is off topic so I will not address here. Please feel free to send me an email directly. > >>>Excuses me but I hardly understand what you would like to achieve except >>>blocking delivery of this solution. As I see it, your only argument is that >>>SyncE is not a part of 1588. Well sure, SyncE is only mentioned in >>>appendices, but you know, Telecom Profiles are not part of 1588, they are only >>>mentioned in appendices. >**GA. It's unfortunate you feel this way. Renesas has been a long-time member of ITU-T and also of OCP. Yes, we are blocking a patch to add an unrelated protocol to an implementation of a PTP protocol, as it does not belong in this project - as most members of the linuxptp community do not have the background or expertise to review contributions to an ESMC implementation. However, adding support for 1588 Profiles, as allowed by the 1588 standard, makes complete sense. It also makes sense to put in supporting SW of "PTP", since it's also interfacing the PHC (i.e. ts2phc, phc2sys). I don't think anyone is suggesting not allowing Profile support to be patch to the linuxptp project. Well, thanks for your opinion. For me, it makes sense when SW is used by someone, if this Telecom profiles are used by linuxptp users, it would be easiest for them to use SyncE from one place and for developers to implement it. > >>>Should support for them be removed from linuxptp? >>>In my humble opinion, it wouldn't make any sense. >**GA. No, it would not make sense to remove an interface between PTP and ESMC, but it should be just that - an interface. If you want to focus your patch to this interface, then please submit that to the linuxptp project. > >>>Maybe SyncE without PTP does make some sense, but for telecoms it makes most >>>sense to use them both (if hardware supports it). >**GA. And I have arguments of only needed ESMC. Again, not saying there will not be an interface between the two protocols, but contributions to either protocol as part of the implementation are mutually exclusive - as they are applicable to independent clocks as defined by the ITU-T. > >>>Following this idea, it makes most sense to have both in the same package, as >>>for full support of "Annex L" (1588), the SyncE daemon shall provide data to >>>the PTP daemon. It is easiest to maintain both applications in one repository, >>>this prevents any incompatibility issues between shared interfaces. >**GA. An argument of code reuse is not valid to submit code to an unrelated project. The ESMC protocol has absolutely no dependencies on PTP or the PHC. The Annex was put in to address the high accuracy profile, which was new in 1588-2019. Again, it does not suggest using the ITU-T concept of SyncE (in fact, HA Profile recommends using a coherent, congruent physical layer clock managed by PTP itself - referenced as L1Sync). Code reuse is minimal, we want to have them together for sake of users and developers. Synchronous Ethernet is mentioned in the Annex, so this is actually a kind of suggestion? > >**GA. You are correct the ITU-T G.8275.1 Profile recommends the use of physical layer "assistance" (and G.8275.2, as optional) - but to be clear, it's assisting the local PTP clock, it is not managing the PTP clock nor does the PTP protocol have any influence on the ESMC protocol/SyncE clock. Thus, Annex L in 1588 is not applicable for the ITU-T Profiles (nor is it referenced by either profile) - as ITU-T assumes the physical layer clock is managed by the ESMC protocol (not PTP, so does not use L1Sync). In my opinion it could fit linuxptp, your is different. That's all. > >**GA. Again, my push back is that any implementation of the ESMC protocol does not belong in the linuxptp project. Instead, the community should focus on how to interface to the ESMC to meet the recommendations of the applicable 1588 Profiles (i.e. ITU-T). And for those profiles that use L1Sync (i.e. HA Profile), by all means, this belongs in linuxptp project as it's part of PTP (and in this case, the L1Sync interface is likely going through PHC) - but please don't confuse this with ITU-T's SyncE as they are not related. > >Regards, >Greg > >Greg Armstrong >Principal System Architect, Timing Products Division >Renesas Electronics Canada Limited >Mobile: 1-613-218-9373 > > Again, thanks for your opinion. Thank you, Arkadiusz |