Merge Jiri's Dynamic port allocation series.

master
Richard Cochran 2014-09-21 19:40:49 +02:00
commit e5f20ea6df
7 changed files with 383 additions and 211 deletions

306
clock.c
View File

@ -21,6 +21,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <sys/queue.h>
#include "address.h" #include "address.h"
#include "bmc.h" #include "bmc.h"
@ -40,11 +41,12 @@
#include "uds.h" #include "uds.h"
#include "util.h" #include "util.h"
#define CLK_N_PORTS (MAX_PORTS + 1) /* plus one for the UDS interface */
#define N_CLOCK_PFD (N_POLLFD + 1) /* one extra per port, for the fault timer */ #define N_CLOCK_PFD (N_POLLFD + 1) /* one extra per port, for the fault timer */
#define POW2_41 ((double)(1ULL << 41)) #define POW2_41 ((double)(1ULL << 41))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) struct port {
LIST_ENTRY(port) list;
};
struct freq_estimator { struct freq_estimator {
tmv_t origin1; tmv_t origin1;
@ -80,10 +82,12 @@ struct clock {
struct ClockIdentity ptl[PATH_TRACE_MAX]; struct ClockIdentity ptl[PATH_TRACE_MAX];
struct foreign_clock *best; struct foreign_clock *best;
struct ClockIdentity best_id; struct ClockIdentity best_id;
struct port *port[CLK_N_PORTS]; LIST_HEAD(ports_head, port) ports;
struct pollfd pollfd[CLK_N_PORTS*N_CLOCK_PFD]; struct port *uds_port;
int fault_fd[CLK_N_PORTS]; struct pollfd *pollfd;
int pollfd_valid;
int nports; /* does not include the UDS port */ int nports; /* does not include the UDS port */
int last_port_number;
int free_running; int free_running;
int freq_est_interval; int freq_est_interval;
int grand_master_capable; /* for 802.1AS only */ int grand_master_capable; /* for 802.1AS only */
@ -116,6 +120,8 @@ struct clock {
struct clock the_clock; struct clock the_clock;
static void handle_state_decision_event(struct clock *c); static void handle_state_decision_event(struct clock *c);
static int clock_resize_pollfd(struct clock *c, int new_nports);
static void clock_remove_port(struct clock *c, struct port *p);
static int cid_eq(struct ClockIdentity *a, struct ClockIdentity *b) static int cid_eq(struct ClockIdentity *a, struct ClockIdentity *b)
{ {
@ -234,7 +240,7 @@ void clock_send_notification(struct clock *c, struct ptp_message *msg,
{ {
unsigned int event_pos = event / 8; unsigned int event_pos = event / 8;
uint8_t mask = 1 << (event % 8); uint8_t mask = 1 << (event % 8);
struct port *uds = c->port[c->nports]; struct port *uds = c->uds_port;
struct clock_subscriber *s; struct clock_subscriber *s;
LIST_FOREACH(s, &c->subscribers, list) { LIST_FOREACH(s, &c->subscribers, list) {
@ -254,14 +260,14 @@ void clock_send_notification(struct clock *c, struct ptp_message *msg,
void clock_destroy(struct clock *c) void clock_destroy(struct clock *c)
{ {
int i; struct port *p, *tmp;
clock_flush_subscriptions(c); clock_flush_subscriptions(c);
for (i = 0; i < c->nports; i++) { LIST_FOREACH_SAFE(p, &c->ports, list, tmp) {
port_close(c->port[i]); clock_remove_port(c, p);
close(c->fault_fd[i]);
} }
port_close(c->port[i]); /*uds*/ port_close(c->uds_port);
free(c->pollfd);
if (c->clkid != CLOCK_REALTIME) { if (c->clkid != CLOCK_REALTIME) {
phc_close(c->clkid); phc_close(c->clkid);
} }
@ -276,25 +282,25 @@ void clock_destroy(struct clock *c)
msg_cleanup(); msg_cleanup();
} }
static int clock_fault_timeout(struct clock *c, int index, int set) static int clock_fault_timeout(struct port *port, int set)
{ {
struct fault_interval i; struct fault_interval i;
if (!set) { if (!set) {
pr_debug("clearing fault on port %d", index + 1); pr_debug("clearing fault on port %d", port_number(port));
return set_tmo_lin(c->fault_fd[index], 0); return port_set_fault_timer_lin(port, 0);
} }
fault_interval(c->port[index], last_fault_type(c->port[index]), &i); fault_interval(port, last_fault_type(port), &i);
if (i.type == FTMO_LINEAR_SECONDS) { if (i.type == FTMO_LINEAR_SECONDS) {
pr_debug("waiting %d seconds to clear fault on port %d", pr_debug("waiting %d seconds to clear fault on port %d",
i.val, index + 1); i.val, port_number(port));
return set_tmo_lin(c->fault_fd[index], i.val); return port_set_fault_timer_lin(port, i.val);
} else if (i.type == FTMO_LOG2_SECONDS) { } else if (i.type == FTMO_LOG2_SECONDS) {
pr_debug("waiting 2^{%d} seconds to clear fault on port %d", pr_debug("waiting 2^{%d} seconds to clear fault on port %d",
i.val, index + 1); i.val, port_number(port));
return set_tmo_log(c->fault_fd[index], 1, i.val); return port_set_fault_timer_log(port, 1, i.val);
} }
pr_err("Unsupported fault interval type %d", i.type); pr_err("Unsupported fault interval type %d", i.type);
@ -433,7 +439,7 @@ static int clock_management_fill_response(struct clock *c, struct port *p,
respond = 1; respond = 1;
break; break;
case TLV_SUBSCRIBE_EVENTS_NP: case TLV_SUBSCRIBE_EVENTS_NP:
if (p != c->port[c->nports]) { if (p != c->uds_port) {
/* Only the UDS port allowed. */ /* Only the UDS port allowed. */
break; break;
} }
@ -733,7 +739,7 @@ static int forwarding(struct clock *c, struct port *p)
default: default:
break; break;
} }
if (p == c->port[c->nports] && ps != PS_FAULTY) { /*uds*/ if (p == c->uds_port && ps != PS_FAULTY) {
return 1; return 1;
} }
return 0; return 0;
@ -746,14 +752,49 @@ UInteger8 clock_class(struct clock *c)
return c->dds.clockQuality.clockClass; return c->dds.clockQuality.clockClass;
} }
struct clock *clock_create(int phc_index, struct interface *iface, int count, static int clock_add_port(struct clock *c, int phc_index,
enum timestamp_type timestamping,
struct interface *iface)
{
struct port *p;
if (clock_resize_pollfd(c, c->nports + 1))
return -1;
p = port_open(phc_index, timestamping, ++c->last_port_number,
iface, c);
if (!p) {
/* No need to shrink pollfd */
return -1;
}
LIST_INSERT_HEAD(&c->ports, p, list);
c->nports++;
clock_fda_changed(c);
return 0;
}
static void clock_remove_port(struct clock *c, struct port *p)
{
/* Do not call clock_resize_pollfd, it's pointless to shrink
* the allocated memory at this point, clock_destroy will free
* it all anyway. This function is usable from other parts of
* the code, but even then we don't mind if pollfd is larger
* than necessary. */
LIST_REMOVE(p, list);
c->nports--;
clock_fda_changed(c);
port_close(p);
}
struct clock *clock_create(int phc_index, struct interfaces_head *ifaces,
enum timestamp_type timestamping, struct default_ds *dds, enum timestamp_type timestamping, struct default_ds *dds,
enum servo_type servo) enum servo_type servo)
{ {
int i, fadj = 0, max_adj = 0.0, sw_ts = timestamping == TS_SOFTWARE ? 1 : 0; int fadj = 0, max_adj = 0.0, sw_ts = timestamping == TS_SOFTWARE ? 1 : 0;
struct clock *c = &the_clock; struct clock *c = &the_clock;
struct port *p;
char phc[32]; char phc[32];
struct interface *udsif = &c->uds_interface; struct interface *udsif = &c->uds_interface;
struct interface *iface;
struct timespec ts; struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts); clock_gettime(CLOCK_REALTIME, &ts);
@ -848,45 +889,40 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
c->dad.pds.observedParentClockPhaseChangeRate = 0x7fffffff; c->dad.pds.observedParentClockPhaseChangeRate = 0x7fffffff;
c->dad.ptl = c->ptl; c->dad.ptl = c->ptl;
for (i = 0; i < ARRAY_SIZE(c->pollfd); i++) {
c->pollfd[i].fd = -1;
c->pollfd[i].events = 0;
}
clock_sync_interval(c, 0); clock_sync_interval(c, 0);
LIST_INIT(&c->subscribers); LIST_INIT(&c->subscribers);
LIST_INIT(&c->ports);
for (i = 0; i < count; i++) { c->last_port_number = 0;
c->port[i] = port_open(phc_index, timestamping, 1+i, &iface[i], c);
if (!c->port[i]) {
pr_err("failed to open port %s", iface[i].name);
return NULL;
}
c->fault_fd[i] = timerfd_create(CLOCK_MONOTONIC, 0);
if (c->fault_fd[i] < 0) {
pr_err("timerfd_create failed: %m");
return NULL;
}
c->pollfd[N_CLOCK_PFD * i + N_POLLFD].fd = c->fault_fd[i];
c->pollfd[N_CLOCK_PFD * i + N_POLLFD].events = POLLIN|POLLPRI;
}
/* /*
* One extra port is for the UDS interface. * Create the UDS interface.
*/ */
c->port[i] = port_open(phc_index, timestamping, 0, udsif, c); if (clock_resize_pollfd(c, 0)) {
if (!c->port[i]) { pr_err("failed to allocate pollfd");
return NULL;
}
c->uds_port = port_open(phc_index, timestamping, 0, udsif, c);
if (!c->uds_port) {
pr_err("failed to open the UDS port"); pr_err("failed to open the UDS port");
return NULL; return NULL;
} }
clock_fda_changed(c);
c->dds.numberPorts = c->nports = count; /* Create the ports. */
STAILQ_FOREACH(iface, ifaces, list) {
if (clock_add_port(c, phc_index, timestamping, iface)) {
pr_err("failed to open port %s", iface->name);
return NULL;
}
}
for (i = 0; i < c->nports; i++) c->dds.numberPorts = c->nports;
port_dispatch(c->port[i], EV_INITIALIZE, 0);
port_dispatch(c->port[i], EV_INITIALIZE, 0); /*uds*/ LIST_FOREACH(p, &c->ports, list) {
port_dispatch(p, EV_INITIALIZE, 0);
}
port_dispatch(c->uds_port, EV_INITIALIZE, 0);
return c; return c;
} }
@ -943,40 +979,84 @@ struct ClockIdentity clock_identity(struct clock *c)
return c->dds.clockIdentity; return c->dds.clockIdentity;
} }
void clock_install_fda(struct clock *c, struct port *p, struct fdarray fda) static int clock_resize_pollfd(struct clock *c, int new_nports)
{ {
int i, j, k; struct pollfd *new_pollfd;
for (i = 0; i < c->nports + 1; i++) {
if (p == c->port[i]) /* Need to allocate one extra block of fds for uds */
break; new_pollfd = realloc(c->pollfd,
(new_nports + 1) * N_CLOCK_PFD *
sizeof(struct pollfd));
if (!new_pollfd)
return -1;
c->pollfd = new_pollfd;
return 0;
}
static void clock_fill_pollfd(struct pollfd *dest, struct port *p)
{
struct fdarray *fda;
int i;
fda = port_fda(p);
for (i = 0; i < N_POLLFD; i++) {
dest[i].fd = fda->fd[i];
dest[i].events = POLLIN|POLLPRI;
} }
for (j = 0; j < N_POLLFD; j++) { dest[i].fd = port_fault_fd(p);
k = N_CLOCK_PFD * i + j; dest[i].events = POLLIN|POLLPRI;
c->pollfd[k].fd = fda.fd[j]; }
c->pollfd[k].events = POLLIN|POLLPRI;
static void clock_check_pollfd(struct clock *c)
{
struct port *p;
struct pollfd *dest = c->pollfd;
if (c->pollfd_valid)
return;
LIST_FOREACH(p, &c->ports, list) {
clock_fill_pollfd(dest, p);
dest += N_CLOCK_PFD;
} }
clock_fill_pollfd(dest, c->uds_port);
c->pollfd_valid = 1;
}
void clock_fda_changed(struct clock *c)
{
c->pollfd_valid = 0;
}
static int clock_do_forward_mgmt(struct clock *c,
struct port *in, struct port *out,
struct ptp_message *msg, int *pre_sent)
{
if (in == out || !forwarding(c, out))
return 0;
if (!*pre_sent) {
/* delay calling msg_pre_send until
* actually forwarding */
msg_pre_send(msg);
*pre_sent = 1;
}
return port_forward(out, msg);
} }
static void clock_forward_mgmt_msg(struct clock *c, struct port *p, struct ptp_message *msg) static void clock_forward_mgmt_msg(struct clock *c, struct port *p, struct ptp_message *msg)
{ {
int i, pdulen = 0, msg_ready = 0; struct port *piter;
struct port *fwd; int pdulen = 0, msg_ready = 0;
if (forwarding(c, p) && msg->management.boundaryHops) { if (forwarding(c, p) && msg->management.boundaryHops) {
for (i = 0; i < c->nports + 1; i++) {
fwd = c->port[i];
if (fwd != p && forwarding(c, fwd)) {
/* delay calling msg_pre_send until
* actually forwarding */
if (!msg_ready) {
msg_ready = 1;
pdulen = msg->header.messageLength; pdulen = msg->header.messageLength;
msg->management.boundaryHops--; msg->management.boundaryHops--;
msg_pre_send(msg); LIST_FOREACH(piter, &c->ports, list) {
} if (clock_do_forward_mgmt(c, p, piter, msg, &msg_ready))
if (port_forward(fwd, msg)) pr_err("port %d: management forward failed",
pr_err("port %d: management forward failed", i + 1); port_number(piter));
}
} }
if (clock_do_forward_mgmt(c, p, c->uds_port, msg, &msg_ready))
pr_err("uds port: management forward failed");
if (msg_ready) { if (msg_ready) {
msg_post_recv(msg, pdulen); msg_post_recv(msg, pdulen);
msg->management.boundaryHops++; msg->management.boundaryHops++;
@ -986,7 +1066,8 @@ static void clock_forward_mgmt_msg(struct clock *c, struct port *p, struct ptp_m
int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg) int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
{ {
int changed = 0, i, res, answers; int changed = 0, res, answers;
struct port *piter;
struct management_tlv *mgt; struct management_tlv *mgt;
struct ClockIdentity *tcid, wildcard = { struct ClockIdentity *tcid, wildcard = {
{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
@ -1021,7 +1102,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
clock_management_send_error(p, msg, TLV_WRONG_LENGTH); clock_management_send_error(p, msg, TLV_WRONG_LENGTH);
return changed; return changed;
} }
if (p != c->port[c->nports]) { if (p != c->uds_port) {
/* Sorry, only allowed on the UDS port. */ /* Sorry, only allowed on the UDS port. */
clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); clock_management_send_error(p, msg, TLV_NOT_SUPPORTED);
return changed; return changed;
@ -1037,7 +1118,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
switch (mgt->id) { switch (mgt->id) {
case TLV_PORT_PROPERTIES_NP: case TLV_PORT_PROPERTIES_NP:
if (p != c->port[c->nports]) { if (p != c->uds_port) {
/* Only the UDS port allowed. */ /* Only the UDS port allowed. */
clock_management_send_error(p, msg, TLV_NOT_SUPPORTED); clock_management_send_error(p, msg, TLV_NOT_SUPPORTED);
return 0; return 0;
@ -1082,8 +1163,8 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
break; break;
default: default:
answers = 0; answers = 0;
for (i = 0; i < c->nports; i++) { LIST_FOREACH(piter, &c->ports, list) {
res = port_manage(c->port[i], p, msg); res = port_manage(piter, p, msg);
if (res < 0) if (res < 0)
return changed; return changed;
if (res > 0) if (res > 0)
@ -1101,7 +1182,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
void clock_notify_event(struct clock *c, enum notification event) void clock_notify_event(struct clock *c, enum notification event)
{ {
struct port *uds = c->port[c->nports]; struct port *uds = c->uds_port;
struct PortIdentity pid = port_identity(uds); struct PortIdentity pid = port_identity(uds);
struct ptp_message *msg; struct ptp_message *msg;
UInteger16 msg_len; UInteger16 msg_len;
@ -1139,10 +1220,13 @@ struct PortIdentity clock_parent_identity(struct clock *c)
int clock_poll(struct clock *c) int clock_poll(struct clock *c)
{ {
int cnt, err, i, j, k, sde = 0; int cnt, err, i, sde = 0;
enum fsm_event event; enum fsm_event event;
struct pollfd *cur;
struct port *p;
cnt = poll(c->pollfd, ARRAY_SIZE(c->pollfd), -1); clock_check_pollfd(c);
cnt = poll(c->pollfd, (c->nports + 1) * N_CLOCK_PFD, -1);
if (cnt < 0) { if (cnt < 0) {
if (EINTR == errno) { if (EINTR == errno) {
return 0; return 0;
@ -1154,39 +1238,38 @@ int clock_poll(struct clock *c)
return 0; return 0;
} }
for (i = 0; i < c->nports; i++) { cur = c->pollfd;
LIST_FOREACH(p, &c->ports, list) {
/* Let the ports handle their events. */ /* Let the ports handle their events. */
for (j = err = 0; j < N_POLLFD && !err; j++) { for (i = err = 0; i < N_POLLFD && !err; i++) {
k = N_CLOCK_PFD * i + j; if (cur[i].revents & (POLLIN|POLLPRI)) {
if (c->pollfd[k].revents & (POLLIN|POLLPRI)) { event = port_event(p, i);
event = port_event(c->port[i], j);
if (EV_STATE_DECISION_EVENT == event) if (EV_STATE_DECISION_EVENT == event)
sde = 1; sde = 1;
if (EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES == event) if (EV_ANNOUNCE_RECEIPT_TIMEOUT_EXPIRES == event)
sde = 1; sde = 1;
err = port_dispatch(c->port[i], event, 0); err = port_dispatch(p, event, 0);
/* Clear any fault after a little while. */ /* Clear any fault after a little while. */
if (PS_FAULTY == port_state(c->port[i])) { if (PS_FAULTY == port_state(p)) {
clock_fault_timeout(c, i, 1); clock_fault_timeout(p, 1);
break; break;
} }
} }
} }
/* Check the fault timer. */ /* Check the fault timer. */
k = N_CLOCK_PFD * i + N_POLLFD; if (cur[N_POLLFD].revents & (POLLIN|POLLPRI)) {
if (c->pollfd[k].revents & (POLLIN|POLLPRI)) { clock_fault_timeout(p, 0);
clock_fault_timeout(c, i, 0); port_dispatch(p, EV_FAULT_CLEARED, 0);
port_dispatch(c->port[i], EV_FAULT_CLEARED, 0);
} }
cur += N_CLOCK_PFD;
} }
/* Check the UDS port. */ /* Check the UDS port. */
for (j = 0; j < N_POLLFD; j++) { for (i = 0; i < N_POLLFD; i++) {
k = N_CLOCK_PFD * i + j; if (cur[i].revents & (POLLIN|POLLPRI)) {
if (c->pollfd[k].revents & (POLLIN|POLLPRI)) { event = port_event(c->uds_port, i);
event = port_event(c->port[i], j);
if (EV_STATE_DECISION_EVENT == event) if (EV_STATE_DECISION_EVENT == event)
sde = 1; sde = 1;
} }
@ -1260,20 +1343,6 @@ void clock_peer_delay(struct clock *c, tmv_t ppd, double nrr)
stats_add_value(c->stats.delay, tmv_to_nanoseconds(ppd)); stats_add_value(c->stats.delay, tmv_to_nanoseconds(ppd));
} }
void clock_remove_fda(struct clock *c, struct port *p, struct fdarray fda)
{
int i, j, k;
for (i = 0; i < c->nports + 1; i++) {
if (p == c->port[i])
break;
}
for (j = 0; j < N_POLLFD; j++) {
k = N_CLOCK_PFD * i + j;
c->pollfd[k].fd = -1;
c->pollfd[k].events = 0;
}
}
int clock_slave_only(struct clock *c) int clock_slave_only(struct clock *c)
{ {
return c->dds.flags & DDS_SLAVE_ONLY; return c->dds.flags & DDS_SLAVE_ONLY;
@ -1398,10 +1467,11 @@ static void handle_state_decision_event(struct clock *c)
{ {
struct foreign_clock *best = NULL, *fc; struct foreign_clock *best = NULL, *fc;
struct ClockIdentity best_id; struct ClockIdentity best_id;
int fresh_best = 0, i; struct port *piter;
int fresh_best = 0;
for (i = 0; i < c->nports; i++) { LIST_FOREACH(piter, &c->ports, list) {
fc = port_compute_best(c->port[i]); fc = port_compute_best(piter);
if (!fc) if (!fc)
continue; continue;
if (!best || dscmp(&fc->dataset, &best->dataset) > 0) if (!best || dscmp(&fc->dataset, &best->dataset) > 0)
@ -1430,10 +1500,10 @@ static void handle_state_decision_event(struct clock *c)
c->best = best; c->best = best;
c->best_id = best_id; c->best_id = best_id;
for (i = 0; i < c->nports; i++) { LIST_FOREACH(piter, &c->ports, list) {
enum port_state ps; enum port_state ps;
enum fsm_event event; enum fsm_event event;
ps = bmc_state_decision(c, c->port[i]); ps = bmc_state_decision(c, piter);
switch (ps) { switch (ps) {
case PS_LISTENING: case PS_LISTENING:
event = EV_NONE; event = EV_NONE;
@ -1457,7 +1527,7 @@ static void handle_state_decision_event(struct clock *c)
event = EV_FAULT_DETECTED; event = EV_FAULT_DETECTED;
break; break;
} }
port_dispatch(c->port[i], event, fresh_best); port_dispatch(piter, event, fresh_best);
} }
} }

20
clock.h
View File

@ -63,14 +63,13 @@ UInteger8 clock_class(struct clock *c);
* *
* @param phc_index PTP hardware clock device to use. * @param phc_index PTP hardware clock device to use.
* Pass -1 to select CLOCK_REALTIME. * Pass -1 to select CLOCK_REALTIME.
* @param interface An array of network interfaces. * @param ifaces A queue of network interfaces.
* @param count The number of elements in @a interfaces.
* @param timestamping The timestamping mode for this clock. * @param timestamping The timestamping mode for this clock.
* @param dds A pointer to a default data set for the clock. * @param dds A pointer to a default data set for the clock.
* @param servo The servo that this clock will use. * @param servo The servo that this clock will use.
* @return A pointer to the single global clock instance. * @return A pointer to the single global clock instance.
*/ */
struct clock *clock_create(int phc_index, struct interface *iface, int count, struct clock *clock_create(int phc_index, struct interfaces_head *ifaces,
enum timestamp_type timestamping, struct default_ds *dds, enum timestamp_type timestamping, struct default_ds *dds,
enum servo_type servo); enum servo_type servo);
@ -117,12 +116,11 @@ int clock_gm_capable(struct clock *c);
struct ClockIdentity clock_identity(struct clock *c); struct ClockIdentity clock_identity(struct clock *c);
/** /**
* Install a port's file descriptor array into its controlling clock. * Informs clock that a file descriptor of one of its ports changed. The
* clock will rebuild its array of file descriptors to poll.
* @param c The clock instance. * @param c The clock instance.
* @param p The port installing the array.
* @param fda The port's open file decriptors for its sockets and timers.
*/ */
void clock_install_fda(struct clock *c, struct port *p, struct fdarray fda); void clock_fda_changed(struct clock *c);
/** /**
* Manage the clock according to a given message. * Manage the clock according to a given message.
@ -192,14 +190,6 @@ void clock_peer_delay(struct clock *c, tmv_t ppd, double nrr);
*/ */
int clock_poll(struct clock *c); int clock_poll(struct clock *c);
/**
* Remove a port's file descriptor array from its controlling clock.
* @param c The clock instance.
* @param p The port removing the array.
* @param fda The port's file decriptor array.
*/
void clock_remove_fda(struct clock *c, struct port *p, struct fdarray fda);
/** /**
* Obtain the slave-only flag from a clock's default data set. * Obtain the slave-only flag from a clock's default data set.
* @param c The clock instance. * @param c The clock instance.

View File

@ -20,6 +20,7 @@
#include <float.h> #include <float.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "config.h" #include "config.h"
#include "ether.h" #include "ether.h"
@ -162,40 +163,40 @@ static enum parser_result parse_pod_setting(const char *option,
static enum parser_result parse_port_setting(const char *option, static enum parser_result parse_port_setting(const char *option,
const char *value, const char *value,
struct config *cfg, struct config *cfg,
int p) struct interface *iface)
{ {
enum parser_result r; enum parser_result r;
int val; int val;
r = parse_pod_setting(option, value, &cfg->iface[p].pod); r = parse_pod_setting(option, value, &iface->pod);
if (r != NOT_PARSED) if (r != NOT_PARSED)
return r; return r;
if (!strcmp(option, "network_transport")) { if (!strcmp(option, "network_transport")) {
if (!strcasecmp("L2", value)) if (!strcasecmp("L2", value))
cfg->iface[p].transport = TRANS_IEEE_802_3; iface->transport = TRANS_IEEE_802_3;
else if (!strcasecmp("UDPv4", value)) else if (!strcasecmp("UDPv4", value))
cfg->iface[p].transport = TRANS_UDP_IPV4; iface->transport = TRANS_UDP_IPV4;
else if (!strcasecmp("UDPv6", value)) else if (!strcasecmp("UDPv6", value))
cfg->iface[p].transport = TRANS_UDP_IPV6; iface->transport = TRANS_UDP_IPV6;
else else
return BAD_VALUE; return BAD_VALUE;
} else if (!strcmp(option, "delay_mechanism")) { } else if (!strcmp(option, "delay_mechanism")) {
if (!strcasecmp("Auto", value)) if (!strcasecmp("Auto", value))
cfg->iface[p].dm = DM_AUTO; iface->dm = DM_AUTO;
else if (!strcasecmp("E2E", value)) else if (!strcasecmp("E2E", value))
cfg->iface[p].dm = DM_E2E; iface->dm = DM_E2E;
else if (!strcasecmp("P2P", value)) else if (!strcasecmp("P2P", value))
cfg->iface[p].dm = DM_P2P; iface->dm = DM_P2P;
else else
return BAD_VALUE; return BAD_VALUE;
} else if (!strcmp(option, "delay_filter")) { } else if (!strcmp(option, "delay_filter")) {
if (!strcasecmp("moving_average", value)) if (!strcasecmp("moving_average", value))
cfg->iface[p].delay_filter = FILTER_MOVING_AVERAGE; iface->delay_filter = FILTER_MOVING_AVERAGE;
else if (!strcasecmp("moving_median", value)) else if (!strcasecmp("moving_median", value))
cfg->iface[p].delay_filter = FILTER_MOVING_MEDIAN; iface->delay_filter = FILTER_MOVING_MEDIAN;
else else
return BAD_VALUE; return BAD_VALUE;
@ -203,7 +204,7 @@ static enum parser_result parse_port_setting(const char *option,
r = get_ranged_int(value, &val, 1, INT_MAX); r = get_ranged_int(value, &val, 1, INT_MAX);
if (r != PARSED_OK) if (r != PARSED_OK)
return r; return r;
cfg->iface[p].delay_filter_length = val; iface->delay_filter_length = val;
} else } else
return NOT_PARSED; return NOT_PARSED;
@ -616,7 +617,8 @@ int config_read(char *name, struct config *cfg)
FILE *fp; FILE *fp;
char buf[1024], *line, *c; char buf[1024], *line, *c;
const char *option, *value; const char *option, *value;
int current_port = 0, line_num; struct interface *current_port = NULL;
int line_num;
fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r"); fp = 0 == strncmp(name, "-", 2) ? stdin : fopen(name, "r");
@ -652,8 +654,9 @@ int config_read(char *name, struct config *cfg)
goto parse_error; goto parse_error;
} }
current_port = config_create_interface(port, cfg); current_port = config_create_interface(port, cfg);
if (current_port < 0) if (!current_port)
goto parse_error; goto parse_error;
config_init_interface(current_port, cfg);
} }
continue; continue;
} }
@ -665,7 +668,7 @@ int config_read(char *name, struct config *cfg)
fprintf(stderr, "could not parse line %d in %s section\n", fprintf(stderr, "could not parse line %d in %s section\n",
line_num, line_num,
current_section == GLOBAL_SECTION ? current_section == GLOBAL_SECTION ?
"global" : cfg->iface[current_port].name); "global" : current_port->name);
goto parse_error; goto parse_error;
} }
@ -683,7 +686,7 @@ int config_read(char *name, struct config *cfg)
fprintf(stderr, "unknown option %s at line %d in %s section\n", fprintf(stderr, "unknown option %s at line %d in %s section\n",
option, line_num, option, line_num,
current_section == GLOBAL_SECTION ? current_section == GLOBAL_SECTION ?
"global" : cfg->iface[current_port].name); "global" : current_port->name);
goto parse_error; goto parse_error;
case BAD_VALUE: case BAD_VALUE:
fprintf(stderr, "%s is a bad value for option %s at line %d\n", fprintf(stderr, "%s is a bad value for option %s at line %d\n",
@ -717,36 +720,45 @@ parse_error:
return -2; return -2;
} }
/* returns the number matching that interface, or -1 on failure */ struct interface *config_create_interface(char *name, struct config *cfg)
int config_create_interface(char *name, struct config *cfg)
{ {
struct interface *iface; struct interface *iface;
int i;
if (cfg->nports >= MAX_PORTS) {
fprintf(stderr, "more than %d ports specified\n", MAX_PORTS);
return -1;
}
iface = &cfg->iface[cfg->nports];
/* only create each interface once (by name) */ /* only create each interface once (by name) */
for(i = 0; i < cfg->nports; i++) { STAILQ_FOREACH(iface, &cfg->interfaces, list) {
if (0 == strncmp(name, cfg->iface[i].name, MAX_IFNAME_SIZE)) if (0 == strncmp(name, iface->name, MAX_IFNAME_SIZE))
return i; return iface;
}
iface = calloc(1, sizeof(struct interface));
if (!iface) {
fprintf(stderr, "cannot allocate memory for a port\n");
return NULL;
} }
strncpy(iface->name, name, MAX_IFNAME_SIZE); strncpy(iface->name, name, MAX_IFNAME_SIZE);
STAILQ_INSERT_TAIL(&cfg->interfaces, iface, list);
return iface;
}
void config_init_interface(struct interface *iface, struct config *cfg)
{
iface->dm = cfg->dm; iface->dm = cfg->dm;
iface->transport = cfg->transport; iface->transport = cfg->transport;
memcpy(&iface->pod, &cfg->pod, sizeof(cfg->pod)); memcpy(&iface->pod, &cfg->pod, sizeof(cfg->pod));
sk_get_ts_info(name, &iface->ts_info); sk_get_ts_info(iface->name, &iface->ts_info);
iface->delay_filter = cfg->dds.delay_filter; iface->delay_filter = cfg->dds.delay_filter;
iface->delay_filter_length = cfg->dds.delay_filter_length; iface->delay_filter_length = cfg->dds.delay_filter_length;
}
cfg->nports++;
void config_destroy(struct config *cfg)
return i; {
struct interface *iface;
while ((iface = STAILQ_FIRST(&cfg->interfaces))) {
STAILQ_REMOVE_HEAD(&cfg->interfaces, list);
free(iface);
}
} }

View File

@ -20,6 +20,8 @@
#ifndef HAVE_CONFIG_H #ifndef HAVE_CONFIG_H
#define HAVE_CONFIG_H #define HAVE_CONFIG_H
#include <sys/queue.h>
#include "ds.h" #include "ds.h"
#include "dm.h" #include "dm.h"
#include "filter.h" #include "filter.h"
@ -27,11 +29,11 @@
#include "servo.h" #include "servo.h"
#include "sk.h" #include "sk.h"
#define MAX_PORTS 8
#define MAX_IFNAME_SIZE 108 /* = UNIX_PATH_MAX */ #define MAX_IFNAME_SIZE 108 /* = UNIX_PATH_MAX */
/** Defines a network interface, with PTP options. */ /** Defines a network interface, with PTP options. */
struct interface { struct interface {
STAILQ_ENTRY(interface) list;
char name[MAX_IFNAME_SIZE + 1]; char name[MAX_IFNAME_SIZE + 1];
enum delay_mechanism dm; enum delay_mechanism dm;
enum transport_type transport; enum transport_type transport;
@ -62,8 +64,7 @@ struct config {
int cfg_ignore; int cfg_ignore;
/* configured interfaces */ /* configured interfaces */
struct interface iface[MAX_PORTS]; STAILQ_HEAD(interfaces_head, interface) interfaces;
int nports;
enum timestamp_type timestamping; enum timestamp_type timestamping;
enum transport_type transport; enum transport_type transport;
@ -102,6 +103,8 @@ struct config {
}; };
int config_read(char *name, struct config *cfg); int config_read(char *name, struct config *cfg);
int config_create_interface(char *name, struct config *cfg); struct interface *config_create_interface(char *name, struct config *cfg);
void config_init_interface(struct interface *iface, struct config *cfg);
void config_destroy(struct config *cfg);
#endif #endif

89
port.c
View File

@ -22,6 +22,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/queue.h>
#include "bmc.h" #include "bmc.h"
#include "clock.h" #include "clock.h"
@ -60,11 +61,13 @@ struct nrate_estimator {
}; };
struct port { struct port {
LIST_ENTRY(port) list;
char *name; char *name;
struct clock *clock; struct clock *clock;
struct transport *trp; struct transport *trp;
enum timestamp_type timestamping; enum timestamp_type timestamping;
struct fdarray fda; struct fdarray fda;
int fault_fd;
struct foreign_clock *best; struct foreign_clock *best;
enum syfu_state syfu; enum syfu_state syfu;
struct ptp_message *last_syncfup; struct ptp_message *last_syncfup;
@ -193,6 +196,16 @@ int fault_interval(struct port *port, enum fault_type ft,
return 0; return 0;
} }
int port_fault_fd(struct port *port)
{
return port->fault_fd;
}
struct fdarray *port_fda(struct port *port)
{
return &port->fda;
}
int set_tmo_log(int fd, unsigned int scale, int log_seconds) int set_tmo_log(int fd, unsigned int scale, int log_seconds)
{ {
struct itimerspec tmo = { struct itimerspec tmo = {
@ -253,6 +266,17 @@ int set_tmo_random(int fd, int min, int span, int log_seconds)
return timerfd_settime(fd, 0, &tmo, NULL); return timerfd_settime(fd, 0, &tmo, NULL);
} }
int port_set_fault_timer_log(struct port *port,
unsigned int scale, int log_seconds)
{
return set_tmo_log(port->fault_fd, scale, log_seconds);
}
int port_set_fault_timer_lin(struct port *port, int seconds)
{
return set_tmo_lin(port->fault_fd, seconds);
}
static void fc_clear(struct foreign_clock *fc) static void fc_clear(struct foreign_clock *fc)
{ {
struct ptp_message *m; struct ptp_message *m;
@ -1343,6 +1367,14 @@ static void flush_peer_delay(struct port *p)
} }
} }
static void port_clear_fda(struct port *p, int count)
{
int i;
for (i = 0; i < count; i++)
p->fda.fd[i] = -1;
}
static void port_disable(struct port *p) static void port_disable(struct port *p)
{ {
int i; int i;
@ -1353,12 +1385,13 @@ static void port_disable(struct port *p)
p->best = NULL; p->best = NULL;
free_foreign_masters(p); free_foreign_masters(p);
clock_remove_fda(p->clock, p, p->fda);
transport_close(p->trp, &p->fda); transport_close(p->trp, &p->fda);
for (i = 0; i < N_TIMER_FDS; i++) { for (i = 0; i < N_TIMER_FDS; i++) {
close(p->fda.fd[FD_ANNOUNCE_TIMER + i]); close(p->fda.fd[FD_ANNOUNCE_TIMER + i]);
} }
port_clear_fda(p, N_TIMER_FDS);
clock_fda_changed(p->clock);
} }
static int port_initialize(struct port *p) static int port_initialize(struct port *p)
@ -1401,7 +1434,7 @@ static int port_initialize(struct port *p)
port_nrate_initialize(p); port_nrate_initialize(p);
clock_install_fda(p->clock, p, p->fda); clock_fda_changed(p->clock);
return 0; return 0;
no_tmo: no_tmo:
@ -1417,16 +1450,18 @@ no_timers:
static int port_renew_transport(struct port *p) static int port_renew_transport(struct port *p)
{ {
int res;
if (!port_is_enabled(p)) { if (!port_is_enabled(p)) {
return 0; return 0;
} }
clock_remove_fda(p->clock, p, p->fda);
transport_close(p->trp, &p->fda); transport_close(p->trp, &p->fda);
if (transport_open(p->trp, p->name, &p->fda, p->timestamping)) { port_clear_fda(p, FD_ANNOUNCE_TIMER);
return -1; res = transport_open(p->trp, p->name, &p->fda, p->timestamping);
} /* Need to call clock_fda_changed even if transport_open failed in
clock_install_fda(p->clock, p, p->fda); * order to update clock to the now closed descriptors. */
return 0; clock_fda_changed(p->clock);
return res;
} }
/* /*
@ -1903,6 +1938,8 @@ void port_close(struct port *p)
} }
transport_destroy(p->trp); transport_destroy(p->trp);
filter_destroy(p->delay_filter); filter_destroy(p->delay_filter);
if (p->fault_fd >= 0)
close(p->fault_fd);
free(p); free(p);
} }
@ -2217,6 +2254,11 @@ struct PortIdentity port_identity(struct port *p)
return p->portIdentity; return p->portIdentity;
} }
int port_number(struct port *p)
{
return portnum(p);
}
int port_manage(struct port *p, struct port *ingress, struct ptp_message *msg) int port_manage(struct port *p, struct port *ingress, struct ptp_message *msg)
{ {
struct management_tlv *mgt; struct management_tlv *mgt;
@ -2408,18 +2450,15 @@ struct port *port_open(int phc_index,
pr_err("port %d: PHC device mismatch", number); pr_err("port %d: PHC device mismatch", number);
pr_err("port %d: /dev/ptp%d requested, but /dev/ptp%d attached", pr_err("port %d: /dev/ptp%d requested, but /dev/ptp%d attached",
number, phc_index, interface->ts_info.phc_index); number, phc_index, interface->ts_info.phc_index);
free(p); goto err_port;
return NULL;
} }
p->pod = interface->pod; p->pod = interface->pod;
p->name = interface->name; p->name = interface->name;
p->clock = clock; p->clock = clock;
p->trp = transport_create(interface->transport); p->trp = transport_create(interface->transport);
if (!p->trp) { if (!p->trp)
free(p); goto err_port;
return NULL;
}
p->timestamping = timestamping; p->timestamping = timestamping;
p->portIdentity.clockIdentity = clock_identity(clock); p->portIdentity.clockIdentity = clock_identity(clock);
p->portIdentity.portNumber = number; p->portIdentity.portNumber = number;
@ -2431,12 +2470,28 @@ struct port *port_open(int phc_index,
interface->delay_filter_length); interface->delay_filter_length);
if (!p->delay_filter) { if (!p->delay_filter) {
pr_err("Failed to create delay filter"); pr_err("Failed to create delay filter");
transport_destroy(p->trp); goto err_transport;
free(p);
return NULL;
} }
p->nrate.ratio = 1.0; p->nrate.ratio = 1.0;
port_clear_fda(p, N_TIMER_FDS);
p->fault_fd = -1;
if (number) {
p->fault_fd = timerfd_create(CLOCK_MONOTONIC, 0);
if (p->fault_fd < 0) {
pr_err("timerfd_create failed: %m");
goto err_filter;
}
}
return p; return p;
err_filter:
filter_destroy(p->delay_filter);
err_transport:
transport_destroy(p->trp);
err_port:
free(p);
return NULL;
} }
enum port_state port_state(struct port *port) enum port_state port_state(struct port *port)

45
port.h
View File

@ -119,6 +119,13 @@ int port_prepare_and_send(struct port *p, struct ptp_message *msg, int event);
*/ */
struct PortIdentity port_identity(struct port *p); struct PortIdentity port_identity(struct port *p);
/**
* Obtain a port number.
* @param p A port instance.
* @return The port number of 'p'.
*/
int port_number(struct port *p);
/** /**
* Manage a port according to a given message. * Manage a port according to a given message.
* @param p A pointer previously obtained via port_open(). * @param p A pointer previously obtained via port_open().
@ -200,6 +207,22 @@ struct port *port_open(int phc_index,
*/ */
enum port_state port_state(struct port *port); enum port_state port_state(struct port *port);
/**
* Return array of file descriptors for this port. The fault fd is not
* included.
* @param port A port instance
* @return Array of file descriptors. Unused descriptors are guranteed
* to be set to -1.
*/
struct fdarray *port_fda(struct port *port);
/**
* Return file descriptor of the port.
* @param port A port instance.
* @return File descriptor or -1 if not applicable.
*/
int port_fault_fd(struct port *port);
/** /**
* Utility function for setting or resetting a file descriptor timer. * Utility function for setting or resetting a file descriptor timer.
* *
@ -244,6 +267,28 @@ int set_tmo_random(int fd, int min, int span, int log_seconds);
*/ */
int set_tmo_lin(int fd, int seconds); int set_tmo_lin(int fd, int seconds);
/**
* Sets port's fault file descriptor timer.
* Passing both 'scale' and 'log_seconds' as zero disables the timer.
*
* @param fd A port instance.
* @param scale The multiplicative factor for the timer.
* @param log_seconds The exponential factor for the timer.
* @return Zero on success, non-zero otherwise.
*/
int port_set_fault_timer_log(struct port *port,
unsigned int scale, int log_seconds);
/**
* Sets port's fault file descriptor timer.
* Passing 'seconds' as zero disables the timer.
*
* @param fd A port instance.
* @param seconds The timeout value for the timer.
* @return Zero on success, non-zero otherwise.
*/
int port_set_fault_timer_lin(struct port *port, int seconds);
/** /**
* Returns a port's last fault type. * Returns a port's last fault type.
* *

41
ptp4l.c
View File

@ -40,6 +40,8 @@
int assume_two_step = 0; int assume_two_step = 0;
static struct config cfg_settings = { static struct config cfg_settings = {
.interfaces = STAILQ_HEAD_INITIALIZER(cfg_settings.interfaces),
.dds = { .dds = {
.dds = { .dds = {
.flags = DDS_TWO_STEP_FLAG, .flags = DDS_TWO_STEP_FLAG,
@ -164,9 +166,7 @@ int main(int argc, char *argv[])
{ {
char *config = NULL, *req_phc = NULL, *progname; char *config = NULL, *req_phc = NULL, *progname;
int c, i; int c, i;
struct interface *iface = cfg_settings.iface; struct interface *iface;
char *ports[MAX_PORTS];
int nports = 0;
int *cfg_ignore = &cfg_settings.cfg_ignore; int *cfg_ignore = &cfg_settings.cfg_ignore;
enum delay_mechanism *dm = &cfg_settings.dm; enum delay_mechanism *dm = &cfg_settings.dm;
enum transport_type *transport = &cfg_settings.transport; enum transport_type *transport = &cfg_settings.transport;
@ -229,8 +229,8 @@ int main(int argc, char *argv[])
config = optarg; config = optarg;
break; break;
case 'i': case 'i':
ports[nports] = optarg; if (!config_create_interface(optarg, &cfg_settings))
nports++; return -1;
break; break;
case 'p': case 'p':
req_phc = optarg; req_phc = optarg;
@ -291,14 +291,7 @@ int main(int argc, char *argv[])
print_set_syslog(cfg_settings.use_syslog); print_set_syslog(cfg_settings.use_syslog);
print_set_level(cfg_settings.print_level); print_set_level(cfg_settings.print_level);
for (i = 0; i < nports; i++) { if (STAILQ_EMPTY(&cfg_settings.interfaces)) {
if (config_create_interface(ports[i], &cfg_settings) < 0) {
fprintf(stderr, "too many interfaces\n");
return -1;
}
}
if (!cfg_settings.nports) {
fprintf(stderr, "no interface specified\n"); fprintf(stderr, "no interface specified\n");
usage(progname); usage(progname);
return -1; return -1;
@ -338,18 +331,21 @@ int main(int argc, char *argv[])
break; break;
} }
/* check whether timestamping mode is supported. */ /* Init interface configs and check whether timestamping mode is
for (i = 0; i < cfg_settings.nports; i++) { * supported. */
if (iface[i].ts_info.valid && STAILQ_FOREACH(iface, &cfg_settings.interfaces, list) {
((iface[i].ts_info.so_timestamping & required_modes) != required_modes)) { config_init_interface(iface, &cfg_settings);
if (iface->ts_info.valid &&
((iface->ts_info.so_timestamping & required_modes) != required_modes)) {
fprintf(stderr, "interface '%s' does not support " fprintf(stderr, "interface '%s' does not support "
"requested timestamping mode.\n", "requested timestamping mode.\n",
iface[i].name); iface->name);
return -1; return -1;
} }
} }
/* determine PHC Clock index */ /* determine PHC Clock index */
iface = STAILQ_FIRST(&cfg_settings.interfaces);
if (cfg_settings.dds.free_running) { if (cfg_settings.dds.free_running) {
phc_index = -1; phc_index = -1;
} else if (*timestamping == TS_SOFTWARE || *timestamping == TS_LEGACY_HW) { } else if (*timestamping == TS_SOFTWARE || *timestamping == TS_LEGACY_HW) {
@ -359,8 +355,8 @@ int main(int argc, char *argv[])
fprintf(stderr, "bad ptp device string\n"); fprintf(stderr, "bad ptp device string\n");
return -1; return -1;
} }
} else if (iface[0].ts_info.valid) { } else if (iface->ts_info.valid) {
phc_index = iface[0].ts_info.phc_index; phc_index = iface->ts_info.phc_index;
} else { } else {
fprintf(stderr, "ptp device not specified and\n" fprintf(stderr, "ptp device not specified and\n"
"automatic determination is not\n" "automatic determination is not\n"
@ -372,12 +368,12 @@ int main(int argc, char *argv[])
pr_info("selected /dev/ptp%d as PTP clock", phc_index); pr_info("selected /dev/ptp%d as PTP clock", phc_index);
} }
if (generate_clock_identity(&ds->clockIdentity, iface[0].name)) { if (generate_clock_identity(&ds->clockIdentity, iface->name)) {
fprintf(stderr, "failed to generate a clock identity\n"); fprintf(stderr, "failed to generate a clock identity\n");
return -1; return -1;
} }
clock = clock_create(phc_index, iface, cfg_settings.nports, clock = clock_create(phc_index, &cfg_settings.interfaces,
*timestamping, &cfg_settings.dds, *timestamping, &cfg_settings.dds,
cfg_settings.clock_servo); cfg_settings.clock_servo);
if (!clock) { if (!clock) {
@ -391,5 +387,6 @@ int main(int argc, char *argv[])
} }
clock_destroy(clock); clock_destroy(clock);
config_destroy(&cfg_settings);
return 0; return 0;
} }