[PATCH grout] add high-performance clock
Morten Brørup
mb at smartsharesystems.com
Fri Jun 12 20:45:06 CEST 2026
I really want a high-performance clock in Grout.
And since the rte_rdtsc() clock is out of sync with any system wide clock, e.g. CLOCK_MONOTONIC_RAW, I am making the clock private to Grout, and exposing an API for other processes to read it.
This also means that gr_clock_ns() is no longer available as a generic utility function for other processes. (I.e. it cannot be used for profiling in smoke/fib_inject.c or for random generator seeding in cli/main.c, so I have fixed both of these.)
The gr_clock_ns() function has become a client API that reads the clock from the Grout process.
So far, so good!
Now, I'm unsure about where the new clock API and API "implementation" should go.
So I am asking for guidance on this.
For now, there is only one API function, "gr_clock_ns_t gr_clock_ns(struct gr_api_client *);".
I have put it in gr_clock.h as an inline function, but is that the right place?
Should I make it non-inline and add the implementation to gr_api_client_impl.h?
And should I move the clock module stuff from gr_clock.h to gr_api.h?
The "clock" module seems essential, and not really an "infra" module, so I have put it in the main/ directory rather than under modules/infra/. Is that wrong? Should I just consider the "clock" module an "infra" module, and move everything there?
Please see the diff below.
PS: The gr_clock_raw() function was unused, so I removed it.
-Morten
---
cat main/clock.h
---
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 SmartShare Systems
#pragma once
#include <gr_clock.h>
#include <rte_common.h>
#include <rte_cycles.h>
#include <rte_per_lcore.h>
RTE_DECLARE_PER_LCORE(gr_clock_ns_t, clock_ns);
RTE_DECLARE_PER_LCORE(int32_t, clock_s);
// Get common (monotonically increasing) clock from snapshot [nanoseconds].
//
// Resembles CLOCK_MONOTONIC_RAW:
// - Pauses (does not increase) while the system is suspended or hibernated.
// - Accurate for short intervals, where NTP adjustments would distort the measurement.
// - Not accurate for long intervals. It drifts with hardware.
// - - Drifts up to 4.3 seconds/day = 26 minutes/year. (Typical PC XTAL with 50 PPM accuracy.)
//
// Wraps around after hundreds of years.
// Does not return negative values.
//
// Call clock_update() to update the clock snapshots for the current thread.
static __rte_always_inline gr_clock_ns_t clock_ns(void) {
RTE_ASSERT(RTE_PER_LCORE(clock_ns) != INT64_C(-1));
__rte_assume(RTE_PER_LCORE(clock_ns) >= 0);
return RTE_PER_LCORE(clock_ns);
}
// Get common (monotonically increasing) clock from snapshot [seconds].
//
// Resembles CLOCK_MONOTONIC_RAW:
// - Pauses (does not increase) while the system is suspended or hibernated.
// - Not accurate for long intervals. It drifts with hardware.
// - - Drifts up to 4.3 seconds/day = 26 minutes/year. (Typical PC XTAL with 50 PPM accuracy.)
//
// Wraps around after hundreds of years.
// Does not return negative values.
//
// Call clock_update() to update the clock snapshots for the current thread.
static __rte_always_inline int32_t clock_s(void) {
RTE_ASSERT(RTE_PER_LCORE(clock_s) != INT32_C(-1));
__rte_assume(RTE_PER_LCORE(clock_s) >= 0);
return RTE_PER_LCORE(clock_s);
}
// Update the clock snapshots for the current thread.
void clock_update(void);
---
cat main/clock.c
---
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 SmartShare Systems
#include "clock.h"
#include "log.h"
#include "module.h"
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
LOG_TYPE("clock");
RTE_DEFINE_PER_LCORE(gr_clock_ns_t, clock_ns) = INT64_C(-1);
RTE_DEFINE_PER_LCORE(int32_t, clock_s) = INT32_C(-1);
static uint64_t clock_tsc_hz = 0;
void clock_update(void) {
// Considerations regarding types and boundaries, assuming a 10 GHz CPU:
// clock_tsc_hz = 10*10^9 > UINT32_MAX (~4.29*10^9): Requires uint64_t.
// max(tsc % clock_tsc_hz) * GR_NS_PER_S = ~9.99*10^18 < UINT64_MAX (~18.4*10^18): OK.
const uint64_t tsc = rte_rdtsc();
RTE_PER_LCORE(clock_ns) = (tsc / clock_tsc_hz) * GR_NS_PER_S
+ (tsc % clock_tsc_hz) * GR_NS_PER_S / clock_tsc_hz;
RTE_PER_LCORE(clock_s) = (tsc / clock_tsc_hz);
}
static __rte_cold void clock_init(struct event_base *) {
clock_tsc_hz = rte_get_tsc_hz();
LOG(DEBUG, "tsc hz: %lu", clock_tsc_hz);
#if 1
// Performance information
uint64_t tsc;
gr_clock_ns_t ns;
rte_compiler_barrier();
tsc = rte_rdtsc();
ns = (tsc / clock_tsc_hz) * GR_NS_PER_S
+ (tsc % clock_tsc_hz) * GR_NS_PER_S / clock_tsc_hz;
rte_compiler_barrier();
tsc = rte_rdtsc() - tsc;
rte_compiler_barrier();
__asm__ __volatile__("" :: "r" (ns)); // Don't optimize away the calculation of ns.
LOG(DEBUG, "rdtsc: %lu cycles, %.2f ns",
tsc, (float)(tsc * GR_NS_PER_S) / (float)clock_tsc_hz);
#endif
}
static struct api_out clock_get_ns_handler(const void *, struct api_ctx *ctx) {
clock_update();
gr_clock_ns_t clock = clock_ns();
api_send(ctx, sizeof(clock), &clock);
return api_out(0, 0, NULL);
}
static struct module clock_module = {
.name = "clock",
.init = clock_init,
};
RTE_INIT(clock_constructor) {
module_register(&clock_module);
api_handler(GR_CLOCK_GET_NS, clock_get_ns_handler);
}
---
diff --git a/api/gr_clock.h b/api/gr_clock.h
index d2d98fba..ccdcdcf6 100644
--- a/api/gr_clock.h
+++ b/api/gr_clock.h
@@ -4,30 +4,47 @@
#pragma once
+#include <gr_api.h>
+
#include <stdint.h>
#include <time.h>
// High-resolution clock [nanoseconds].
-// Used with CLOCK_MONOTONIC_RAW, unless otherwise specified.
+// Used with Grout clock (resembles CLOCK_MONOTONIC_RAW), unless otherwise specified.
// Note: Does not have Y2038 problems. Not even with CLOCK_REALTIME.
// Note: Using signed, to avoid need for casting to signed
// in calculations where race conditions may cause negative differences.
typedef int64_t gr_clock_ns_t;
-// Get powered-on (non-suspended, non-hibernated) time since last boot,
-// using a common clock across all processes.
-static inline struct timespec gr_clock_raw(void) {
- struct timespec tp = {0};
- clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
- return tp;
-}
+#define GR_NS_PER_S (gr_clock_ns_t)INT64_C(1000000000)
+
+// clock module ////////////////////////////////////////////////////////////////
+
+#define GR_CLOCK_MODULE 0xc10c
+
+enum gr_clock_requests : uint32_t {
+ GR_CLOCK_GET_NS = GR_MSG_TYPE(GR_CLOCK_MODULE, 0x0001),
+};
+
+GR_REQ(GR_CLOCK_GET_NS, struct gr_empty, gr_clock_ns_t);
-#define GR_NS_PER_S (gr_clock_ns_t)1000000000LL
+// Get powered-on (non-suspended, non-hibernated) time since last boot [nanoseconds].
+//
+// Resembles CLOCK_MONOTONIC_RAW:
+// - Pauses (does not increase) while the system is suspended or hibernated.
+// - Accurate for short intervals, where NTP adjustments would distort the measurement.
+// - Not accurate for long intervals. It drifts with hardware.
+// - - Drifts up to 4.3 seconds/day = 26 minutes/year. (Typical PC XTAL with 50 PPM accuracy.)
+//
+// Wraps around after hundreds of years.
+// Returns positive value on success, negative errno on failure.
+static inline gr_clock_ns_t gr_clock_ns(struct gr_api_client *client) {
+ gr_clock_ns_t ret;
+ void *resp_ptr = NULL;
-// Get powered-on (non-suspended, non-hibernated) time since last boot [nanoseconds],
-// using a common clock across all processes.
-// Does not return negative values.
-static inline gr_clock_ns_t gr_clock_ns(void) {
- struct timespec tp = gr_clock_raw();
- return tp.tv_sec * GR_NS_PER_S + tp.tv_nsec;
+ ret = gr_api_client_send_recv(client, GR_CLOCK_GET_NS, 0, NULL, &resp_ptr);
+ if (ret == 0)
+ ret = *(gr_clock_ns_t *)resp_ptr;
+ free(resp_ptr);
+ return ret;
}
diff --git a/cli/main.c b/cli/main.c
index defaedcf..4510c7b4 100644
--- a/cli/main.c
+++ b/cli/main.c
@@ -179,7 +179,7 @@ int main(int argc, char **argv) {
tty_init();
// initialize a non-constant seed for random() calls
- srandom(gr_clock_ns());
+ srandom(time(NULL));
if (ec_init() < 0) {
errorf("ec_init: %s", strerror(errno));
diff --git a/main/meson.build b/main/meson.build
index a57d8600..f0823ff3 100644
--- a/main/meson.build
+++ b/main/meson.build
@@ -3,6 +3,7 @@
src += files(
'api.c',
+ 'clock.c',
'control_queue.c',
'dpdk.c',
'event.c',
diff --git a/modules/infra/control/l3_nexthop.c b/modules/infra/control/l3_nexthop.c
index 79258d54..c4cff9fe 100644
--- a/modules/infra/control/l3_nexthop.c
+++ b/modules/infra/control/l3_nexthop.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Robin Jarry
+#include "clock.h"
#include "iface.h"
#include "log.h"
#include "mbuf.h"
@@ -8,8 +9,6 @@
#include "nexthop.h"
#include "rcu.h"
-#include <gr_clock.h>
-
#include <rte_hash.h>
#include <stdint.h>
@@ -295,12 +294,12 @@ static struct nexthop_type_ops l3_nh_ops = {
static void l3_age(struct nexthop *nh, struct nexthop_info_l3 *l3) {
const struct nexthop_af_ops *ops;
- gr_clock_ns_t now = gr_clock_ns();
unsigned probes, max_probes;
time_t reply_age;
+ clock_update();
ops = af_ops[l3->af];
- reply_age = (now - l3->last_reply) / GR_NS_PER_S;
+ reply_age = (clock_ns() - l3->last_reply) / GR_NS_PER_S;
max_probes = nh_conf.max_ucast_probes + nh_conf.max_bcast_probes;
probes = l3->ucast_probes + l3->bcast_probes;
diff --git a/modules/infra/control/lacp.c b/modules/infra/control/lacp.c
index 54f7930a..faa84bdd 100644
--- a/modules/infra/control/lacp.c
+++ b/modules/infra/control/lacp.c
@@ -2,14 +2,13 @@
// Copyright (c) 2025 Robin Jarry
#include "bond.h"
+#include "clock.h"
#include "iface.h"
#include "lacp.h"
#include "log.h"
#include "mbuf.h"
#include "module.h"
-#include <gr_clock.h>
-
#include <event2/event.h>
#include <rte_mbuf.h>
@@ -64,8 +63,9 @@ void lacp_input_cb(void *obj, uintptr_t, const struct control_queue_drain *drain
pdu = rte_pktmbuf_mtod(mbuf, struct lacp_pdu *);
// Store partner information from received PDU
+ clock_update();
member->remote = pdu->actor;
- member->last_rx = gr_clock_ns();
+ member->last_rx = clock_ns();
// Save old member state to detect changes
bool old_active = member->active;
@@ -117,12 +117,12 @@ static int lacp_send(const struct bond_member *member) {
// Periodic timer callback to send LACP PDUs and check timeouts
static void lacp_periodic(evutil_socket_t, short, void *) {
struct iface_info_bond *bond;
- gr_clock_ns_t now, timeout;
+ gr_clock_ns_t timeout;
struct bond_member *member;
const struct iface *port;
struct iface *iface;
- now = gr_clock_ns();
+ clock_update();
iface = NULL;
while ((iface = iface_next(GR_IFACE_TYPE_BOND, iface)) != NULL) {
@@ -143,7 +143,7 @@ static void lacp_periodic(evutil_socket_t, short, void *) {
else
timeout = LACP_LONG_TIMEOUT * GR_NS_PER_S;
- if (now - member->last_rx > timeout && member->active) {
+ if (clock_ns() - member->last_rx > timeout && member->active) {
// Partner timed out - enter FAILED state
member->active = false;
member->local.state &= ~LACP_STATE_SYNCHRONIZED;
@@ -161,7 +161,7 @@ static void lacp_periodic(evutil_socket_t, short, void *) {
}
// Send LACP PDU if needed
- if (!member->need_to_transmit && member->next_tx > now
+ if (!member->need_to_transmit && member->next_tx > clock_ns()
&& member->last_rx > 0)
continue;
if (!(port->flags & GR_IFACE_F_UP) || !(port->state & GR_IFACE_S_RUNNING))
@@ -175,9 +175,9 @@ static void lacp_periodic(evutil_socket_t, short, void *) {
member->need_to_transmit = false;
if (member->remote.state & LACP_STATE_FAST)
- member->next_tx = now + LACP_SHORT_TIMEOUT * GR_NS_PER_S;
+ member->next_tx = clock_ns() + LACP_SHORT_TIMEOUT * GR_NS_PER_S;
else
- member->next_tx = now + LACP_LONG_TIMEOUT * GR_NS_PER_S;
+ member->next_tx = clock_ns() + LACP_LONG_TIMEOUT * GR_NS_PER_S;
}
// Update active members list if any port timed out
diff --git a/modules/infra/datapath/main_loop.c b/modules/infra/datapath/main_loop.c
index f462cfbd..4127631d 100644
--- a/modules/infra/datapath/main_loop.c
+++ b/modules/infra/datapath/main_loop.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2023 Robin Jarry
+// Copyright (c) 2026 SmartShare Systems
+#include "clock.h"
#include "config.h"
#include "datapath.h"
#include "log.h"
@@ -258,6 +260,7 @@ reconfig:
sleep = 0;
timestamp = rte_rdtsc();
for (;;) {
+ clock_update();
rte_graph_walk(graph);
if (++loop == HOUSEKEEPING_INTERVAL) {
diff --git a/modules/ip/control/nexthop.c b/modules/ip/control/nexthop.c
index 3ef0c3e9..9563bdb3 100644
--- a/modules/ip/control/nexthop.c
+++ b/modules/ip/control/nexthop.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry
+#include "clock.h"
#include "control_input.h"
#include "iface.h"
#include "ip4.h"
@@ -9,7 +10,6 @@
#include "log.h"
#include "module.h"
-#include <gr_clock.h>
#include <gr_net_types.h>
#include <rte_arp.h>
@@ -187,7 +187,8 @@ void arp_probe_input_cb(void *obj, uintptr_t, const struct control_queue_drain *
// static next hops never need updating
if (!(l3->flags & GR_NH_F_STATIC)) {
// Refresh all fields.
- l3->last_reply = gr_clock_ns();
+ clock_update();
+ l3->last_reply = clock_ns();
l3->state = GR_NH_S_REACHABLE;
l3->ucast_probes = 0;
l3->bcast_probes = 0;
diff --git a/modules/ip/datapath/arp_output_request.c b/modules/ip/datapath/arp_output_request.c
index 9633cdcf..a1b31786 100644
--- a/modules/ip/datapath/arp_output_request.c
+++ b/modules/ip/datapath/arp_output_request.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry
+#include "clock.h"
#include "control_input.h"
#include "eth.h"
#include "graph.h"
@@ -9,8 +10,6 @@
#include "ip4_datapath.h"
#include "trace.h"
-#include <gr_clock.h>
-
#include <rte_arp.h>
#include <rte_ether.h>
#include <rte_mbuf.h>
@@ -35,7 +34,8 @@ int arp_output_request_solicit(struct nexthop *nh) {
} else {
// This function is called by the control plane main thread.
// It is OK to modify the nexthop here.
- l3->last_request = gr_clock_ns();
+ clock_update();
+ l3->last_request = clock_ns();
if (l3->ucast_probes < nh_conf.max_ucast_probes)
l3->ucast_probes++;
else
diff --git a/modules/ip/datapath/icmp_input.c b/modules/ip/datapath/icmp_input.c
index b0426246..537fa477 100644
--- a/modules/ip/datapath/icmp_input.c
+++ b/modules/ip/datapath/icmp_input.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry
+#include "clock.h"
#include "control_output.h"
#include "graph.h"
#include "ip4_datapath.h"
@@ -8,8 +9,6 @@
#include "mbuf.h"
#include "trace.h"
-#include <gr_clock.h>
-
#include <rte_icmp.h>
enum {
@@ -55,7 +54,7 @@ icmp_input_process(struct rte_graph *graph, struct rte_node *node, void **objs,
ip_data->src = ip;
edge = OUTPUT;
} else if (icmp_cb[icmp->icmp_type]) {
- control_output_set_cb(mbuf, icmp_cb[icmp->icmp_type], gr_clock_ns());
+ control_output_set_cb(mbuf, icmp_cb[icmp->icmp_type], clock_ns());
edge = CONTROL;
} else {
edge = UNSUPPORTED;
diff --git a/modules/ip/datapath/icmp_local_send.c b/modules/ip/datapath/icmp_local_send.c
index 17fe08a4..4e4570f7 100644
--- a/modules/ip/datapath/icmp_local_send.c
+++ b/modules/ip/datapath/icmp_local_send.c
@@ -1,14 +1,13 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Christophe Fontaine
+#include "clock.h"
#include "control_input.h"
#include "graph.h"
#include "ip4.h"
#include "ip4_datapath.h"
#include "mbuf.h"
-#include <gr_clock.h>
-
#include <rte_icmp.h>
#include <netinet/in.h>
@@ -101,7 +100,7 @@ static uint16_t icmp_local_send_process(
);
payload = rte_pktmbuf_mtod_offset(mbuf, gr_clock_ns_t *, sizeof(*icmp));
- *payload = gr_clock_ns();
+ *payload = clock_ns();
// Build ICMP packet
icmp->icmp_type = RTE_ICMP_TYPE_ECHO_REQUEST;
diff --git a/modules/ip6/control/nexthop.c b/modules/ip6/control/nexthop.c
index 5e53cc52..8412f4e5 100644
--- a/modules/ip6/control/nexthop.c
+++ b/modules/ip6/control/nexthop.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry
+#include "clock.h"
#include "control_input.h"
#include "icmp6.h"
#include "iface.h"
@@ -10,7 +11,6 @@
#include "log.h"
#include "module.h"
-#include <gr_clock.h>
#include <gr_net_types.h>
#include <event2/event.h>
@@ -236,7 +236,8 @@ void ndp_probe_input_cb(void *obj, uintptr_t, const struct control_queue_drain *
if (!(l3->flags & GR_NH_F_STATIC) && lladdr_found == ICMP6_OPT_FOUND) {
// Refresh all fields.
- l3->last_reply = gr_clock_ns();
+ clock_update();
+ l3->last_reply = clock_ns();
l3->state = GR_NH_S_REACHABLE;
l3->ucast_probes = 0;
l3->bcast_probes = 0;
diff --git a/modules/ip6/datapath/icmp6_input.c b/modules/ip6/datapath/icmp6_input.c
index cbeb3f7e..84c6e664 100644
--- a/modules/ip6/datapath/icmp6_input.c
+++ b/modules/ip6/datapath/icmp6_input.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry
+#include "clock.h"
#include "control_output.h"
#include "graph.h"
#include "icmp6.h"
@@ -10,8 +11,6 @@
#include "mbuf.h"
#include "trace.h"
-#include <gr_clock.h>
-
enum {
ICMP6_OUTPUT = 0,
NEIGH_SOLICIT,
@@ -82,7 +81,7 @@ icmp6_input_process(struct rte_graph *graph, struct rte_node *node, void **objs,
case ICMP6_TYPE_ROUTER_ADVERT:
default:
if (icmp6_cb[icmp6->type] != NULL) {
- control_output_set_cb(mbuf, icmp6_cb[icmp6->type], gr_clock_ns());
+ control_output_set_cb(mbuf, icmp6_cb[icmp6->type], clock_ns());
next = CONTROL;
} else {
next = UNSUPPORTED;
diff --git a/modules/ip6/datapath/icmp6_local_send.c b/modules/ip6/datapath/icmp6_local_send.c
index c37fb05d..fcb11a32 100644
--- a/modules/ip6/datapath/icmp6_local_send.c
+++ b/modules/ip6/datapath/icmp6_local_send.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2025 Olivier Gournet
+#include "clock.h"
#include "control_input.h"
#include "graph.h"
#include "icmp6.h"
@@ -8,8 +9,6 @@
#include "ip6_datapath.h"
#include "mbuf.h"
-#include <gr_clock.h>
-
#include <rte_ip6.h>
#include <netinet/in.h>
@@ -108,7 +107,7 @@ static uint16_t icmp6_local_send_process(
mbuf->ol_flags |= RTE_MBUF_F_RX_RSS_HASH;
payload = PAYLOAD(icmp6_echo);
- *payload = gr_clock_ns();
+ *payload = clock_ns();
data = ip6_local_mbuf_data(mbuf);
data->iface = iface_from_id(msg->iface_id);
diff --git a/modules/ip6/datapath/ndp_ns_output.c b/modules/ip6/datapath/ndp_ns_output.c
index 7c059392..f0b08643 100644
--- a/modules/ip6/datapath/ndp_ns_output.c
+++ b/modules/ip6/datapath/ndp_ns_output.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry
+#include "clock.h"
#include "control_input.h"
#include "graph.h"
#include "icmp6.h"
@@ -9,7 +10,6 @@
#include "ip6_datapath.h"
#include "trace.h"
-#include <gr_clock.h>
#include <gr_macro.h>
#include <rte_mbuf.h>
@@ -32,7 +32,8 @@ int nh6_solicit(struct nexthop *nh) {
// This function is called by the control plane main thread.
// It is OK to modify the nexthop here.
- l3->last_request = gr_clock_ns();
+ clock_update();
+ l3->last_request = clock_ns();
if (l3->ucast_probes < nh_conf.max_ucast_probes)
l3->ucast_probes++;
else
diff --git a/modules/l2/cli/fdb.c b/modules/l2/cli/fdb.c
index 48d90b16..d93f0b33 100644
--- a/modules/l2/cli/fdb.c
+++ b/modules/l2/cli/fdb.c
@@ -97,8 +97,12 @@ static cmd_status_t fdb_show(struct gr_api_client *c, const struct ec_pnode *p)
};
const struct gr_fdb_entry *fdb;
char flags[128];
+ gr_clock_ns now;
int ret;
+ if ((now = gr_clock_ns(c)) < 0)
+ return CMD_ERROR;
+
if (arg_str(p, "BRIDGE") != NULL) {
if (arg_iface(c, p, "BRIDGE", GR_IFACE_TYPE_BRIDGE, &req.bridge_id) < 0)
return CMD_ERROR;
@@ -138,7 +142,7 @@ static cmd_status_t fdb_show(struct gr_api_client *c, const struct ec_pnode *p)
if (fdb_format_flags(flags, sizeof(flags), fdb->flags))
gr_table_cell(table, 5, "%s", flags);
- gr_table_cell(table, 6, "%ld", (gr_clock_ns() - fdb->last_seen) / GR_NS_PER_S);
+ gr_table_cell(table, 6, "%ld", (now - fdb->last_seen) / GR_NS_PER_S);
if (gr_table_print_row(table) < 0)
break;
diff --git a/modules/l2/control/fdb.c b/modules/l2/control/fdb.c
index 823e8c81..b42c5873 100644
--- a/modules/l2/control/fdb.c
+++ b/modules/l2/control/fdb.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Robin Jarry
+#include "clock.h"
#include "event.h"
#include "iface.h"
#include "l2.h"
@@ -8,8 +9,6 @@
#include "module.h"
#include "rcu.h"
-#include <gr_clock.h>
-
#include <rte_common.h>
#include <rte_hash.h>
@@ -138,7 +137,7 @@ void fdb_learn(
fdb = data;
}
- fdb->last_seen = gr_clock_ns();
+ fdb->last_seen = clock_ns();
if ((fdb->flags & GR_FDB_F_LEARN)
&& (fdb->iface_id != iface_id || !l3_addr_eq(&fdb->vtep, vtep))) {
@@ -204,10 +203,11 @@ static struct api_out fdb_add(const void *request, struct api_ctx *) {
if ((ret = rte_mempool_get(fdb_pool, &data)) < 0)
return api_out(-ret, 0, NULL);
+ clock_update();
e = data;
*e = req->fdb;
e->bridge_id = iface->id;
- e->last_seen = gr_clock_ns();
+ e->last_seen = clock_ns();
if ((ret = rte_hash_add_key_data(fdb_hash, &key, data)) < 0) {
rte_mempool_put(fdb_pool, e);
@@ -216,10 +216,11 @@ static struct api_out fdb_add(const void *request, struct api_ctx *) {
event_push(GR_EVENT_FDB_ADD, e);
} else if (req->exist_ok) {
+ clock_update();
e = data;
*e = req->fdb;
e->bridge_id = iface->id;
- e->last_seen = gr_clock_ns();
+ e->last_seen = clock_ns();
event_push(GR_EVENT_FDB_UPDATE, e);
} else {
@@ -397,14 +398,13 @@ void fdb_sync_hardware(const struct iface *bridge, struct iface *member, bool ad
static void fdb_ageing_cb(evutil_socket_t, short /*what*/, void * /*priv*/) {
const struct iface *bridge;
struct gr_fdb_entry *fdb;
- gr_clock_ns_t now;
uint32_t next = 0;
uint16_t max_age;
const void *key;
void *data;
time_t age;
- now = gr_clock_ns();
+ clock_update();
while (rte_hash_iterate(fdb_hash, &key, &data, &next) >= 0) {
fdb = data;
@@ -412,7 +412,7 @@ static void fdb_ageing_cb(evutil_socket_t, short /*what*/, void * /*priv*/) {
if ((fdb->flags & GR_FDB_F_STATIC) || !(fdb->flags & GR_FDB_F_LEARN))
continue;
- age = (now - fdb->last_seen) / GR_NS_PER_S;
+ age = (clock_ns() - fdb->last_seen) / GR_NS_PER_S;
bridge = iface_from_id(fdb->bridge_id);
if (bridge != NULL)
diff --git a/modules/policy/cli/conntrack.c b/modules/policy/cli/conntrack.c
index 051cd01c..11596342 100644
--- a/modules/policy/cli/conntrack.c
+++ b/modules/policy/cli/conntrack.c
@@ -19,6 +19,9 @@ static cmd_status_t conn_list(struct gr_api_client *c, const struct ec_pnode *)
gr_clock_ns_t now;
int ret;
+ if ((now = gr_clock_ns(c)) < 0)
+ return CMD_ERROR;
+
struct gr_table *table = gr_table_new();
gr_table_column(table, "IFACE", GR_DISP_LEFT); // 0
gr_table_column(table, "ID", GR_DISP_RIGHT); // 1
@@ -31,8 +34,6 @@ static cmd_status_t conn_list(struct gr_api_client *c, const struct ec_pnode *)
gr_table_column(table, "DPORT", GR_DISP_RIGHT | GR_DISP_INT); // 8
gr_table_column(table, "LAST_UPDATE", GR_DISP_RIGHT); // 9
- now = gr_clock_ns();
-
gr_api_client_stream_foreach (conn, ret, c, GR_CONNTRACK_LIST, 0, NULL) {
gr_table_cell(table, 0, "%s", iface_name_from_id(c, conn->iface_id));
gr_table_cell(table, 1, "0x%08x", conn->id);
diff --git a/modules/policy/control/conntrack.c b/modules/policy/control/conntrack.c
index 43793fa2..0d1cf180 100644
--- a/modules/policy/control/conntrack.c
+++ b/modules/policy/control/conntrack.c
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2025 Robin Jarry
+#include "clock.h"
#include "conntrack.h"
#include "log.h"
#include "module.h"
#include "rcu.h"
-#include <gr_clock.h>
#include <gr_net_types.h>
#include <rte_hash.h>
@@ -240,7 +240,7 @@ again:
goto again;
}
- atomic_store(&c->last_update, gr_clock_ns());
+ atomic_store(&c->last_update, clock_ns());
}
bool gr_conn_parse_key(
@@ -389,13 +389,14 @@ struct conn *gr_conn_insert(const struct conn_key *fwd_key, const struct conn_ke
}
static void do_ageing(evutil_socket_t, short /*what*/, void * /*priv*/) {
- gr_clock_ns_t now = gr_clock_ns(), last;
+ gr_clock_ns_t last;
uint64_t age, timeout;
struct conn *conn;
const void *key;
uint32_t iter;
void *data;
+ clock_update();
iter = 0;
while (rte_hash_iterate(conn_hash, &key, &data, &iter) >= 0) {
conn = conn_ptr(data);
@@ -442,9 +443,9 @@ static void do_ageing(evutil_socket_t, short /*what*/, void * /*priv*/) {
}
last = atomic_load(&conn->last_update);
- if (last > now)
+ if (last > clock_ns())
continue;
- age = (now - last) / GR_NS_PER_S;
+ age = (clock_ns() - last) / GR_NS_PER_S;
if (age > timeout)
gr_conn_destroy(conn);
}
diff --git a/smoke/fib_inject.c b/smoke/fib_inject.c
index dc050625..8871a54b 100644
--- a/smoke/fib_inject.c
+++ b/smoke/fib_inject.c
@@ -192,7 +192,7 @@ int main(int argc, char **argv) {
unsigned count = 10000;
uint32_t installed = 0;
unsigned dist_count;
- gr_clock_ns_t time;
+ struct timespec before, after;
bool ipv6 = false;
float duration;
int ret;
@@ -242,14 +242,17 @@ int main(int argc, char **argv) {
if (create_nexthops(c) < 0)
return EXIT_FAILURE;
- time = gr_clock_ns();
+ clock_gettime(CLOCK_MONOTONIC_RAW, &before);
if (ipv6)
ret = inject_ipv6(c, count);
else
ret = inject_ipv4(c, count);
- duration = (float)(gr_clock_ns() - time) / (float)GR_NS_PER_S;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &after);
+
+ duration = (float)(after.tv_sec - before.tv_sec)
+ + (float)(after.tv_nsec - before.tv_nsec) / (float)GR_NS_PER_S;
printf("total time: %.1fs (%.1f routes/s)\n", duration, (float)count / duration);
More information about the grout
mailing list