On 8/1/2020 10:46 AM, Vladimir Oltean wrote:
> Slaves in ts2phc are PHC devices that implement the extts kernel API.
> They are slaves just in the sense that they timestamp a pulse emitted by
> somebody else.
>
> Currently in ts2phc, PPS slaves are also the only candidates for the
> clocks that get synchronized. There are 2 aspects that make this too
> restrictive:
>
> - Not all PPS slaves may be synchronization slaves. Consider a dynamic
> environment of multiple DSA switches using boundary_clock_jbod, and
> only one port is in the PS_SLAVE state. In that case, the clock of
> that port should be the synchronization master (called the "source
> clock" from now on, as far as ts2phc is concerned), regardless of
> whether it supports the extts API or not.
>
> - Not all synchronization slaves may be PPS slaves. Specifically, the
> "PHC" type of PPS master in ts2phc can also be, fundamentally,
> disciplined. The code should be prepared to handle this by recognizing
> that things that can be disciplined by a servo should be represented
> by a "struct clock", and things that can timestamp external events
> should be represented by a "struct ts2phc_slave".
>
This seems straight forward. Do we have any entity which can timestamp
external events but can't be disciplined by a servo? I don't think we do.
> Signed-off-by: Vladimir Oltean <olte...@gmail.com>
> ---
> ts2phc.c | 69 +++++++++++++++++++++++++++++++++++++++
> ts2phc.h | 26 +++++++++++++++
> ts2phc_slave.c | 88 ++++++++++++++++++++++----------------------------
> 3 files changed, 134 insertions(+), 49 deletions(-)
>
> diff --git a/ts2phc.c b/ts2phc.c
> index 0be7b18bda71..8a4071d083e3 100644
> --- a/ts2phc.c
> +++ b/ts2phc.c
> @@ -7,12 +7,18 @@
> * @note SPDX-License-Identifier: GPL-2.0+
> */
> #include <stdlib.h>
> +#include <net/if.h>
> +#include <sys/types.h>
> +#include <unistd.h>
>
> +#include "clockadj.h"
> #include "config.h"
> #include "interface.h"
> +#include "phc.h"
> #include "print.h"
> #include "ts2phc.h"
> #include "version.h"
> +#include "contain.h"
>
> struct interface {
> STAILQ_ENTRY(interface) list;
> @@ -29,6 +35,69 @@ static void ts2phc_cleanup(struct ts2phc_private *priv)
> }
> }
>
> +struct servo *servo_add(struct ts2phc_private *priv, struct clock *clock)
> +{
> + enum servo_type type = config_get_int(priv->cfg, NULL, "clock_servo");
> + struct servo *servo;
> + int fadj, max_adj;
> +
> + fadj = (int) clockadj_get_freq(clock->clkid);
> + /* Due to a bug in older kernels, the reading may silently fail
> + and return 0. Set the frequency back to make sure fadj is
> + the actual frequency of the clock. */
> + clockadj_set_freq(clock->clkid, fadj);
> +
> + max_adj = phc_max_adj(clock->clkid);
> +
> + servo = servo_create(priv->cfg, type, -fadj, max_adj, 0);
> + if (!servo)
> + return NULL;
> +
> + servo_sync_interval(servo, SERVO_SYNC_INTERVAL);
> +
> + return servo;
> +}
> +
> +struct clock *clock_add(struct ts2phc_private *priv, const char *device)
> +{
> + clockid_t clkid = CLOCK_INVALID;
> + int phc_index = -1;
> + struct clock *c;
> + int err;
> +
> + clkid = posix_clock_open(device, &phc_index);
> + if (clkid == CLOCK_INVALID)
> + return NULL;
> +
> + LIST_FOREACH(c, &priv->clocks, list) {
> + if (c->phc_index == phc_index) {
> + /* Already have the clock, don't add it again */
> + posix_clock_close(clkid);
> + return c;
> + }
> + }
> +
> + c = calloc(1, sizeof(*c));
> + if (!c) {
> + pr_err("failed to allocate memory for a clock");
> + return NULL;
> + }
> + c->clkid = clkid;
> + c->phc_index = phc_index;
> + c->servo_state = SERVO_UNLOCKED;
> + c->servo = servo_add(priv, c);
> + c->no_adj = config_get_int(priv->cfg, NULL, "free_running");
> + err = asprintf(&c->name, "/dev/ptp%d", phc_index);
> + if (err < 0) {
> + free(c);
> + posix_clock_close(clkid);
> + return NULL;
> + }
> +
> + LIST_INSERT_HEAD(&priv->clocks, c, list);
> + return c;
> +}
> +
It feels a bit weird that this code isn't already itself a separate .c
file that can be reused or shared with other programs that create
clocks. But you're mostly just moving this code here from a previous
location so I think it is fine.
> static void usage(char *progname)
> {
> fprintf(stderr,
> diff --git a/ts2phc.h b/ts2phc.h
> index a5d50fc7e3be..135b795f11dc 100644
> --- a/ts2phc.h
> +++ b/ts2phc.h
> @@ -21,16 +21,42 @@
> #ifndef HAVE_TS2PHC_H
> #define HAVE_TS2PHC_H
>
> +#include <sys/queue.h>
> +#include <time.h>
> +#include "servo.h"
> +
> struct ts2phc_slave_array;
>
> +#define SERVO_SYNC_INTERVAL 1.0
> +
> +struct clock {
> + LIST_ENTRY(clock) list;
> + LIST_ENTRY(clock) dst_list;
> + clockid_t clkid;
> + int phc_index;
> + int state;
> + int new_state;
> + struct servo *servo;
> + enum servo_state servo_state;
> + char *name;
> + int no_adj;
> + int is_destination;
> +};
> +
> struct ts2phc_private {
> struct ts2phc_master *master;
> STAILQ_HEAD(slave_ifaces_head, ts2phc_slave) slaves;
> unsigned int n_slaves;
> struct ts2phc_slave_array *polling_array;
> struct config *cfg;
> + struct clock *source;
> + LIST_HEAD(clock_head, clock) clocks;
> };
>
> +struct servo *servo_add(struct ts2phc_private *priv, struct clock *clock);
> +struct clock *clock_add(struct ts2phc_private *priv, const char *device);
> +void clock_add_tstamp(struct clock *clock, struct timespec ts);
> +
> #include "ts2phc_master.h"
> #include "ts2phc_slave.h"
>
> diff --git a/ts2phc_slave.c b/ts2phc_slave.c
> index 7b4fbfa70440..5ed76518ea20 100644
> --- a/ts2phc_slave.c
> +++ b/ts2phc_slave.c
> @@ -26,21 +26,17 @@
>
> #define NS_PER_SEC 1000000000LL
> #define SAMPLE_WEIGHT 1.0
> -#define SERVO_SYNC_INTERVAL 1.0
>
> struct ts2phc_slave {
> char *name;
> STAILQ_ENTRY(ts2phc_slave) list;
> struct ptp_pin_desc pin_desc;
> - enum servo_state state;
> unsigned int polarity;
> int32_t correction;
> uint32_t ignore_lower;
> uint32_t ignore_upper;
> - struct servo *servo;
> - clockid_t clk;
> + struct clock *clock;
> int no_adj;
> - int fd;
> };
>
> struct ts2phc_slave_array {
> @@ -96,8 +92,10 @@ static int ts2phc_slave_array_create(struct ts2phc_private
> *priv)
> i++;
> }
> for (i = 0; i < priv->n_slaves; i++) {
> + struct ts2phc_slave *slave = polling_array->slave[i];
> +
> polling_array->pfd[i].events = POLLIN | POLLPRI;
> - polling_array->pfd[i].fd = polling_array->slave[i]->fd;
> + polling_array->pfd[i].fd = CLOCKID_TO_FD(slave->clock->clkid);
> }
>
> priv->polling_array = polling_array;
> @@ -111,15 +109,15 @@ static void ts2phc_slave_array_destroy(struct
> ts2phc_private *priv)
>
> free(polling_array->slave);
> free(polling_array->pfd);
> - polling_array->slave = NULL;
> - polling_array->pfd = NULL;
> + free(polling_array);
> + priv->polling_array = NULL;
> }
>
> static int ts2phc_slave_clear_fifo(struct ts2phc_slave *slave)
> {
> struct pollfd pfd = {
> .events = POLLIN | POLLPRI,
> - .fd = slave->fd,
> + .fd = CLOCKID_TO_FD(slave->clock->clkid),
> };
> struct ptp_extts_event event;
> int cnt, size;
> @@ -151,10 +149,9 @@ static int ts2phc_slave_clear_fifo(struct ts2phc_slave
> *slave)
> static struct ts2phc_slave *ts2phc_slave_create(struct ts2phc_private *priv,
> const char *device)
> {
> - enum servo_type servo = config_get_int(priv->cfg, NULL, "clock_servo");
> - int err, fadj, junk, max_adj, pulsewidth;
> struct ptp_extts_request extts;
> struct ts2phc_slave *slave;
> + int err, pulsewidth;
>
> slave = calloc(1, sizeof(*slave));
> if (!slave) {
> @@ -183,33 +180,23 @@ static struct ts2phc_slave *ts2phc_slave_create(struct
> ts2phc_private *priv,
> slave->ignore_upper = 1000000000 - pulsewidth;
> slave->ignore_lower = pulsewidth;
>
> - slave->clk = posix_clock_open(device, &junk);
> - if (slave->clk == CLOCK_INVALID) {
> + slave->clock = clock_add(priv, device);
> + if (!slave->clock) {
> pr_err("failed to open clock");
> goto no_posix_clock;
> }
> - slave->no_adj = config_get_int(priv->cfg, NULL, "free_running");
> - slave->fd = CLOCKID_TO_FD(slave->clk);
> -
> - pr_debug("PHC slave %s has ptp index %d", device, junk);
> -
> - fadj = (int) clockadj_get_freq(slave->clk);
> - /* Due to a bug in older kernels, the reading may silently fail
> - and return 0. Set the frequency back to make sure fadj is
> - the actual frequency of the clock. */
> - clockadj_set_freq(slave->clk, fadj);
>
> - max_adj = phc_max_adj(slave->clk);
> + pr_debug("PHC slave %s has ptp index %d", device,
> + slave->clock->phc_index);
>
> - slave->servo = servo_create(priv->cfg, servo, -fadj, max_adj, 0);
> - if (!slave->servo) {
> + slave->clock->servo = servo_add(priv, slave->clock);
> + if (!slave->clock->servo) {
> pr_err("failed to create servo");
> goto no_servo;
> }
> - servo_sync_interval(slave->servo, SERVO_SYNC_INTERVAL);
>
> - if (phc_number_pins(slave->clk) > 0) {
> - err = phc_pin_setfunc(slave->clk, &slave->pin_desc);
> + if (phc_number_pins(slave->clock->clkid) > 0) {
> + err = phc_pin_setfunc(slave->clock->clkid, &slave->pin_desc);
> if (err < 0) {
> pr_err("PTP_PIN_SETFUNC request failed");
> goto no_pin_func;
> @@ -223,7 +210,8 @@ static struct ts2phc_slave *ts2phc_slave_create(struct
> ts2phc_private *priv,
> memset(&extts, 0, sizeof(extts));
> extts.index = slave->pin_desc.chan;
> extts.flags = 0;
> - if (ioctl(slave->fd, PTP_EXTTS_REQUEST2, &extts)) {
> + if (ioctl(CLOCKID_TO_FD(slave->clock->clkid), PTP_EXTTS_REQUEST2,
> + &extts)) {
> pr_err(PTP_EXTTS_REQUEST_FAILED);
> }
> if (ts2phc_slave_clear_fifo(slave)) {
> @@ -233,9 +221,9 @@ static struct ts2phc_slave *ts2phc_slave_create(struct
> ts2phc_private *priv,
> return slave;
> no_ext_ts:
> no_pin_func:
> - servo_destroy(slave->servo);
> + servo_destroy(slave->clock->servo);
> no_servo:
> - posix_clock_close(slave->clk);
> + posix_clock_close(slave->clock->clkid);
> no_posix_clock:
> free(slave->name);
> free(slave);
> @@ -249,11 +237,12 @@ static void ts2phc_slave_destroy(struct ts2phc_slave
> *slave)
> memset(&extts, 0, sizeof(extts));
> extts.index = slave->pin_desc.chan;
> extts.flags = 0;
> - if (ioctl(slave->fd, PTP_EXTTS_REQUEST2, &extts)) {
> + if (ioctl(CLOCKID_TO_FD(slave->clock->clkid), PTP_EXTTS_REQUEST2,
> + &extts)) {
> pr_err(PTP_EXTTS_REQUEST_FAILED);
> }
> - servo_destroy(slave->servo);
> - posix_clock_close(slave->clk);
> + servo_destroy(slave->clock->servo);
> + posix_clock_close(slave->clock->clkid);
> free(slave->name);
> free(slave);
> }
> @@ -281,27 +270,22 @@ static int ts2phc_slave_event(struct ts2phc_slave
> *slave,
> return 0;
> }
>
> - if (!source_ts.valid) {
> - pr_debug("%s ignoring invalid master time stamp", slave->name);
> - return 0;
> - }
> -
> - adj = servo_sample(slave->servo, offset, extts_ts,
> - SAMPLE_WEIGHT, &slave->state);
> + adj = servo_sample(slave->clock->servo, offset, extts_ts,
> + SAMPLE_WEIGHT, &slave->clock->servo_state);
>
> pr_debug("%s master offset %10" PRId64 " s%d freq %+7.0f",
> - slave->name, offset, slave->state, adj);
> + slave->name, offset, slave->clock->servo_state, adj);
>
> - switch (slave->state) {
> + switch (slave->clock->servo_state) {
> case SERVO_UNLOCKED:
> break;
> case SERVO_JUMP:
> - clockadj_set_freq(slave->clk, -adj);
> - clockadj_step(slave->clk, -offset);
> + clockadj_set_freq(slave->clock->clkid, -adj);
> + clockadj_step(slave->clock->clkid, -offset);
> break;
> case SERVO_LOCKED:
> case SERVO_LOCKED_STABLE:
> - clockadj_set_freq(slave->clk, -adj);
> + clockadj_set_freq(slave->clock->clkid, -adj);
> break;
> }
> return 0;
> @@ -317,7 +301,7 @@ static enum extts_result ts2phc_slave_offset(struct
> ts2phc_slave *slave,
> uint64_t event_ns, source_ns;
> int cnt;
>
> - cnt = read(slave->fd, &event, sizeof(event));
> + cnt = read(CLOCKID_TO_FD(slave->clock->clkid), &event, sizeof(event));
> if (cnt != sizeof(event)) {
> pr_err("read extts event failed: %m");
> return EXTTS_ERROR;
> @@ -389,7 +373,8 @@ int ts2phc_slave_arm(struct ts2phc_private *priv)
> STAILQ_FOREACH(slave, &priv->slaves, list) {
> extts.index = slave->pin_desc.chan;
> extts.flags = slave->polarity | PTP_ENABLE_FEATURE;
> - err = ioctl(slave->fd, PTP_EXTTS_REQUEST2, &extts);
> + err = ioctl(CLOCKID_TO_FD(slave->clock->clkid),
> + PTP_EXTTS_REQUEST2, &extts);
> if (err < 0) {
> pr_err(PTP_EXTTS_REQUEST_FAILED);
> return -1;
> @@ -445,6 +430,11 @@ int ts2phc_slave_poll(struct ts2phc_private *priv)
> err = ts2phc_master_getppstime(priv->master, &source_ts.ts);
> source_ts.valid = err ? false : true;
>
> + if (!source_ts.valid) {
> + pr_debug("ignoring invalid master time stamp");
> + return 0;
> + }
> +
> for (i = 0; i < priv->n_slaves; i++) {
> if (polling_array->pfd[i].revents & (POLLIN|POLLPRI)) {
> ts2phc_slave_event(polling_array->slave[i], source_ts);
>
_______________________________________________
Linuxptp-devel mailing list
Linuxptp-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linuxptp-devel