From: Ioana Ciornei Date: Thu, 28 May 2026 17:34:51 +0000 (+0300) Subject: dpaa2-switch: support VLAN flag changes on existing VIDs X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=9111f02861175854bcbd1d7ca4fff9d2b48bac62;p=thirdparty%2Fkernel%2Flinux.git dpaa2-switch: support VLAN flag changes on existing VIDs The switchdev core notifies the driver on VLAN flag changes on existing VIDs through the changed field of the switchdev_obj_port_vlan structure. Without this patch, the driver was erroring out from the start if the same VID was inserted twice, from its perspective, even though the second call was actually a flag change. $ bridge vlan add dev eth2 vid 30 untagged $ bridge vlan add dev eth2 vid 30 [ 458.589534] fsl_dpaa2_switch dpsw.0 eth2: VLAN 30 already configured This patch fixes the above behavior by, first of all, removing the checks and return of errors on a VLAN already being installed. The patch also moves the sequence of code which checks if there is space for a new VLAN so that the verification is being done only for VLANs not know by the switch and not flag changes. A new parameter is added to the dpaa2_switch_port_add_vlan() function so that we pass the vlan->changed necessary information. Based on this new parameter and the flags value, the untagged flag will be added or removed from a VLAN installed on a port. The same thing is also extended to the PVID configuration. Signed-off-by: Ioana Ciornei Link: https://patch.msgid.link/20260528173452.1953102-5-ioana.ciornei@nxp.com Signed-off-by: Jakub Kicinski --- diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 4022635171b5..505ccaa93ee4 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -298,49 +298,68 @@ set_tci_error: } static int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv, - u16 vid, u16 flags) + u16 vid, u16 flags, bool changed) { struct ethsw_core *ethsw = port_priv->ethsw_data; struct net_device *netdev = port_priv->netdev; struct dpsw_vlan_if_cfg vcfg = {0}; int err; - if (port_priv->vlans[vid]) { - netdev_err(netdev, "VLAN %d already configured\n", vid); - return -EEXIST; + if (!port_priv->vlans[vid]) { + /* If hit, this VLAN rule will lead the packet into the FDB + * table specified in the vlan configuration below + */ + vcfg.num_ifs = 1; + vcfg.if_id[0] = port_priv->idx; + vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); + vcfg.options |= DPSW_VLAN_ADD_IF_OPT_FDB_ID; + err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, + vid, &vcfg); + if (err) { + netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err); + return err; + } + + port_priv->vlans[vid] = ETHSW_VLAN_MEMBER; } - /* If hit, this VLAN rule will lead the packet into the FDB table - * specified in the vlan configuration below - */ + memset(&vcfg, 0, sizeof(vcfg)); vcfg.num_ifs = 1; vcfg.if_id[0] = port_priv->idx; - vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); - vcfg.options |= DPSW_VLAN_ADD_IF_OPT_FDB_ID; - err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, vid, &vcfg); - if (err) { - netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err); - return err; - } - - port_priv->vlans[vid] = ETHSW_VLAN_MEMBER; if (flags & BRIDGE_VLAN_INFO_UNTAGGED) { - err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0, - ethsw->dpsw_handle, - vid, &vcfg); + if (!(port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED)) { + err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0, + ethsw->dpsw_handle, + vid, &vcfg); + if (err) { + netdev_err(netdev, + "dpsw_vlan_add_if_untagged err %d\n", + err); + return err; + } + port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED; + } + } else if (changed && (port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED)) { + err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, + ethsw->dpsw_handle, + vid, &vcfg); if (err) { netdev_err(netdev, - "dpsw_vlan_add_if_untagged err %d\n", err); - return err; + "dpsw_vlan_remove_if_untagged err %d\n", + err); } - port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED; + port_priv->vlans[vid] &= ~ETHSW_VLAN_UNTAGGED; } if (flags & BRIDGE_VLAN_INFO_PVID) { err = dpaa2_switch_port_set_pvid(port_priv, vid); if (err) return err; + } else if (changed && port_priv->vlans[vid] & ETHSW_VLAN_PVID) { + err = dpaa2_switch_port_set_pvid(port_priv, 4095); + if (err) + return err; } return 0; @@ -970,6 +989,7 @@ static int dpaa2_switch_port_vlan_add(struct net_device *netdev, __be16 proto, .obj.orig_dev = netdev, /* This API only allows programming tagged, non-PVID VIDs */ .flags = 0, + .changed = false, }; return dpaa2_switch_port_vlans_add(netdev, &vlan); @@ -1803,25 +1823,19 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev, struct dpsw_attr *attr = ðsw->sw_attr; int err = 0; - /* Make sure that the VLAN is not already configured - * on the switch port - */ - if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER) { - netdev_err(netdev, "VLAN %d already configured\n", vlan->vid); - return -EEXIST; - } - - /* Check if there is space for a new VLAN */ - err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, - ðsw->sw_attr); - if (err) { - netdev_err(netdev, "dpsw_get_attributes err %d\n", err); - return err; - } - if (attr->max_vlans - attr->num_vlans < 1) - return -ENOSPC; - if (!port_priv->ethsw_data->vlans[vlan->vid]) { + /* Only check for space in case this is a new VLAN from the + * DPSW perspective + */ + err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, + ðsw->sw_attr); + if (err) { + netdev_err(netdev, "dpsw_get_attributes err %d\n", err); + return err; + } + if (attr->max_vlans - attr->num_vlans < 1) + return -ENOSPC; + /* this is a new VLAN */ err = dpaa2_switch_add_vlan(port_priv, vlan->vid); if (err) @@ -1830,7 +1844,8 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev, port_priv->ethsw_data->vlans[vlan->vid] |= ETHSW_VLAN_GLOBAL; } - return dpaa2_switch_port_add_vlan(port_priv, vlan->vid, vlan->flags); + return dpaa2_switch_port_add_vlan(port_priv, vlan->vid, vlan->flags, + vlan->changed); } static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc, @@ -2146,7 +2161,8 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev) * the dpaa2 switch interfaces are not capable to be VLAN unaware */ return dpaa2_switch_port_add_vlan(port_priv, DEFAULT_VLAN_ID, - BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID); + BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID, + false); } static int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *netdev)