]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
dpaa2-switch: support VLAN flag changes on existing VIDs
authorIoana Ciornei <ioana.ciornei@nxp.com>
Thu, 28 May 2026 17:34:51 +0000 (20:34 +0300)
committerJakub Kicinski <kuba@kernel.org>
Wed, 3 Jun 2026 02:13:18 +0000 (19:13 -0700)
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 <ioana.ciornei@nxp.com>
Link: https://patch.msgid.link/20260528173452.1953102-5-ioana.ciornei@nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c

index 4022635171b53856323a8341ae488d9566258ea2..505ccaa93ee412fbc68729f1ec21da5e8b2f880f 100644 (file)
@@ -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 = &ethsw->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,
-                                 &ethsw->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,
+                                         &ethsw->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)