Thread (25 messages) 25 messages, 2 authors, 2026-06-09

Re: [PATCH net-next v3 02/12] dpaa2-switch: add support for LAG offload

From: Jakub Kicinski <kuba@kernel.org>
Date: 2026-06-06 02:10:49
Also in: lkml

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
dpaa2-switch: add support for LAG offload

This patch adds the bulk of the changes needed in order to support
offloading of an upper bond device.
quoted hunk ↗ jump to hunk
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index a0bf5b50aae5f..d082ecf9b1252 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -2210,19 +2300,275 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
+static int dpaa2_switch_port_bond_join(struct net_device *netdev,
+				       struct net_device *bond_dev,
+				       struct netdev_lag_upper_info *info,
+				       struct netlink_ext_ack *extack)
+{
[ ... ]
+	/* If the bond device is a switch port, join the bridge as well */
+	bridge_dev = netdev_master_upper_dev_get(bond_dev);
+	if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
+		return 0;
+
+	err = dpaa2_switch_port_bridge_join(netdev, bridge_dev, extack);
[Severity: High]
Should switchdev_bridge_port_offload() inside dpaa2_switch_port_bridge_join()
receive the actual bridge port (the bond device) rather than the lower
physical port?

For ports enslaved to a LAG, passing the lower physical port might cause
switchdev to immediately abort the offload with -EINVAL, since the netdev
is not directly enslaved to the bridge. I see this was addressed in a later
commit adding a dpaa2_switch_port_to_bridge_port() helper.

[Severity: High]
When a port joins a LAG that is already enslaved to a bridge, is there a risk
of dpaa2_switch_port_set_fdb() corrupting the in_use flag of active FDBs?

Since the port first acquires the bond's shared FDB, and then
dpaa2_switch_port_bridge_join() finds a port already using that same FDB,
the code in dpaa2_switch_port_set_fdb() seems to lack a self-assignment
check. This could mark the shared FDB as unused before reassigning it,
allowing another standalone port to reallocate it. I note this was fixed in
a later commit covering bond scenarios.

[Severity: High]
Will dpaa2_switch_port_set_fdb() skip bonds when iterating through lower
devices, breaking cross-LAG forwarding?

When a port joins a bridge, dpaa2_switch_port_set_fdb() uses a non-recursive
iterator (netdev_for_each_lower_dev). If the other lower device is a bond,
dpaa2_switch_port_dev_check() will return false, skipping nested ports and
failing to share the hardware FDB. I note this was also fixed in a later
commit.
+	if (err)
+		goto err_bridge_join;
+
+	return err;
+
+err_bridge_join:
+	dpaa2_switch_port_del_vlan(port_priv, DEFAULT_VLAN_ID);
+err_vlan_add:
+err_lag_cfg:
+	rcu_assign_pointer(port_priv->lag, NULL);
+	dpaa2_switch_set_lag_cfg(bond_dev, lag_id, ethsw);
+err_egress_flood:
+	if (port_priv->fdb != old_fdb) {
+		new_fdb = port_priv->fdb;
+
+		/* Explicitly move the port back into the old private FDB */
+		old_fdb->in_use = true;
+		old_fdb->bridge_dev = NULL;
+		port_priv->fdb = old_fdb;
+
+		/* Reprogram the bond FDB to no longer include this port */
+		dpaa2_switch_fdb_set_egress_flood(ethsw, new_fdb->fdb_id);
[Severity: High]
Are we permanently leaking a hardware FDB resource in this error path?

If dpaa2_switch_port_bridge_join() fails, it will have allocated a new
intermediate FDB and marked it in_use. The cleanup block here restores
the original old_fdb, but does not appear to set new_fdb->in_use back to
false. Repeated failures might exhaust the fixed number of FDBs.
+	} else {
+		/* Same FDB means that this was the first port joining the
+		 * bond. Keep using the same FDB.
+		 */
+		old_fdb->bridge_dev = NULL;
+	}
[ ... ]
+static int dpaa2_switch_port_bond_leave(struct net_device *netdev,
+					struct net_device *bond_dev)
+{
[ ... ]
+lag_cleanup:
+	/* Recreate the LAG configuration for the LAG group that we left. In
+	 * case any step failed, at least we free up a LAG resource.
+	 */
+	rcu_assign_pointer(port_priv->lag, NULL);
+	dpaa2_switch_set_lag_cfg(bond_dev, lag->id, ethsw);
[Severity: Critical]
Could this trigger a kernel panic if lag is NULL on the rollback path?

If dpaa2_switch_port_bond_join() fails and sets port_priv->lag to NULL, a
subsequent NETDEV_CHANGEUPPER rollback calls this function. The lag variable
is initialized via rtnl_dereference(port_priv->lag), which would be NULL,
leading to a dereference when accessing lag->id here.
quoted hunk ↗ jump to hunk
+
+	return err;
+}
+
 static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,
 					    struct netdev_notifier_changeupper_info *info)
 {
+	struct net_device *upper_dev, *br;
 	struct netlink_ext_ack *extack;
-	struct net_device *upper_dev;
 	int err;
 
 	if (!dpaa2_switch_port_dev_check(netdev))
@@ -2239,6 +2585,24 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,
 
 		if (!info->linking)
 			dpaa2_switch_port_pre_bridge_leave(netdev);
+	} else if (netif_is_lag_master(upper_dev)) {
+		if (!info->linking) {
+			if (netif_is_bridge_port(upper_dev))
+				dpaa2_switch_port_pre_bridge_leave(netdev);
+			return 0;
+		}
+
+		if (netif_is_bridge_port(upper_dev)) {
+			br = netdev_master_upper_dev_get(upper_dev);
+			err = dpaa2_switch_prechangeupper_sanity_checks(netdev,
+									br,
+									extack);
+			if (err)
+				return err;
+		}
+
+		return dpaa2_switch_pre_lag_join(netdev, upper_dev,
+						 info->upper_info, extack);
 	}
 
 	return 0;
[Severity: High]
Might this bypass topological checks and allow VLAN uppers on offloaded LAGs?

If a port joins a LAG first, and then a VLAN is added to the LAG, the
event propagates here with the VLAN device as upper_dev. Since it is
neither a bridge nor a LAG master, this function returns 0 without checking
vlan_uses_dev(), inadvertently allowing an unsupported hardware configuration.
quoted hunk ↗ jump to hunk
@@ -2263,6 +2627,80 @@ static int dpaa2_switch_port_changeupper(struct net_device *netdev,
 							     extack);
 		else
 			return dpaa2_switch_port_bridge_leave(netdev);
[Severity: High]
Does calling dpaa2_switch_port_bridge_leave() wrongly split LAG member ports
into isolated private FDBs?

When a LAG leaves a bridge, this is invoked for each member port. It will
unconditionally call dpaa2_switch_port_set_fdb(port_priv, NULL), which forces
the port to drop the shared FDB and isolates it from the other LAG members.
I note this was also fixed in a later commit covering bond scenarios.
+	} else if (netif_is_lag_master(upper_dev)) {
+		if (info->linking)
+			return dpaa2_switch_port_bond_join(netdev, upper_dev,
+							   info->upper_info,
+							   extack);
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help