[PATCH net 2/3] net: dsa: motorcomm: Rewrite FDB entry on port migration
From: David Yang
Date: Wed Nov 11 2026 - 18:49:51 EST
When unicast addresses move between ports, DSA calls .port_fdb_add()
without a prior .port_fdb_del() call, making the new port directly OR
into the existing entries. The old port mask then loses track in DSA and
can never be deleted.
Fuse FDB join/leave into one change function, and rewrite the whole
entry on add with the bridge port mask as the leave mask, so that ports
left behind by the migration are dropped from the entry.
Fixes: 186623f4aa72 ("net: dsa: yt921x: Add support for Motorcomm YT921x")
Signed-off-by: David Yang <mmyangfl@xxxxxxxxx>
---
drivers/net/dsa/motorcomm/chip.c | 96 ++++++++++++++++----------------
1 file changed, 49 insertions(+), 47 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index 150c51f1aa35..a2be819ccbed 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -729,13 +729,14 @@ yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
}
static int
-yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
- u16 vid, u16 ports_mask)
+yt921x_fdb_update(struct yt921x_priv *priv, const unsigned char *addr,
+ u16 vid, u16 leave_ports_mask, u16 join_ports_mask)
{
u16 index;
u32 ctrl1;
u32 ctrl2;
u32 ctrl;
+ u32 val1;
u32 val2;
u32 val;
int res;
@@ -744,69 +745,63 @@ yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
res = yt921x_fdb_has(priv, addr, vid, &index);
if (res)
return res;
- if (index >= YT921X_FDB_NUM)
- return 0;
+ if (index >= YT921X_FDB_NUM) {
+ if (!join_ports_mask)
+ return 0;
+
+ return yt921x_fdb_add(priv, addr, vid, join_ports_mask);
+ }
/* Check if action required */
res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
if (res)
return res;
- ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask);
- if (ctrl2 == val2)
- return 0;
- if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) {
- ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START;
- res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
- if (res)
- return res;
+ ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(leave_ports_mask);
+ if (!join_ports_mask) {
+ if (ctrl2 == val2)
+ return 0;
- return yt921x_fdb_wait(priv, &val);
+ if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) {
+ ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START;
+ res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl);
+ if (res)
+ return res;
+
+ return yt921x_fdb_wait(priv, &val);
+ }
}
- res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1);
+ res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1);
if (res)
return res;
+ ctrl1 = val1;
+ if (join_ports_mask) {
+ ctrl1 &= ~YT921X_FDB_IO1_STATUS_M;
+ ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC;
+ if (ctrl1 != val1)
+ ctrl2 &= ~YT921X_FDB_IO2_EGR_PORTS_M;
+ }
+ ctrl2 |= YT921X_FDB_IO2_EGR_PORTS(join_ports_mask);
+ if (ctrl1 == val1 && ctrl2 == val2)
+ return 0;
+
return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
}
+static int
+yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr,
+ u16 vid, u16 ports_mask)
+{
+ return yt921x_fdb_update(priv, addr, vid, ports_mask, 0);
+}
+
static int
yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid,
u16 ports_mask)
{
- u16 index;
- u32 ctrl1;
- u32 ctrl2;
- u32 val1;
- u32 val2;
- int res;
-
- /* Check for presence */
- res = yt921x_fdb_has(priv, addr, vid, &index);
- if (res)
- return res;
- if (index >= YT921X_FDB_NUM)
- return yt921x_fdb_add(priv, addr, vid, ports_mask);
-
- /* Check if action required */
- res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1);
- if (res)
- return res;
- res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2);
- if (res)
- return res;
-
- ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M;
- ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC;
- ctrl2 = val2;
- if (ctrl1 != val1)
- ctrl2 &= ~YT921X_FDB_IO2_EGR_PORTS_M;
- ctrl2 |= YT921X_FDB_IO2_EGR_PORTS(ports_mask);
- if (ctrl1 == val1 && ctrl2 == val2)
- return 0;
-
- return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2);
+ return yt921x_fdb_update(priv, addr, vid, 0, ports_mask);
}
static int
@@ -877,10 +872,17 @@ yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port,
const unsigned char *addr, u16 vid, struct dsa_db db)
{
struct yt921x_priv *priv = dsa_to_yt921x_priv(ds);
+ struct dsa_port *dp = dsa_to_port(ds, port);
+ u32 bridge_ports_mask = 0;
+ struct net_device *bdev;
int res;
+ bdev = dsa_port_bridge_dev_get(dp);
+ if (bdev)
+ bridge_ports_mask = dsa_bridge_ports(ds, bdev);
+
mutex_lock(&priv->reg_lock);
- res = yt921x_fdb_join(priv, addr, vid, BIT(port));
+ res = yt921x_fdb_update(priv, addr, vid, bridge_ports_mask, BIT(port));
mutex_unlock(&priv->reg_lock);
return res;
--
2.53.0