* [PATCH net 2/3] net: dsa: motorcomm: Rewrite FDB entry on port migration
2026-11-11 23:41 [PATCH net 0/3] net: dsa: motorcomm: FDB fixup David Yang
2026-11-11 23:41 ` [PATCH net 1/3] net: dsa: motorcomm: Do not inherit ports from dynamic FDB entries David Yang
@ 2026-11-11 23:41 ` David Yang
2026-11-11 23:41 ` [PATCH net 3/3] net: dsa: motorcomm: Use VID 0 as the unaware PVID David Yang
2 siblings, 0 replies; 4+ messages in thread
From: David Yang @ 2026-11-11 23:41 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
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@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH net 3/3] net: dsa: motorcomm: Use VID 0 as the unaware PVID
2026-11-11 23:41 [PATCH net 0/3] net: dsa: motorcomm: FDB fixup David Yang
2026-11-11 23:41 ` [PATCH net 1/3] net: dsa: motorcomm: Do not inherit ports from dynamic FDB entries David Yang
2026-11-11 23:41 ` [PATCH net 2/3] net: dsa: motorcomm: Rewrite FDB entry on port migration David Yang
@ 2026-11-11 23:41 ` David Yang
2 siblings, 0 replies; 4+ messages in thread
From: David Yang @ 2026-11-11 23:41 UTC (permalink / raw)
To: netdev
Cc: David Yang, Andrew Lunn, Vladimir Oltean, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel
The hardware classifies untagged and priority-tagged frames to the
port's PVID before the VLAN table lookup, and the FDB lookup key equals
the classified VID (measured on YT9215S).
The driver assigned PVID 4095 to the VLAN-unaware ports, while switchdev
reports the bridge's FDB entries with VID 0. These entries were then
installed under FID 0 and could never be hit by the hardware lookups.
Dynamic entries learnt under VID 4095, if any, took effect instead.
Lift the restriction on VLAN 0, and use it as the PVID of the
VLAN-unaware ports instead. The standalone default of 4095 stays, so
mislearnt entries, if any, would not pollute the bridge FDB.
Fixes: 186623f4aa72 ("net: dsa: yt921x: Add support for Motorcomm YT921x")
Signed-off-by: David Yang <mmyangfl@gmail.com>
---
drivers/net/dsa/motorcomm/chip.c | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c
index a2be819ccbed..376f16466cdd 100644
--- a/drivers/net/dsa/motorcomm/chip.c
+++ b/drivers/net/dsa/motorcomm/chip.c
@@ -79,7 +79,6 @@ static const struct yt921x_info yt921x_infos[] = {
{}
};
-#define YT921X_VID_UNWARE 4095
struct yt921x_reg_mdio {
struct mii_bus *bus;
@@ -962,7 +961,7 @@ yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering)
bdev = dsa_port_bridge_dev_get(dp);
if (!bdev || !vlan_filtering)
- pvid = YT921X_VID_UNWARE;
+ pvid = 0;
else
br_vlan_get_pvid(bdev, &pvid);
res = yt921x_port_set_pvid(priv, port, pvid);
@@ -1030,8 +1029,7 @@ yt921x_pvid_clear(struct yt921x_priv *priv, int port)
vlan_filtering = dsa_port_is_vlan_filtering(dp);
- res = yt921x_port_set_pvid(priv, port,
- vlan_filtering ? 0 : YT921X_VID_UNWARE);
+ res = yt921x_port_set_pvid(priv, port, 0);
if (res)
return res;
@@ -1188,7 +1186,7 @@ static int yt921x_userport_standalone(struct yt921x_priv *priv, int port)
/* Unrelated since learning is off and all packets are trapped;
* set it anyway
*/
- res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE);
+ res = yt921x_port_set_pvid(priv, port, 4095);
if (res)
return res;
@@ -2270,7 +2268,6 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
{
struct dsa_switch *ds = &priv->ds;
unsigned long cpu_ports_mask;
- u32 ctrls[2];
u32 ctrl;
int port;
int res;
@@ -2328,15 +2325,6 @@ static int yt921x_chip_setup_dsa(struct yt921x_priv *priv)
if (res)
return res;
- /* Tagged VID 0 should be treated as untagged, which confuses the
- * hardware a lot
- */
- ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M;
- ctrls[1] = 0;
- res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls);
- if (res)
- return res;
-
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread