net: dsa: microchip: fix bridging with more than two member ports
authorSvenning Sørensen <sss@secomea.com>
Fri, 18 Feb 2022 11:27:01 +0000 (11:27 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sat, 19 Feb 2022 16:22:46 +0000 (16:22 +0000)
Commit b3612ccdf284 ("net: dsa: microchip: implement multi-bridge support")
plugged a packet leak between ports that were members of different bridges.
Unfortunately, this broke another use case, namely that of more than two
ports that are members of the same bridge.

After that commit, when a port is added to a bridge, hardware bridging
between other member ports of that bridge will be cleared, preventing
packet exchange between them.

Fix by ensuring that the Port VLAN Membership bitmap includes any existing
ports in the bridge, not just the port being added.

Fixes: b3612ccdf284 ("net: dsa: microchip: implement multi-bridge support")
Signed-off-by: Svenning Sørensen <sss@secomea.com>
Tested-by: Oleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/microchip/ksz_common.c

index 55dbda04ea6289d5a8d1db95403830140044452f..243f8ad6d06efc10b98d25933493a90a7711a3fb 100644 (file)
@@ -26,7 +26,7 @@ void ksz_update_port_member(struct ksz_device *dev, int port)
        struct dsa_switch *ds = dev->ds;
        u8 port_member = 0, cpu_port;
        const struct dsa_port *dp;
-       int i;
+       int i, j;
 
        if (!dsa_is_user_port(ds, port))
                return;
@@ -45,13 +45,33 @@ void ksz_update_port_member(struct ksz_device *dev, int port)
                        continue;
                if (!dsa_port_bridge_same(dp, other_dp))
                        continue;
+               if (other_p->stp_state != BR_STATE_FORWARDING)
+                       continue;
 
-               if (other_p->stp_state == BR_STATE_FORWARDING &&
-                   p->stp_state == BR_STATE_FORWARDING) {
+               if (p->stp_state == BR_STATE_FORWARDING) {
                        val |= BIT(port);
                        port_member |= BIT(i);
                }
 
+               /* Retain port [i]'s relationship to other ports than [port] */
+               for (j = 0; j < ds->num_ports; j++) {
+                       const struct dsa_port *third_dp;
+                       struct ksz_port *third_p;
+
+                       if (j == i)
+                               continue;
+                       if (j == port)
+                               continue;
+                       if (!dsa_is_user_port(ds, j))
+                               continue;
+                       third_p = &dev->ports[j];
+                       if (third_p->stp_state != BR_STATE_FORWARDING)
+                               continue;
+                       third_dp = dsa_to_port(ds, j);
+                       if (dsa_port_bridge_same(other_dp, third_dp))
+                               val |= BIT(j);
+               }
+
                dev->dev_ops->cfg_port_member(dev, i, val | cpu_port);
        }