On 5/2/2022 12:05 PM, Arkadiusz Kubalewski wrote:
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 <piotr.kwapulin...@intel.com> Signed-off-by: Piotr Kwapulinski <piotr.kwapulin...@intel.com> Co-developed-by: Michal Michalik <michal.micha...@intel.com> Signed-off-by: Michal Michalik <michal.micha...@intel.com> Signed-off-by: Arkadiusz Kubalewski <arkadiusz.kubalew...@intel.com> --- synce_clock.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++++++ synce_clock.h | 52 +++++++++ 2 files changed, 348 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 000000000000..8007fa390bbd --- /dev/null +++ b/synce_clock.c @@ -0,0 +1,296 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/** + * @file synce_clock.c + * @brief Implements a SYNCE clock interface. + * @note Copyright (C) 2022 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#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_clock(void)
Please rename to create_synce_clock()
+{ + 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 Sync-E devices initialized"); + goto err; + } else if (count != clk->num_devices) { + pr_warning("initialized only %d from %d Sync-E 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_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 000000000000..e5559a4cc9cc --- /dev/null +++ b/synce_clock.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/** + * @file synce_clock.h + * @brief Implements a Sync-E clock behavior. + * @note Copyright (C) 2022 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef HAVE_SYNCE_CLOCK_H +#define HAVE_SYNCE_CLOCK_H + +#include "config.h" + +/* Opaque type */ +struct synce_clock; + +/** + * Create a Sync-E clock instance. There can only be one synce_clock in a + * system so subsequent calls will destroy the previous clock instance. + * + * @param cfg Pointer to the SYNCE-type configuration database + * @return Pointer to the single global Sync-E 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
_______________________________________________ Linuxptp-devel mailing list Linuxptp-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linuxptp-devel