[PATCH grout] l2: optimize bridge FDB source MAC learning and ageing

Morten Brørup mb at smartsharesystems.com
Wed Jun 10 12:15:24 CEST 2026


Use the much faster per-thread clock snapshot intead of reading the clock.

Only update the bridge FDB entry's last_seen timestamp if at least 1/4
second has passed since its last update.
This reduces the pressure on the CPU's store unit.
Furthermore, if the entry is held in the cache of another CPU core, not
writing to it eliminates the need for the CPU to invalidate the cache line
in the other CPU core.

In the ageing function, calculate the age in nanoseconds instead of
seconds.
This replaces costly division operations by cheaper multiplication
operations.

Signed-off-by: Morten Brørup <mb at smartsharesystems.com>
---
 modules/l2/control/fdb.c | 35 +++++++++++++++++++++--------------
 1 file changed, 21 insertions(+), 14 deletions(-)

diff --git a/modules/l2/control/fdb.c b/modules/l2/control/fdb.c
index 823e8c81..bc22869c 100644
--- a/modules/l2/control/fdb.c
+++ b/modules/l2/control/fdb.c
@@ -1,6 +1,8 @@
 // SPDX-License-Identifier: BSD-3-Clause
 // Copyright (c) 2026 Robin Jarry
+// Copyright (c) 2026 SmartShare Systems
 
+#include "clock.h"
 #include "event.h"
 #include "iface.h"
 #include "l2.h"
@@ -8,8 +10,6 @@
 #include "module.h"
 #include "rcu.h"
 
-#include <gr_clock.h>
-
 #include <rte_common.h>
 #include <rte_hash.h>
 
@@ -115,7 +115,7 @@ void fdb_learn(
 	struct gr_fdb_entry *fdb;
 	void *data;
 
-	if (rte_hash_lookup_data(fdb_hash, &key, &data) < 0) {
+	if (unlikely(rte_hash_lookup_data(fdb_hash, &key, &data) < 0)) {
 		if (rte_mempool_get(fdb_pool, &data) < 0)
 			return; // pool exhausted
 
@@ -126,6 +126,7 @@ void fdb_learn(
 		fdb->flags = GR_FDB_F_LEARN;
 		fdb->iface_id = iface_id;
 		fdb->vtep = *vtep;
+		fdb->last_seen = clock_ns();
 
 		if (rte_hash_add_key_data(fdb_hash, &key, fdb) < 0) {
 			// no space left in hash
@@ -134,19 +135,25 @@ void fdb_learn(
 		}
 
 		event_push(GR_EVENT_FDB_ADD, fdb);
-	} else {
-		fdb = data;
+		return;
 	}
 
-	fdb->last_seen = gr_clock_ns();
+	fdb = data;
 
-	if ((fdb->flags & GR_FDB_F_LEARN)
-	    && (fdb->iface_id != iface_id || !l3_addr_eq(&fdb->vtep, vtep))) {
+	if (unlikely((fdb->iface_id != iface_id) | !l3_addr_eq(&fdb->vtep, vtep))
+	    && (fdb->flags & GR_FDB_F_LEARN)) {
 		// update in case the mac address has moved
 		fdb->iface_id = iface_id;
 		fdb->vtep = *vtep;
+		fdb->last_seen = clock_ns();
 		event_push(GR_EVENT_FDB_UPDATE, fdb);
+		return;
 	}
+
+	// Update timestamp.
+	// Optimization: Don't if less than 1/4 second has passed.
+	if (unlikely(fdb->last_seen + GR_NS_PER_S / 4 <= clock_ns()))
+		fdb->last_seen = clock_ns();
 }
 
 void fdb_purge_iface(uint16_t iface_id) {
@@ -399,10 +406,10 @@ static void fdb_ageing_cb(evutil_socket_t, short /*what*/, void * /*priv*/) {
 	struct gr_fdb_entry *fdb;
 	gr_clock_ns_t now;
 	uint32_t next = 0;
-	uint16_t max_age;
+	gr_clock_ns_t max_age;
 	const void *key;
 	void *data;
-	time_t age;
+	gr_clock_ns_t age;
 
 	now = gr_clock_ns();
 
@@ -412,13 +419,13 @@ 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 = now - fdb->last_seen;
 
 		bridge = iface_from_id(fdb->bridge_id);
 		if (bridge != NULL)
-			max_age = iface_info_bridge(bridge)->ageing_time;
+			max_age = iface_info_bridge(bridge)->ageing_time * GR_NS_PER_S;
 		else
-			max_age = GR_BRIDGE_DEFAULT_AGEING;
+			max_age = GR_BRIDGE_DEFAULT_AGEING * GR_NS_PER_S;
 
 		if (age > max_age) {
 			LOG(DEBUG,
@@ -427,7 +434,7 @@ static void fdb_ageing_cb(evutil_socket_t, short /*what*/, void * /*priv*/) {
 			    fdb->vlan_id,
 			    fdb->bridge_id,
 			    fdb->iface_id,
-			    age);
+			    (time_t)(age / GR_NS_PER_S));
 			rte_hash_del_key(fdb_hash, key);
 		}
 	}
-- 
2.43.0



More information about the grout mailing list