6.17-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Machon daniel.machon@microchip.com
[ Upstream commit c9d1b0b54258ba13b567dd116ead3c7c30cba7d8 ]
The sparx5 driver programs UC/MC/BC flooding in sparx5_update_fwd() by unconditionally applying bridge_fwd_mask to all flood PGIDs. Any bridge topology change that triggers sparx5_update_fwd() (for example enslaving another port) therefore reinstalls flooding in hardware for already bridged ports, regardless of their per-port flood flags.
This results in clobbering of the flood masks, and desynchronization between software and hardware: the bridge still reports “flood off” for the port, but hardware has flooding enabled due to unconditional PGID reprogramming.
Steps to reproduce:
$ ip link add br0 type bridge $ ip link set br0 up $ ip link set eth0 master br0 $ ip link set eth0 up $ bridge link set dev eth0 flood off $ ip link set eth1 master br0 $ ip link set eth1 up
At this point, flooding is silently re-enabled for eth0. Software still shows “flood off” for eth0, but hardware has flooding enabled.
To fix this, flooding is now set explicitly during bridge join/leave, through sparx5_port_attr_bridge_flags():
On bridge join, UC/MC/BC flooding is enabled by default.
On bridge leave, UC/MC/BC flooding is disabled.
sparx5_update_fwd() no longer touches the flood PGIDs, clobbering the flood masks, and desynchronizing software and hardware.
Initialization of the flooding PGIDs have been moved to sparx5_start(). This is required as flooding PGIDs defaults to 0x3fffffff in hardware and the initialization was previously handled in sparx5_update_fwd(), which was removed.
With this change, user-configured flooding flags persist across bridge updates and are no longer overridden by sparx5_update_fwd().
Fixes: d6fce5141929 ("net: sparx5: add switching support") Signed-off-by: Daniel Machon daniel.machon@microchip.com Reviewed-by: Simon Horman horms@kernel.org Link: https://patch.msgid.link/20251003-fix-flood-fwd-v1-1-48eb478b2904@microchip.... Signed-off-by: Paolo Abeni pabeni@redhat.com Signed-off-by: Sasha Levin sashal@kernel.org --- drivers/net/ethernet/microchip/sparx5/sparx5_main.c | 5 +++++ .../net/ethernet/microchip/sparx5/sparx5_switchdev.c | 12 ++++++++++++ drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c | 10 ---------- 3 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c index 74ad1d73b4652..40b1bfc600a79 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c @@ -708,6 +708,11 @@ static int sparx5_start(struct sparx5 *sparx5) /* Init masks */ sparx5_update_fwd(sparx5);
+ /* Init flood masks */ + for (int pgid = sparx5_get_pgid(sparx5, PGID_UC_FLOOD); + pgid <= sparx5_get_pgid(sparx5, PGID_BCAST); pgid++) + sparx5_pgid_clear(sparx5, pgid); + /* CPU copy CPU pgids */ spx5_wr(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1), sparx5, ANA_AC_PGID_MISC_CFG(sparx5_get_pgid(sparx5, PGID_CPU))); diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c index bc9ecb9392cd3..0a71abbd3da58 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c @@ -176,6 +176,7 @@ static int sparx5_port_bridge_join(struct sparx5_port *port, struct net_device *bridge, struct netlink_ext_ack *extack) { + struct switchdev_brport_flags flags = {0}; struct sparx5 *sparx5 = port->sparx5; struct net_device *ndev = port->ndev; int err; @@ -205,6 +206,11 @@ static int sparx5_port_bridge_join(struct sparx5_port *port, */ __dev_mc_unsync(ndev, sparx5_mc_unsync);
+ /* Enable uc/mc/bc flooding */ + flags.mask = BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; + flags.val = flags.mask; + sparx5_port_attr_bridge_flags(port, flags); + return 0;
err_switchdev_offload: @@ -215,6 +221,7 @@ static int sparx5_port_bridge_join(struct sparx5_port *port, static void sparx5_port_bridge_leave(struct sparx5_port *port, struct net_device *bridge) { + struct switchdev_brport_flags flags = {0}; struct sparx5 *sparx5 = port->sparx5;
switchdev_bridge_port_unoffload(port->ndev, NULL, NULL, NULL); @@ -234,6 +241,11 @@ static void sparx5_port_bridge_leave(struct sparx5_port *port,
/* Port enters in host more therefore restore mc list */ __dev_mc_sync(port->ndev, sparx5_mc_sync, sparx5_mc_unsync); + + /* Disable uc/mc/bc flooding */ + flags.mask = BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; + flags.val = 0; + sparx5_port_attr_bridge_flags(port, flags); }
static int sparx5_port_changeupper(struct net_device *dev, diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c index d42097aa60a0e..4947828719038 100644 --- a/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c +++ b/drivers/net/ethernet/microchip/sparx5/sparx5_vlan.c @@ -167,16 +167,6 @@ void sparx5_update_fwd(struct sparx5 *sparx5) /* Divide up fwd mask in 32 bit words */ bitmap_to_arr32(mask, sparx5->bridge_fwd_mask, SPX5_PORTS);
- /* Update flood masks */ - for (port = sparx5_get_pgid(sparx5, PGID_UC_FLOOD); - port <= sparx5_get_pgid(sparx5, PGID_BCAST); port++) { - spx5_wr(mask[0], sparx5, ANA_AC_PGID_CFG(port)); - if (is_sparx5(sparx5)) { - spx5_wr(mask[1], sparx5, ANA_AC_PGID_CFG1(port)); - spx5_wr(mask[2], sparx5, ANA_AC_PGID_CFG2(port)); - } - } - /* Update SRC masks */ for (port = 0; port < sparx5->data->consts->n_ports; port++) { if (test_bit(port, sparx5->bridge_fwd_mask)) {