realtek: dsa: track the primary port of a LAG

The first member of a LAG becomes its primary port when lag_primary[]
holds -1, but the array comes zero-filled from devm_kzalloc() and is
never set to -1. Every member is therefore marked non-primary, and the
FDB and MDB code of the RTL83xx skips non-primary ports.

The leave path is wrong as well: it elects a new primary whenever any
member leaves, uses fls64(), which is one-based, as a port number, and
keeps the old primary when the last member leaves, so the next LAG with
that id starts without one.

Set every slot to -1 at probe, record the primary only after the
hardware join succeeded, and on leave elect a new primary only when the
primary itself leaves: the lowest remaining member, or -1 when the LAG
is empty.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Quinn O'Shea <qoshea@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/25792
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Quinn O'Shea authored and Markus Stockhausen committed 2026-10-11 08:54:06 +02:00
1 parent c39546671e
commit 8509328d1a
3 files changed
+14 -13

No files matched your search

@@ -439,6 +439,7 @@ static int rtl83xx_sw_probe(struct platform_device *pdev)
return -ENOMEM;
priv->r = r;
memset(priv->lag_primary, -1, sizeof(priv->lag_primary));
priv->l2_uc_map = devm_kcalloc(dev, r->fib_entries, sizeof(*priv->l2_uc_map),
GFP_KERNEL);
@@ -1422,19 +1422,19 @@ static int rtldsa_port_lag_join(struct dsa_switch *ds,
pr_info("port_lag_join: group %d, port %d\n", group, port);
err = otto_lag_add(priv->ds, group, port, info);
if (err) {
err = -EINVAL;
goto out;
}
if (priv->lag_primary[group] == -1)
priv->lag_primary[group] = port;
else
priv->lag_non_primary |= BIT_ULL(port);
priv->lagmembers |= BIT_ULL(port);
pr_debug("lag_members = %llX\n", priv->lagmembers);
err = otto_lag_add(priv->ds, group, port, info);
if (err) {
err = -EINVAL;
goto out;
}
out:
mutex_unlock(&priv->reg_mutex);
@@ -1466,12 +1466,12 @@ static int rtldsa_port_lag_leave(struct dsa_switch *ds, int port,
goto out;
}
/* To re-elect primary interface, just remove the first interface in
* this-group's interfaces from non-primary
*/
if (priv->lags_port_members[group]) {
priv->lag_primary[group] = fls64(priv->lags_port_members[group]);
priv->lag_non_primary &= ~BIT_ULL(priv->lag_primary[group]);
if (priv->lag_primary[group] == port) {
u64 members = priv->lags_port_members[group];
priv->lag_primary[group] = members ? __ffs64(members) : -1;
if (members)
priv->lag_non_primary &= ~BIT_ULL(priv->lag_primary[group]);
}
/* No need to update fdb entries since they make use of trunk_id for entry.
@@ -507,7 +507,7 @@ struct rtl838x_switch_priv {
/** @lag_primary: port of a LAG is primary (repesenting) and is added to
* the port matrix
*/
u32 lag_primary[MAX_LAGS];
int lag_primary[MAX_LAGS];
/**
* @lag_non_primary: Port (bit) is part of any LAG but not the