realtek: handle changed flags in VLAN configuration
authorJan Hoffmann <jan@3e8.eu>
Sat, 6 May 2023 17:28:55 +0000 (19:28 +0200)
committerSander Vanheule <sander@svanheule.net>
Sun, 7 May 2023 17:07:34 +0000 (19:07 +0200)
The port_vlan_add method may be called while a port is already a member
of that VLAN, so it needs to be able to handle changed flags. Fix it to
properly handle when the PVID or UNTAGGED flag was previously set, but
now no longer is.

To reduce duplication, move PVID configuration to a separate function.

Signed-off-by: Jan Hoffmann <jan@3e8.eu>
target/linux/realtek/files-5.10/drivers/net/dsa/rtl83xx/dsa.c
target/linux/realtek/files-5.15/drivers/net/dsa/rtl83xx/dsa.c

index 2d603cd42cdfc13da71dd2c72d9b9a2b176c5396..e9fc6f15ed008e8c7eda797f64e1b8b27458fabd 100644 (file)
@@ -1450,6 +1450,20 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port,
        return 0;
 }
 
+static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
+                                 int port, int pvid)
+{
+       /* Set both inner and outer PVID of the port */
+       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
+       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
+       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
+                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
+       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
+                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
+
+       priv->ports[port].pvid = pvid;
+}
+
 static void rtl83xx_vlan_add(struct dsa_switch *ds, int port,
                            const struct switchdev_obj_port_vlan *vlan)
 {
@@ -1468,20 +1482,11 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port,
 
        mutex_lock(&priv->reg_mutex);
 
-       if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
-               for (v = vlan->vid_begin; v <= vlan->vid_end; v++) {
-                       if (!v)
-                               continue;
-                       /* Set both inner and outer PVID of the port */
-                       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, v);
-                       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, v);
-                       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
-                                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
-                       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
-                                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
-
-                       priv->ports[port].pvid = vlan->vid_end;
-               }
+       for (v = vlan->vid_begin; v <= vlan->vid_end; v++) {
+               if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+                       rtl83xx_vlan_set_pvid(priv, port, v);
+               else if (priv->ports[port].pvid == v)
+                       rtl83xx_vlan_set_pvid(priv, port, 0);
        }
 
        for (v = vlan->vid_begin; v <= vlan->vid_end; v++) {
@@ -1503,6 +1508,8 @@ static void rtl83xx_vlan_add(struct dsa_switch *ds, int port,
                info.tagged_ports |= BIT_ULL(port);
                if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                        info.untagged_ports |= BIT_ULL(port);
+               else
+                       info.untagged_ports &= ~BIT_ULL(port);
 
                priv->r->vlan_set_untagged(v, info.untagged_ports);
                pr_debug("Untagged ports, VLAN %d: %llx\n", v, info.untagged_ports);
@@ -1537,12 +1544,7 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
        for (v = vlan->vid_begin; v <= vlan->vid_end; v++) {
                /* Reset to default if removing the current PVID */
                if (v == pvid) {
-                       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, 0);
-                       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, 0);
-                       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
-                                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
-                       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
-                                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
+                       rtl83xx_vlan_set_pvid(priv, port, 0);
                }
                /* Get port memberships of this vlan */
                priv->r->vlan_tables_read(v, &info);
index 703306498e04dbbe9d0856995966d42d3fae3f84..b50330e1b97e5b8e6722b43857221ff5b8ceab05 100644 (file)
@@ -1434,6 +1434,20 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port,
        return 0;
 }
 
+static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
+                                 int port, int pvid)
+{
+       /* Set both inner and outer PVID of the port */
+       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
+       priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
+       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
+                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
+       priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
+                                       PBVLAN_MODE_UNTAG_AND_PRITAG);
+
+       priv->ports[port].pvid = pvid;
+}
+
 static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
                            const struct switchdev_obj_port_vlan *vlan,
                            struct netlink_ext_ack *extack)
@@ -1456,17 +1470,10 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
 
        mutex_lock(&priv->reg_mutex);
 
-       if (vlan->flags & BRIDGE_VLAN_INFO_PVID && vlan->vid) {
-               /* Set both inner and outer PVID of the port */
-               priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, vlan->vid);
-               priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, vlan->vid);
-               priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
-                                               PBVLAN_MODE_UNTAG_AND_PRITAG);
-               priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
-                                               PBVLAN_MODE_UNTAG_AND_PRITAG);
-
-               priv->ports[port].pvid = vlan->vid;
-       }
+       if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+               rtl83xx_vlan_set_pvid(priv, port, vlan->vid);
+       else if (priv->ports[port].pvid == vlan->vid)
+               rtl83xx_vlan_set_pvid(priv, port, 0);
 
        /* Get port memberships of this vlan */
        priv->r->vlan_tables_read(vlan->vid, &info);
@@ -1486,6 +1493,8 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
        info.tagged_ports |= BIT_ULL(port);
        if (vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED)
                info.untagged_ports |= BIT_ULL(port);
+       else
+               info.untagged_ports &= ~BIT_ULL(port);
 
        priv->r->vlan_set_untagged(vlan->vid, info.untagged_ports);
        pr_debug("Untagged ports, VLAN %d: %llx\n", vlan->vid, info.untagged_ports);
@@ -1518,12 +1527,7 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
 
        /* Reset to default if removing the current PVID */
        if (vlan->vid == pvid) {
-               priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, 0);
-               priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, 0);
-               priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
-                                               PBVLAN_MODE_UNTAG_AND_PRITAG);
-               priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
-                                               PBVLAN_MODE_UNTAG_AND_PRITAG);
+               rtl83xx_vlan_set_pvid(priv, port, 0);
        }
        /* Get port memberships of this vlan */
        priv->r->vlan_tables_read(vlan->vid, &info);