Event subscribing
This puts groundwork for event subscription and notification. The individual events are added by subsequent patches. Signed-off-by: Jiri Benc <jbenc@redhat.com>master
parent
d0a67e8b1a
commit
5104e3e56b
134
clock.c
134
clock.c
|
@ -22,6 +22,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "address.h"
|
||||||
#include "bmc.h"
|
#include "bmc.h"
|
||||||
#include "clock.h"
|
#include "clock.h"
|
||||||
#include "clockadj.h"
|
#include "clockadj.h"
|
||||||
|
@ -59,6 +60,14 @@ struct clock_stats {
|
||||||
unsigned int max_count;
|
unsigned int max_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct clock_subscriber {
|
||||||
|
LIST_ENTRY(clock_subscriber) list;
|
||||||
|
uint8_t events[EVENT_BITMASK_CNT];
|
||||||
|
struct PortIdentity targetPortIdentity;
|
||||||
|
struct address addr;
|
||||||
|
UInteger16 sequenceId;
|
||||||
|
};
|
||||||
|
|
||||||
struct clock {
|
struct clock {
|
||||||
clockid_t clkid;
|
clockid_t clkid;
|
||||||
struct servo *servo;
|
struct servo *servo;
|
||||||
|
@ -99,6 +108,7 @@ struct clock {
|
||||||
int stats_interval;
|
int stats_interval;
|
||||||
struct clockcheck *sanity_check;
|
struct clockcheck *sanity_check;
|
||||||
struct interface uds_interface;
|
struct interface uds_interface;
|
||||||
|
LIST_HEAD(clock_subscribers_head, clock_subscriber) subscribers;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct clock the_clock;
|
struct clock the_clock;
|
||||||
|
@ -110,9 +120,114 @@ static int cid_eq(struct ClockIdentity *a, struct ClockIdentity *b)
|
||||||
return 0 == memcmp(a, b, sizeof(*a));
|
return 0 == memcmp(a, b, sizeof(*a));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef LIST_FOREACH_SAFE
|
||||||
|
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||||
|
for ((var) = LIST_FIRST((head)); \
|
||||||
|
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||||
|
(var) = (tvar))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void remove_subscriber(struct clock_subscriber *s)
|
||||||
|
{
|
||||||
|
LIST_REMOVE(s, list);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clock_update_subscription(struct clock *c, struct ptp_message *req,
|
||||||
|
uint8_t *bitmask)
|
||||||
|
{
|
||||||
|
struct clock_subscriber *s;
|
||||||
|
int i, remove = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < EVENT_BITMASK_CNT; i++) {
|
||||||
|
if (bitmask[i]) {
|
||||||
|
remove = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_FOREACH(s, &c->subscribers, list) {
|
||||||
|
if (!memcmp(&s->targetPortIdentity, &req->header.sourcePortIdentity,
|
||||||
|
sizeof(struct PortIdentity))) {
|
||||||
|
/* Found, update the transport address and event
|
||||||
|
* mask. */
|
||||||
|
if (!remove) {
|
||||||
|
s->addr = req->address;
|
||||||
|
memcpy(s->events, bitmask, EVENT_BITMASK_CNT);
|
||||||
|
} else {
|
||||||
|
remove_subscriber(s);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (remove)
|
||||||
|
return;
|
||||||
|
/* Not present yet, add the subscriber. */
|
||||||
|
s = malloc(sizeof(*s));
|
||||||
|
if (!s) {
|
||||||
|
pr_err("failed to allocate memory for a subscriber");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->targetPortIdentity = req->header.sourcePortIdentity;
|
||||||
|
s->addr = req->address;
|
||||||
|
memcpy(s->events, bitmask, EVENT_BITMASK_CNT);
|
||||||
|
s->sequenceId = 0;
|
||||||
|
LIST_INSERT_HEAD(&c->subscribers, s, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clock_get_subscription(struct clock *c, struct ptp_message *req,
|
||||||
|
uint8_t *bitmask)
|
||||||
|
{
|
||||||
|
struct clock_subscriber *s;
|
||||||
|
|
||||||
|
LIST_FOREACH(s, &c->subscribers, list) {
|
||||||
|
if (!memcmp(&s->targetPortIdentity, &req->header.sourcePortIdentity,
|
||||||
|
sizeof(struct PortIdentity))) {
|
||||||
|
memcpy(bitmask, s->events, EVENT_BITMASK_CNT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* A client without entry means the client has no subscriptions. */
|
||||||
|
memset(bitmask, 0, EVENT_BITMASK_CNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clock_flush_subscriptions(struct clock *c)
|
||||||
|
{
|
||||||
|
struct clock_subscriber *s, *tmp;
|
||||||
|
|
||||||
|
LIST_FOREACH_SAFE(s, &c->subscribers, list, tmp) {
|
||||||
|
remove_subscriber(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clock_send_notification(struct clock *c, struct ptp_message *msg,
|
||||||
|
int msglen, enum notification event)
|
||||||
|
{
|
||||||
|
unsigned int event_pos = event / 8;
|
||||||
|
uint8_t mask = 1 << (event % 8);
|
||||||
|
struct port *uds = c->port[c->nports];
|
||||||
|
struct clock_subscriber *s;
|
||||||
|
|
||||||
|
LIST_FOREACH(s, &c->subscribers, list) {
|
||||||
|
if (!(s->events[event_pos] & mask))
|
||||||
|
continue;
|
||||||
|
/* send event */
|
||||||
|
msg->header.sequenceId = htons(s->sequenceId);
|
||||||
|
s->sequenceId++;
|
||||||
|
msg->management.targetPortIdentity.clockIdentity =
|
||||||
|
s->targetPortIdentity.clockIdentity;
|
||||||
|
msg->management.targetPortIdentity.portNumber =
|
||||||
|
htons(s->targetPortIdentity.portNumber);
|
||||||
|
msg->address = s->addr;
|
||||||
|
port_forward_to(uds, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void clock_destroy(struct clock *c)
|
void clock_destroy(struct clock *c)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
clock_flush_subscriptions(c);
|
||||||
for (i = 0; i < c->nports; i++) {
|
for (i = 0; i < c->nports; i++) {
|
||||||
port_close(c->port[i]);
|
port_close(c->port[i]);
|
||||||
close(c->fault_fd[i]);
|
close(c->fault_fd[i]);
|
||||||
|
@ -180,6 +295,7 @@ static int clock_management_get_response(struct clock *c, struct port *p,
|
||||||
struct ptp_message *rsp;
|
struct ptp_message *rsp;
|
||||||
struct time_status_np *tsn;
|
struct time_status_np *tsn;
|
||||||
struct grandmaster_settings_np *gsn;
|
struct grandmaster_settings_np *gsn;
|
||||||
|
struct subscribe_events_np *sen;
|
||||||
struct PortIdentity pid = port_identity(p);
|
struct PortIdentity pid = port_identity(p);
|
||||||
struct PTPText *text;
|
struct PTPText *text;
|
||||||
|
|
||||||
|
@ -288,6 +404,15 @@ static int clock_management_get_response(struct clock *c, struct port *p,
|
||||||
datalen = sizeof(*gsn);
|
datalen = sizeof(*gsn);
|
||||||
respond = 1;
|
respond = 1;
|
||||||
break;
|
break;
|
||||||
|
case SUBSCRIBE_EVENTS_NP:
|
||||||
|
if (p != c->port[c->nports]) {
|
||||||
|
/* Only the UDS port allowed. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sen = (struct subscribe_events_np *)tlv->data;
|
||||||
|
clock_get_subscription(c, req, sen->bitmask);
|
||||||
|
respond = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (respond) {
|
if (respond) {
|
||||||
if (datalen % 2) {
|
if (datalen % 2) {
|
||||||
|
@ -309,6 +434,7 @@ static int clock_management_set(struct clock *c, struct port *p,
|
||||||
int respond = 0;
|
int respond = 0;
|
||||||
struct management_tlv *tlv;
|
struct management_tlv *tlv;
|
||||||
struct grandmaster_settings_np *gsn;
|
struct grandmaster_settings_np *gsn;
|
||||||
|
struct subscribe_events_np *sen;
|
||||||
|
|
||||||
tlv = (struct management_tlv *) req->management.suffix;
|
tlv = (struct management_tlv *) req->management.suffix;
|
||||||
|
|
||||||
|
@ -322,6 +448,11 @@ static int clock_management_set(struct clock *c, struct port *p,
|
||||||
*changed = 1;
|
*changed = 1;
|
||||||
respond = 1;
|
respond = 1;
|
||||||
break;
|
break;
|
||||||
|
case SUBSCRIBE_EVENTS_NP:
|
||||||
|
sen = (struct subscribe_events_np *)tlv->data;
|
||||||
|
clock_update_subscription(c, req, sen->bitmask);
|
||||||
|
respond = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (respond && !clock_management_get_response(c, p, id, req))
|
if (respond && !clock_management_get_response(c, p, id, req))
|
||||||
pr_err("failed to send management set response");
|
pr_err("failed to send management set response");
|
||||||
|
@ -669,6 +800,8 @@ struct clock *clock_create(int phc_index, struct interface *iface, int count,
|
||||||
|
|
||||||
clock_sync_interval(c, 0);
|
clock_sync_interval(c, 0);
|
||||||
|
|
||||||
|
LIST_INIT(&c->subscribers);
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
c->port[i] = port_open(phc_index, timestamping, 1+i, &iface[i], c);
|
c->port[i] = port_open(phc_index, timestamping, 1+i, &iface[i], c);
|
||||||
if (!c->port[i]) {
|
if (!c->port[i]) {
|
||||||
|
@ -880,6 +1013,7 @@ int clock_manage(struct clock *c, struct port *p, struct ptp_message *msg)
|
||||||
case PRIMARY_DOMAIN:
|
case PRIMARY_DOMAIN:
|
||||||
case TIME_STATUS_NP:
|
case TIME_STATUS_NP:
|
||||||
case GRANDMASTER_SETTINGS_NP:
|
case GRANDMASTER_SETTINGS_NP:
|
||||||
|
case SUBSCRIBE_EVENTS_NP:
|
||||||
clock_management_send_error(p, msg, NOT_SUPPORTED);
|
clock_management_send_error(p, msg, NOT_SUPPORTED);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
11
clock.h
11
clock.h
|
@ -23,6 +23,7 @@
|
||||||
#include "dm.h"
|
#include "dm.h"
|
||||||
#include "ds.h"
|
#include "ds.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "notification.h"
|
||||||
#include "servo.h"
|
#include "servo.h"
|
||||||
#include "tlv.h"
|
#include "tlv.h"
|
||||||
#include "tmv.h"
|
#include "tmv.h"
|
||||||
|
@ -133,6 +134,16 @@ void clock_install_fda(struct clock *c, struct port *p, struct fdarray fda);
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send notification about an event to all subscribers.
|
||||||
|
* @param c The clock instance.
|
||||||
|
* @param msg The PTP message to send, in network byte order.
|
||||||
|
* @param msglen The length of the message in bytes.
|
||||||
|
* @param event The event that occured.
|
||||||
|
*/
|
||||||
|
void clock_send_notification(struct clock *c, struct ptp_message *msg,
|
||||||
|
int msglen, enum notification event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain a clock's parent data set.
|
* Obtain a clock's parent data set.
|
||||||
* @param c The clock instance.
|
* @param c The clock instance.
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* @file notification.h
|
||||||
|
* @brief Definitions for the notification framework.
|
||||||
|
* @note Copyright (C) 2014 Red Hat, Inc., Jiri Benc <jbenc@redhat.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
#ifndef HAVE_NOTIFICATION_H
|
||||||
|
#define HAVE_NOTIFICATION_H
|
||||||
|
|
||||||
|
enum notification {
|
||||||
|
NOTIFY_DUMMY,
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
4
tlv.c
4
tlv.c
|
@ -242,6 +242,10 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
|
||||||
pdsnp->neighborPropDelayThresh = ntohl(pdsnp->neighborPropDelayThresh);
|
pdsnp->neighborPropDelayThresh = ntohl(pdsnp->neighborPropDelayThresh);
|
||||||
pdsnp->asCapable = ntohl(pdsnp->asCapable);
|
pdsnp->asCapable = ntohl(pdsnp->asCapable);
|
||||||
break;
|
break;
|
||||||
|
case SUBSCRIBE_EVENTS_NP:
|
||||||
|
if (data_len != sizeof(struct subscribe_events_np))
|
||||||
|
goto bad_length;
|
||||||
|
break;
|
||||||
case SAVE_IN_NON_VOLATILE_STORAGE:
|
case SAVE_IN_NON_VOLATILE_STORAGE:
|
||||||
case RESET_NON_VOLATILE_STORAGE:
|
case RESET_NON_VOLATILE_STORAGE:
|
||||||
case INITIALIZE:
|
case INITIALIZE:
|
||||||
|
|
8
tlv.h
8
tlv.h
|
@ -79,6 +79,7 @@ enum management_action {
|
||||||
#define PRIMARY_DOMAIN 0x4002
|
#define PRIMARY_DOMAIN 0x4002
|
||||||
#define TIME_STATUS_NP 0xC000
|
#define TIME_STATUS_NP 0xC000
|
||||||
#define GRANDMASTER_SETTINGS_NP 0xC001
|
#define GRANDMASTER_SETTINGS_NP 0xC001
|
||||||
|
#define SUBSCRIBE_EVENTS_NP 0xC003
|
||||||
|
|
||||||
/* Port management ID values */
|
/* Port management ID values */
|
||||||
#define NULL_MANAGEMENT 0x0000
|
#define NULL_MANAGEMENT 0x0000
|
||||||
|
@ -196,6 +197,13 @@ struct port_ds_np {
|
||||||
Integer32 asCapable;
|
Integer32 asCapable;
|
||||||
} PACKED;
|
} PACKED;
|
||||||
|
|
||||||
|
|
||||||
|
#define EVENT_BITMASK_CNT 64
|
||||||
|
|
||||||
|
struct subscribe_events_np {
|
||||||
|
uint8_t bitmask[EVENT_BITMASK_CNT];
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
enum clock_type {
|
enum clock_type {
|
||||||
CLOCK_TYPE_ORDINARY = 0x8000,
|
CLOCK_TYPE_ORDINARY = 0x8000,
|
||||||
CLOCK_TYPE_BOUNDARY = 0x4000,
|
CLOCK_TYPE_BOUNDARY = 0x4000,
|
||||||
|
|
Loading…
Reference in New Issue