[PATCH grout v2] replace clock_t with Grout specific high-res clock

Morten Brørup mb at smartsharesystems.com
Mon May 25 17:41:00 CEST 2026


The clock_t type is implementation specific.
E.g. on Linux, it is microseconds, and on Windows it is milliseconds.

Replace all uses of clock_t and its accompanying function, gr_clock_us(),
with a new nanosecond resolution clock type, gr_clock_ns_t, and its
accompanying function, gr_grout_ns().

Note about resolution and size of the new clock type:
A clock with nanosecond resolution can be used for purposes not possible
with microsecond resolution, e.g. shaping and pacing. For reference, the
duration of a minimum size (84 byte) packet on a 100 Gbit/s Ethernet link
is 6.72 ns.
The size of the new type is 64 bit, the same size as the type it replaces
(on supported implementations). Changing the resolution to nanoseconds
thus does not impact the size of data structures where it is used.

Note about signedness of the new clock type:
I condidered making the gr_clock_ns_t unsigned, but that would require
additional considerations in code where it is used, e.g. for calculating
age, to prevent wraparound in race conditions. E.g.:
age = (gr_clock_ns() - fdb->last_seen) / NS_PER_S;
If the current thread reads the clock using gr_clock_ns(), and another
thread races to set fdb->last_seen afterwards, the result of the
subtraction is negative. The division by NS_PER_S makes the age zero.
If gr_clock_ns_t was unsigned, the negative result of the subtraction
would be a very large unsigned number. Dividing this very large number
by NS_PER_S would be a large unsigned number, not zero.
Obviously, this could be fixed by type casting gr_clock_ns_t values to
signed int64_t everywhere they are used with subtraction. But such a
requirement increases the risk of bugs.
So I decided to make it signed, like type_t.

Signed-off-by: Morten Brørup <mb at smartsharesystems.com>
---
v2:
* Fixed lines exceeding 100 characters.
* Added note about resolution and size to the patch description.
---
 api/gr_clock.h                           | 23 ++++++++++++++++++-----
 cli/main.c                               |  2 +-
 modules/infra/control/bond.h             |  5 +++--
 modules/infra/control/l3_nexthop.c       |  4 ++--
 modules/infra/control/lacp.c             | 20 +++++++++-----------
 modules/infra/control/nexthop.h          |  4 ++--
 modules/ip/api/gr_ip4.h                  |  2 +-
 modules/ip/control/icmp.c                | 10 +++++++---
 modules/ip/control/nexthop.c             |  2 +-
 modules/ip/datapath/arp_output_request.c |  2 +-
 modules/ip/datapath/icmp_input.c         |  2 +-
 modules/ip/datapath/icmp_local_send.c    | 10 +++++-----
 modules/ip6/api/gr_ip6.h                 |  2 +-
 modules/ip6/control/icmp6.c              | 10 +++++-----
 modules/ip6/control/nexthop.c            |  2 +-
 modules/ip6/datapath/icmp6_input.c       |  2 +-
 modules/ip6/datapath/icmp6_local_send.c  |  6 +++---
 modules/ip6/datapath/ndp_ns_output.c     |  2 +-
 modules/l2/api/gr_l2.h                   |  3 ++-
 modules/l2/cli/fdb.c                     |  3 ++-
 modules/l2/control/fdb.c                 | 12 ++++++------
 modules/policy/api/gr_conntrack.h        |  2 +-
 modules/policy/cli/conntrack.c           |  6 +++---
 modules/policy/control/conntrack.c       |  6 +++---
 modules/policy/control/conntrack.h       |  2 +-
 smoke/fib_inject.c                       |  6 +++---
 26 files changed, 84 insertions(+), 66 deletions(-)

diff --git a/api/gr_clock.h b/api/gr_clock.h
index 07741b6a..0c1b7424 100644
--- a/api/gr_clock.h
+++ b/api/gr_clock.h
@@ -1,20 +1,33 @@
 // SPDX-License-Identifier: BSD-3-Clause
 // Copyright (c) 2025 Robin Jarry
+// Copyright (c) 2026 SmartShare Systems
 
 #pragma once
 
 #include <stdint.h>
 #include <time.h>
 
