[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, ð->src_addr, d->vlan_id, &vtep);
+ fdb_learn(bridge->id, d->iface->id, ð->src_addr, d->vlan_id, &vtep, now);
}
if (rte_is_unicast_ether_addr(ð->dst_addr)) {
--
2.43.0
More information about the grout
mailing list