Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions api/gr_clock.h
Original file line number Diff line number Diff line change
@@ -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) {
#define GR_NS_PER_S (gr_clock_ns_t)1000000000LL

// 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 * CLOCKS_PER_SEC) + (tp.tv_nsec / 1000);
return tp.tv_sec * GR_NS_PER_S + tp.tv_nsec;
}
2 changes: 1 addition & 1 deletion cli/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
5 changes: 3 additions & 2 deletions modules/infra/control/bond.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "iface.h"
#include "lacp.h"

#include <gr_clock.h>
#include <gr_infra.h>

#include <stdint.h>
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions modules/infra/control/l3_nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) / GR_NS_PER_S;
max_probes = nh_conf.max_ucast_probes + nh_conf.max_bcast_probes;
probes = l3->ucast_probes + l3->bcast_probes;

Expand Down
14 changes: 7 additions & 7 deletions modules/infra/control/lacp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
struct bond_member *member;
const struct iface *port;
clock_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) {
Expand All @@ -139,9 +139,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;
timeout = LACP_SHORT_TIMEOUT * GR_NS_PER_S;
else
timeout = LACP_LONG_TIMEOUT * US_PER_S;
timeout = LACP_LONG_TIMEOUT * GR_NS_PER_S;

if (now - member->last_rx > timeout && member->active) {
// Partner timed out - enter FAILED state
Expand Down Expand Up @@ -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 * US_PER_S;
member->next_tx = now + LACP_SHORT_TIMEOUT * GR_NS_PER_S;
else
member->next_tx = now + LACP_LONG_TIMEOUT * US_PER_S;
member->next_tx = now + LACP_LONG_TIMEOUT * GR_NS_PER_S;
}

// Update active members list if any port timed out
Expand Down
4 changes: 2 additions & 2 deletions modules/infra/control/nexthop.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion modules/ip/api/gr_ip4.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 4 additions & 5 deletions modules/ip/control/icmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@

#include <rte_icmp.h>

#include <time.h>

struct icmp_queue_item {
struct rte_mbuf *mbuf;
clock_t timestamp;
gr_clock_ns_t timestamp;
STAILQ_ENTRY(icmp_queue_item) next;
};

Expand Down Expand Up @@ -46,7 +44,8 @@ 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;

Expand Down Expand Up @@ -104,8 +103,8 @@ static struct api_out icmp_send(const void *request, struct api_ctx *) {

static struct api_out icmp_recv(const void *request, struct api_ctx *) {
const struct gr_ip4_icmp_recv_req *icmp_req = request;
gr_clock_ns_t *pkt_timestamp, rcv_timestamp;
struct gr_ip4_icmp_recv_resp *resp = NULL;
clock_t *pkt_timestamp, rcv_timestamp;
struct rte_icmp_hdr *icmp;
struct rte_ipv4_hdr *ip;
struct rte_mbuf *m;
Expand Down
2 changes: 1 addition & 1 deletion modules/ip/control/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion modules/ip/datapath/arp_output_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion modules/ip/datapath/icmp_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 5 additions & 5 deletions modules/ip/datapath/icmp_local_send.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,19 @@ static uint16_t icmp_local_send_process(
struct ip_local_mbuf_data *data;
struct rte_icmp_hdr *icmp;
struct ctl_to_stack *msg;
gr_clock_ns_t *payload;
struct rte_mbuf *mbuf;
clock_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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion modules/ip6/api/gr_ip6.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 6 additions & 8 deletions modules/ip6/control/icmp6.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@
#include <gr_api.h>
#include <gr_ip6.h>

#include <time.h>

struct icmp_queue_item {
struct rte_mbuf *mbuf;
clock_t timestamp;
gr_clock_ns_t timestamp;
STAILQ_ENTRY(icmp_queue_item) next;
};

Expand Down Expand Up @@ -44,13 +42,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;
Expand All @@ -61,7 +59,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 *);
Expand Down Expand Up @@ -108,11 +106,11 @@ static struct api_out icmp6_send(const void *request, struct api_ctx *) {

static struct api_out icmp6_recv(const void *request, struct api_ctx *) {
const struct gr_ip6_icmp_recv_req *recvreq = request;
gr_clock_ns_t *pkt_timestamp, rcv_timestamp;
struct icmp6_echo_reply *icmp6_echo;
struct gr_ip6_icmp_recv_resp *resp;
struct ip6_local_mbuf_data *d_ip6;
struct icmp6 *icmp6;
struct icmp6_echo_reply *icmp6_echo;
clock_t *pkt_timestamp, rcv_timestamp;
struct rte_mbuf *m;
int ret = 0;

Expand Down
2 changes: 1 addition & 1 deletion modules/ip6/control/nexthop.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion modules/ip6/datapath/icmp6_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions modules/ip6/datapath/icmp6_local_send.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ static uint16_t icmp6_local_send_process(
struct icmp6_echo_request *icmp6_echo;
struct ip6_local_mbuf_data *data;
struct ctl_to_stack *msg;
gr_clock_ns_t *payload;
struct rte_mbuf *mbuf;
struct icmp6 *icmp6;
clock_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;
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion modules/ip6/datapath/ndp_ns_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion modules/l2/api/gr_l2.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion modules/l2/cli/fdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,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_us() - fdb->last_seen) / CLOCKS_PER_SEC);
gr_table_cell(table, 6, "%ld", (gr_clock_ns() - fdb->last_seen) / GR_NS_PER_S);

if (gr_table_print_row(table) < 0)
break;
Expand Down
Loading
Loading