-// Get the elapsed time since last boot (using a common clock across all processes).
+// High-resolution clock [nanoseconds].
+// Used with 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;
 }
 
-// Get elapsed time since last boot in microseconds.
-static inline clock_t gr_clock_us(void) {
-	struct timespec tp = gr_clock_raw();
-	return (tp.tv_sec * CLOCKS_PER_SEC) + (tp.tv_nsec / 1000);
+// 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;
+	clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
+	uint64_t ret = (uint64_t)tp.tv_sec * UINT64_C(1000000000) + (uint64_t)tp.tv_nsec;
+	return (int64_t)ret;
 }
diff --git a/cli/main.c b/cli/main.c
index 17b822c6..defaedcf 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_us());
+	srandom(gr_clock_ns());
 
 	if (ec_init() < 0) {
 		errorf("ec_init: %s", strerror(errno));
diff --git a/modules/infra/control/bond.h b/modules/infra/control/bond.h
index 371c5bad..ad7d4dd6 100644
--- a/modules/infra/control/bond.h
+++ b/modules/infra/control/bond.h
@@ -6,6 +6,7 @@
 #include "iface.h"
 #include "lacp.h"
 
+#include <gr_clock.h>
 #include <gr_infra.h>
 
 #include <stdint.h>
@@ -16,8 +17,8 @@ struct bond_member {
 	struct iface *iface;
 	bool active;
 	bool need_to_transmit; // Need to send immediately
-	clock_t next_tx; // Next time we need to send a LACP packet
-	clock_t last_rx; // Last time we received a LACP packet
+	gr_clock_ns_t next_tx; // Next time we need to send a LACP packet
+	gr_clock_ns_t last_rx; // Last time we received a LACP packet
 	// For direct inclusion in LACP packets
 	struct lacp_participant local;
 	struct lacp_participant remote;
diff --git a/modules/infra/control/l3_nexthop.c b/modules/infra/control/l3_nexthop.c
index 90076ea3..92ca484b 100644
--- a/modules/infra/control/l3_nexthop.c
+++ b/modules/infra/control/l3_nexthop.c
@@ -295,12 +295,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;
-	clock_t now = gr_clock_us();
+	gr_clock_ns_t now = gr_clock_ns();
 	unsigned probes, max_probes;
 	time_t reply_age;
 
 	ops = af_ops[l3->af];
-	reply_age = (now - l3->last_reply) / CLOCKS_PER_SEC;
+	reply_age = (now - l3->last_reply) / 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 63fb101b..33f1f68e 100644
--- a/modules/infra/control/lacp.c
+++ b/modules/infra/control/lacp.c
@@ -65,7 +65,7 @@ void lacp_input_cb(void *obj, uintptr_t, const struct control_queue_drain *drain
 
 	// Store partner information from received PDU
 	member->remote = pdu->actor;
-	member->last_rx = gr_clock_us();
+	member->last_rx = gr_clock_ns();
 
 	// Save old member state to detect changes
 	bool old_active = member->active;
@@ -119,10 +119,10 @@ static void lacp_periodic(evutil_socket_t, short, void *) {
 	struct iface_info_bond *bond;
 	struct bond_member *member;
 	const struct iface *port;
-	clock_t now, timeout;
+	gr_clock_ns_t now, timeout;
 	struct iface *iface;
 
-	now = gr_clock_us();
+	now = gr_clock_ns();
 
 	iface = NULL;
 	while ((iface = iface_next(GR_IFACE_TYPE_BOND, iface)) != NULL) {
@@ -138,10 +138,9 @@ static void lacp_periodic(evutil_socket_t, short, void *) {
 
 			// Check for timeout if we've received at least one PDU
 			if (member->last_rx != 0) {
-				if (member->local.state & LACP_STATE_FAST)
-					timeout = LACP_SHORT_TIMEOUT * US_PER_S;
-				else
-					timeout = LACP_LONG_TIMEOUT * US_PER_S;
+				timeout = (member->local.state & LACP_STATE_FAST)
+					? LACP_SHORT_TIMEOUT * (gr_clock_ns_t)NS_PER_S
+					: LACP_LONG_TIMEOUT * (gr_clock_ns_t)NS_PER_S;
 
 				if (now - member->last_rx > timeout && member->active) {
 					// Partner timed out - enter FAILED state
@@ -174,10 +173,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 * US_PER_S;
-			else
-				member->next_tx = now + LACP_LONG_TIMEOUT * US_PER_S;
+			member->next_tx = now + (member->remote.state & LACP_STATE_FAST)
+				? LACP_SHORT_TIMEOUT * (gr_clock_ns_t)NS_PER_S;
+				: LACP_LONG_TIMEOUT * (gr_clock_ns_t)NS_PER_S;
 		}
 
 		// Update active members list if any port timed out
diff --git a/modules/infra/control/nexthop.h b/modules/infra/control/nexthop.h
index 934796bf..5cf72a3a 100644
--- a/modules/infra/control/nexthop.h
+++ b/modules/infra/control/nexthop.h
@@ -41,8 +41,8 @@ static_assert(sizeof(struct nexthop) <= (RTE_CACHE_LINE_MIN_SIZE * 2));
 GR_NH_TYPE_INFO(GR_NH_T_L3, nexthop_info_l3, {
 	BASE(gr_nexthop_info_l3);
 
-	clock_t last_reply; //!< timestamp when last update was received
-	clock_t last_request;
+	gr_clock_ns_t last_reply; //!< timestamp when last update was received
+	gr_clock_ns_t last_request;
 
 	uint8_t ucast_probes;
 	uint8_t bcast_probes;
diff --git a/modules/ip/api/gr_ip4.h b/modules/ip/api/gr_ip4.h
index c150863e..8bf13a4b 100644
--- a/modules/ip/api/gr_ip4.h
+++ b/modules/ip/api/gr_ip4.h
@@ -143,7 +143,7 @@ struct gr_ip4_icmp_recv_resp {
 	uint16_t ident;
 	uint16_t seq_num;
 	ip4_addr_t src_addr;
-	clock_t response_time;
+	gr_clock_ns_t response_time;
 };
 
 GR_REQ(GR_IP4_ICMP_RECV, struct gr_ip4_icmp_recv_req, struct gr_ip4_icmp_recv_resp);
diff --git a/modules/ip/control/icmp.c b/modules/ip/control/icmp.c
index c07712e5..74658e42 100644
--- a/modules/ip/control/icmp.c
+++ b/modules/ip/control/icmp.c
@@ -15,7 +15,7 @@
 
 struct icmp_queue_item {
 	struct rte_mbuf *mbuf;
-	clock_t timestamp;
+	gr_clock_ns_t timestamp;
 	STAILQ_ENTRY(icmp_queue_item) next;
 };
 
@@ -46,7 +46,11 @@ static void icmp_input_cb(void *m, uintptr_t timestamp, const struct control_que
 
 // Search for the oldest ICMP response matching the given identifier.
 // If found, the packet is removed from the queue.
-static struct rte_mbuf *get_icmp_response(uint16_t ident, uint16_t seq_num, clock_t *timestamp) {
+static struct rte_mbuf *get_icmp_response(
+	uint16_t ident,
+	uint16_t seq_num,
+	gr_clock_ns_t *timestamp
+) {
 	struct icmp_queue_item *i, *tmp;
 	struct rte_mbuf *mbuf = NULL;
 
@@ -105,7 +109,7 @@ out:
 static struct api_out icmp_recv(const void *request, struct api_ctx *) {
 	const struct gr_ip4_icmp_recv_req *icmp_req = request;
 	struct gr_ip4_icmp_recv_resp *resp = NULL;
-	clock_t *pkt_timestamp, rcv_timestamp;
+	gr_clock_ns_t *pkt_timestamp, rcv_timestamp;
 	struct rte_icmp_hdr *icmp;
 	struct rte_ipv4_hdr *ip;
 	struct rte_mbuf *m;
diff --git a/modules/ip/control/nexthop.c b/modules/ip/control/nexthop.c
index c9006146..3ef0c3e9 100644
--- a/modules/ip/control/nexthop.c
+++ b/modules/ip/control/nexthop.c
@@ -187,7 +187,7 @@ 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_us();
+		l3->last_reply = gr_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 67db9df0..9633cdcf 100644
--- a/modules/ip/datapath/arp_output_request.c
+++ b/modules/ip/datapath/arp_output_request.c
@@ -35,7 +35,7 @@ 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_us();
+		l3->last_request = gr_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 167cc9d7..b0426246 100644
--- a/modules/ip/datapath/icmp_input.c
+++ b/modules/ip/datapath/icmp_input.c
@@ -55,7 +55,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_us());
+			control_output_set_cb(mbuf, icmp_cb[icmp->icmp_type], gr_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 cc504e5c..3377b3a8 100644
--- a/modules/ip/datapath/icmp_local_send.c
+++ b/modules/ip/datapath/icmp_local_send.c
@@ -90,18 +90,18 @@ static uint16_t icmp_local_send_process(
 	struct rte_icmp_hdr *icmp;
 	struct ctl_to_stack *msg;
 	struct rte_mbuf *mbuf;
-	clock_t *payload;
+	gr_clock_ns_t *payload;
 	rte_edge_t next;
 
 	for (unsigned i = 0; i < n_objs; i++) {
 		mbuf = objs[i];
 		msg = control_input_mbuf_data(mbuf)->data;
 		icmp = (struct rte_icmp_hdr *)rte_pktmbuf_append(
-			mbuf, sizeof(*icmp) + sizeof(clock_t)
+			mbuf, sizeof(*icmp) + sizeof(gr_clock_ns_t)
 		);
 
-		payload = rte_pktmbuf_mtod_offset(mbuf, clock_t *, sizeof(*icmp));
-		*payload = gr_clock_us();
+		payload = rte_pktmbuf_mtod_offset(mbuf, gr_clock_ns_t *, sizeof(*icmp));
+		*payload = gr_clock_ns();
 
 		// Build ICMP packet
 		icmp->icmp_type = RTE_ICMP_TYPE_ECHO_REQUEST;
@@ -116,7 +116,7 @@ static uint16_t icmp_local_send_process(
 
 		data = ip_local_mbuf_data(mbuf);
 		data->proto = IPPROTO_ICMP;
-		data->len = sizeof(*icmp) + sizeof(clock_t);
+		data->len = sizeof(*icmp) + sizeof(gr_clock_ns_t);
 		data->dst = msg->dst;
 		data->src = msg->src;
 		data->vrf_id = msg->vrf_id;
diff --git a/modules/ip6/api/gr_ip6.h b/modules/ip6/api/gr_ip6.h
index bb147a43..d060b191 100644
--- a/modules/ip6/api/gr_ip6.h
+++ b/modules/ip6/api/gr_ip6.h
@@ -208,7 +208,7 @@ struct gr_ip6_icmp_recv_resp {
 	uint16_t ident;
 	uint16_t seq_num;
 	struct rte_ipv6_addr src_addr;
-	clock_t response_time;
+	gr_clock_ns_t response_time;
 };
 
 GR_REQ(GR_IP6_ICMP6_RECV, struct gr_ip6_icmp_recv_req, struct gr_ip6_icmp_recv_resp);
diff --git a/modules/ip6/control/icmp6.c b/modules/ip6/control/icmp6.c
index d0ae86dc..2a3098eb 100644
--- a/modules/ip6/control/icmp6.c
+++ b/modules/ip6/control/icmp6.c
@@ -15,7 +15,7 @@
 
 struct icmp_queue_item {
 	struct rte_mbuf *mbuf;
-	clock_t timestamp;
+	gr_clock_ns_t timestamp;
 	STAILQ_ENTRY(icmp_queue_item) next;
 };
 
@@ -44,13 +44,13 @@ static void icmp6_input_cb(void *m, uintptr_t timestamp, const struct control_qu
 }
 
 #define ICMP6_ERROR_PKT_LEN                                                                        \
-	(GR_ICMP6_HDR_LEN + sizeof(struct rte_ipv6_hdr) + GR_ICMP6_HDR_LEN + sizeof(clock_t))
+	(GR_ICMP6_HDR_LEN + sizeof(struct rte_ipv6_hdr) + GR_ICMP6_HDR_LEN + sizeof(gr_clock_ns_t))
 
 static struct rte_mbuf *get_icmp6_echo_reply(
 	uint16_t ident,
 	uint16_t seq_num,
 	struct icmp6 **out_icmp6,
-	clock_t *timestamp
+	gr_clock_ns_t *timestamp
 ) {
 	struct icmp_queue_item *i, *tmp;
 	struct rte_mbuf *mbuf;
@@ -61,7 +61,7 @@ static struct rte_mbuf *get_icmp6_echo_reply(
 	STAILQ_FOREACH_SAFE (i, &icmp_queue, next, tmp) {
 		mbuf = i->mbuf;
 
-		if (rte_pktmbuf_pkt_len(mbuf) < GR_ICMP6_HDR_LEN + sizeof(clock_t))
+		if (rte_pktmbuf_pkt_len(mbuf) < GR_ICMP6_HDR_LEN + sizeof(gr_clock_ns_t))
 			goto free_and_skip;
 
 		icmp6 = rte_pktmbuf_mtod(mbuf, struct icmp6 *);
@@ -112,7 +112,7 @@ static struct api_out icmp6_recv(const void *request, struct api_ctx *) {
 	struct ip6_local_mbuf_data *d_ip6;
 	struct icmp6 *icmp6;
 	struct icmp6_echo_reply *icmp6_echo;
-	clock_t *pkt_timestamp, rcv_timestamp;
+	gr_clock_ns_t *pkt_timestamp, rcv_timestamp;
 	struct rte_mbuf *m;
 	int ret = 0;
 
diff --git a/modules/ip6/control/nexthop.c b/modules/ip6/control/nexthop.c
index 2012bda1..5e53cc52 100644
--- a/modules/ip6/control/nexthop.c
+++ b/modules/ip6/control/nexthop.c
@@ -236,7 +236,7 @@ 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_us();
+		l3->last_reply = gr_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 6ce747d0..cbeb3f7e 100644
--- a/modules/ip6/datapath/icmp6_input.c
+++ b/modules/ip6/datapath/icmp6_input.c
@@ -82,7 +82,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_us());
+				control_output_set_cb(mbuf, icmp6_cb[icmp6->type], gr_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 676935c2..4da94801 100644
--- a/modules/ip6/datapath/icmp6_local_send.c
+++ b/modules/ip6/datapath/icmp6_local_send.c
@@ -85,14 +85,14 @@ static uint16_t icmp6_local_send_process(
 	struct ctl_to_stack *msg;
 	struct rte_mbuf *mbuf;
 	struct icmp6 *icmp6;
-	clock_t *payload;
+	gr_clock_ns_t *payload;
 	rte_edge_t next;
 	size_t pkt_len;
 
 	for (unsigned i = 0; i < n_objs; i++) {
 		mbuf = objs[i];
 		msg = control_input_mbuf_data(mbuf)->data;
-		pkt_len = sizeof(*icmp6) + sizeof(*icmp6_echo) + sizeof(clock_t);
+		pkt_len = sizeof(*icmp6) + sizeof(*icmp6_echo) + sizeof(gr_clock_ns_t);
 		icmp6 = (struct icmp6 *)rte_pktmbuf_append(mbuf, pkt_len);
 
 		icmp6->type = ICMP6_TYPE_ECHO_REQUEST;
@@ -108,7 +108,7 @@ static uint16_t icmp6_local_send_process(
 		mbuf->ol_flags |= RTE_MBUF_F_RX_RSS_HASH;
 
 		payload = PAYLOAD(icmp6_echo);
-		*payload = gr_clock_us();
+		*payload = gr_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 22f9da34..7c059392 100644
--- a/modules/ip6/datapath/ndp_ns_output.c
+++ b/modules/ip6/datapath/ndp_ns_output.c
@@ -32,7 +32,7 @@ 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_us();
+	l3->last_request = gr_clock_ns();
 	if (l3->ucast_probes < nh_conf.max_ucast_probes)
 		l3->ucast_probes++;
 	else
diff --git a/modules/l2/api/gr_l2.h b/modules/l2/api/gr_l2.h
index b1088a4e..071ec409 100644
--- a/modules/l2/api/gr_l2.h
+++ b/modules/l2/api/gr_l2.h
@@ -5,6 +5,7 @@
 
 #include <gr_api.h>
 #include <gr_bitops.h>
+#include <gr_clock.h>
 #include <gr_macro.h>
 #include <gr_net_types.h>
 
@@ -74,7 +75,7 @@ struct gr_fdb_entry {
 	uint16_t iface_id; // Updated automatically when a MAC moves between members.
 	struct l3_addr vtep; // Remote VTEP for VXLAN-learned entries, 0 for local.
 	gr_fdb_flags_t flags;
-	clock_t last_seen; // Refreshed on each datapath hit for learned entries.
+	gr_clock_ns_t last_seen; // Refreshed on each datapath hit for learned entries.
 };
 
 enum gr_l2_requests : uint32_t {
diff --git a/modules/l2/cli/fdb.c b/modules/l2/cli/fdb.c
index 532e5045..c2c025ca 100644
--- a/modules/l2/cli/fdb.c
+++ b/modules/l2/cli/fdb.c
@@ -138,7 +138,8 @@ 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_us() - fdb->last_seen) / CLOCKS_PER_SEC);
+		gr_table_cell(table, 6, "%ld",
+        	(gr_clock_ns() - fdb->last_seen) / INT64_C(1000000000));
 
 		if (gr_table_print_row(table) < 0)
 			break;
diff --git a/modules/l2/control/fdb.c b/modules/l2/control/fdb.c
index 3982cce1..0c8b87b6 100644
--- a/modules/l2/control/fdb.c
+++ b/modules/l2/control/fdb.c
@@ -138,7 +138,7 @@ void fdb_learn(
 		fdb = data;
 	}
 
-	fdb->last_seen = gr_clock_us();
+	fdb->last_seen = gr_clock_ns();
 
 	if ((fdb->flags & GR_FDB_F_LEARN)
 	    && (fdb->iface_id != iface_id || !l3_addr_eq(&fdb->vtep, vtep))) {
@@ -207,7 +207,7 @@ static struct api_out fdb_add(const void *request, struct api_ctx *) {
 		e = data;
 		*e = req->fdb;
 		e->bridge_id = iface->id;
-		e->last_seen = gr_clock_us();
+		e->last_seen = gr_clock_ns();
 
 		if ((ret = rte_hash_add_key_data(fdb_hash, &key, data)) < 0) {
 			rte_mempool_put(fdb_pool, e);
@@ -219,7 +219,7 @@ static struct api_out fdb_add(const void *request, struct api_ctx *) {
 		e = data;
 		*e = req->fdb;
 		e->bridge_id = iface->id;
-		e->last_seen = gr_clock_us();
+		e->last_seen = gr_clock_ns();
 
 		event_push(GR_EVENT_FDB_UPDATE, e);
 	} else {
@@ -400,11 +400,11 @@ static void fdb_ageing_cb(evutil_socket_t, short /*what*/, void * /*priv*/) {
 	uint32_t next = 0;
 	uint16_t max_age;
 	const void *key;
-	clock_t now;
+	gr_clock_ns_t now;
 	void *data;
 	time_t age;
 
-	now = gr_clock_us();
+	now = gr_clock_ns();
 
 	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) / CLOCKS_PER_SEC;
+		age = (now - fdb->last_seen) / NS_PER_S;
 
 		bridge = iface_from_id(fdb->bridge_id);
 		if (bridge != NULL)
diff --git a/modules/policy/api/gr_conntrack.h b/modules/policy/api/gr_conntrack.h
index de9722b3..5d6bd75b 100644
--- a/modules/policy/api/gr_conntrack.h
+++ b/modules/policy/api/gr_conntrack.h
@@ -70,7 +70,7 @@ struct gr_conntrack {
 	uint8_t proto;
 	struct gr_conntrack_flow fwd_flow;
 	struct gr_conntrack_flow rev_flow;
-	clock_t last_update;
+	gr_clock_ns_t last_update;
 	uint32_t id;
 	gr_conn_state_t state;
 };
diff --git a/modules/policy/cli/conntrack.c b/modules/policy/cli/conntrack.c
index 7eb38121..6b717fc8 100644
--- a/modules/policy/cli/conntrack.c
+++ b/modules/policy/cli/conntrack.c
@@ -16,7 +16,7 @@
 
 static cmd_status_t conn_list(struct gr_api_client *c, const struct ec_pnode *) {
 	const struct gr_conntrack *conn;
-	clock_t now;
+	gr_clock_ns_t now;
 	int ret;
 
 	struct gr_table *table = gr_table_new();
@@ -31,7 +31,7 @@ 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_us();
+	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));
@@ -55,7 +55,7 @@ static cmd_status_t conn_list(struct gr_api_client *c, const struct ec_pnode *)
 
 		gr_table_cell(table, 7, "%u", ntohs(conn->fwd_flow.src_id));
 		gr_table_cell(table, 8, "%u", ntohs(conn->fwd_flow.dst_id));
-		gr_table_cell(table, 9, "%lu", (now - conn->last_update) / 1000000);
+		gr_table_cell(table, 9, "%ld", (now - conn->last_update) / NS_PER_S);
 
 		if (gr_table_print_row(table) < 0)
 			break;
diff --git a/modules/policy/control/conntrack.c b/modules/policy/control/conntrack.c
index 713bc554..fcbf04fe 100644
--- a/modules/policy/control/conntrack.c
+++ b/modules/policy/control/conntrack.c
@@ -240,7 +240,7 @@ again:
 			goto again;
 	}
 
-	atomic_store(&c->last_update, gr_clock_us());
+	atomic_store(&c->last_update, gr_clock_ns());
 }
 
 bool gr_conn_parse_key(
@@ -389,7 +389,7 @@ 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*/) {
-	clock_t now = gr_clock_us(), last;
+	gr_clock_ns_t now = gr_clock_ns(), last;
 	uint64_t age, timeout;
 	struct conn *conn;
 	const void *key;
@@ -444,7 +444,7 @@ static void do_ageing(evutil_socket_t, short /*what*/, void * /*priv*/) {
 		last = atomic_load(&conn->last_update);
 		if (last > now)
 			continue;
-		age = (now - last) / 1000000ULL;
+		age = (now - last) / NS_PER_S;
 		if (age > timeout)
 			gr_conn_destroy(conn);
 	}
diff --git a/modules/policy/control/conntrack.h b/modules/policy/control/conntrack.h
index bb3ea1e5..51f8dd7c 100644
--- a/modules/policy/control/conntrack.h
+++ b/modules/policy/control/conntrack.h
@@ -43,7 +43,7 @@ struct conn {
 	struct conn_key fwd_key;
 	struct conn_key rev_key;
 	_Atomic(gr_conn_state_t) state;
-	_Atomic(clock_t) last_update;
+	_Atomic(gr_clock_ns_t) last_update;
 	struct nat44 nat;
 };
 
diff --git a/smoke/fib_inject.c b/smoke/fib_inject.c
index fee50c32..bf7b96ab 100644
--- a/smoke/fib_inject.c
+++ b/smoke/fib_inject.c
@@ -194,7 +194,7 @@ int main(int argc, char **argv) {
 	unsigned dist_count;
 	bool ipv6 = false;
 	float duration;
-	clock_t time;
+	gr_clock_ns_t time;
 	int ret;
 	int o;
 
@@ -242,14 +242,14 @@ int main(int argc, char **argv) {
 	if (create_nexthops(c) < 0)
 		return EXIT_FAILURE;
 
-	time = gr_clock_us();
+	time = gr_clock_ns();
 
 	if (ipv6)
 		ret = inject_ipv6(c, count);
 	else
 		ret = inject_ipv4(c, count);
 
-	duration = (float)(gr_clock_us() - time) / (float)CLOCKS_PER_SEC;
+	duration = (float)(gr_clock_ns() - time) / (float)1000000000;
 
 	printf("total time: %.1fs (%.1f routes/s)\n", duration, (float)count / duration);
 
-- 
2.43.0



More information about the grout mailing list