Re: [Linuxptp-devel] [PATCH 04/11] synce4l: add synce_clock interface
PTP IEEE 1588 stack for Linux
Brought to you by:
rcochran
From: Aya L. <ay...@nv...> - 2022-06-08 09:09:50
|
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 <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...> > --- > 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 |