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

Morten Brørup mb at smartsharesystems.com
Thu May 21 19:59:25 CEST 2026


In the bridge_input node, read the clock once outside the loop, cache the
value, and pass the cached value as a parameter to fdb_learn() inside the
loop.
This reduces the number of times the node reads the clock, which is a
costly operation, from once per packet to once per burst of packets.

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.

Signed-off-by: Morten Brørup <mb at smartsharesystems.com>
---
 modules/l2/control/fdb.c           | 19 +++++++++++++------
 modules/l2/control/l2.h            |  3 ++-
 modules/l2/datapath/bridge_input.c |  6 +++++-
 3 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/modules/l2/control/fdb.c b/modules/l2/control/fdb.c
index 3982cce1..2f706dec 100644
--- a/modules/l2/control/fdb.c
+++ b/modules/l2/control/fdb.c
@@ -109,13 +109,14 @@ void fdb_learn(
 	uint16_t iface_id,
 	const struct rte_ether_addr *mac,
 	uint16_t vlan_id,
-	const struct l3_addr *vtep
+	const struct l3_addr *vtep,
+	const clock_t now
 ) {
 	const struct fdb_key key = {bridge_id, vlan_id, *mac};
 	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 +127,7 @@ void fdb_learn(
 		fdb->flags = GR_FDB_F_LEARN;
 		fdb->iface_id = iface_id;
 		fdb->vtep = *vtep;
+		fdb->last_seen = now;
 
 		if (rte_hash_add_key_data(fdb_hash, &key, fdb) < 0) {
 			// no space left in hash
@@ -134,19 +136,24 @@ void fdb_learn(
 		}
 
 		event_push(GR_EVENT_FDB_ADD, fdb);
-	} else {
-		fdb = data;
+		return;
 	}
 
-	fdb->last_seen = gr_clock_us();
+	fdb = data;
 
 	if ((fdb->flags & GR_FDB_F_LEARN)
-	    && (fdb->iface_id != iface_id || !l3_addr_eq(&fdb->vtep, vtep))) {
+	    && unlikely(fdb->iface_id != iface_id || !l3_addr_eq(&fdb->vtep, vtep))) {
 		// update in case the mac address has moved
 		fdb->iface_id = iface_id;
 		fdb->vtep = *vtep;
+		fdb->last_seen = now;
 		event_push(GR_EVENT_FDB_UPDATE, fdb);
+		return;
 	}
+
+	// update timestamp if at least 1/4 second since last update
+	if (unlikely(now >= fdb->last_seen + CLOCKS_PER_SEC / 4))
+		fdb->last_seen = now;
 }
 
 void fdb_purge_iface(uint16_t iface_id) {
diff --git a/modules/l2/control/l2.h b/modules/l2/control/l2.h
index 5cbb47cd..a58cda62 100644
--- a/modules/l2/control/l2.h
+++ b/modules/l2/control/l2.h
@@ -33,7 +33,8 @@ void fdb_learn(
 	uint16_t iface_id,
 	const struct rte_ether_addr *,
 	uint16_t vlan_id,
-	const struct l3_addr *vtep
+	const struct l3_addr *vtep,
+	const clock_t now
 );
 
 // Delete all FDB entries referencing the provided interface.
diff --git a/modules/l2/datapath/bridge_input.c b/modules/l2/datapath/bridge_input.c
index e9ea266f..8fb9ea79 100644
--- a/modules/l2/datapath/bridge_input.c
+++ b/modules/l2/datapath/bridge_input.c
@@ -7,6 +7,8 @@
 #include "mbuf.h"
 #include "rxtx.h"
 
+#include <gr_clock.h>
+
 #include <rte_ether.h>
 
 enum edges {
@@ -39,6 +41,8 @@ static uint16_t bridge_input_process(
 	struct rte_mbuf *m;
 	rte_edge_t edge;
 
+	const clock_t now = gr_clock_us();
+
 	for (uint16_t i = 0; i < nb_objs; i++) {
 		m = objs[i];
 		d = iface_mbuf_data(m);
@@ -63,7 +67,7 @@ static uint16_t bridge_input_process(
 			struct l3_addr vtep = {0};
 			if (d->iface->type == GR_IFACE_TYPE_VXLAN)
 				vtep = d->vtep;
-			fdb_learn(bridge->id, d->iface->id, &eth->src_addr, d->vlan_id, &vtep);
+			fdb_learn(bridge->id, d->iface->id, &eth->src_addr, d->vlan_id, &vtep, now);
 		}
 
 		if (rte_is_unicast_ether_addr(&eth->dst_addr)) {
-- 
2.43.0



More information about the grout mailing list