Blob Blame History Raw
From 0aeee46fadb21012c8841a315e02ad27ba5b247f Mon Sep 17 00:00:00 2001
From: Denis Kirjanov <denis.kirjanov@suse.com>
Date: Thu, 8 Sep 2022 14:41:35 +0300
Subject: [PATCH 6/6] net: dsa: microchip: fix bridging with more than two
 member ports
Git-commit: 3d00827a90db6f79abc7cdc553887f89a2e0a184
Patch-mainline: v5.17-rc6
References: git-fixes

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>
Signed-off-by: Denis Kirjanov <denis.kirjanov@suse.com>
---
 drivers/net/dsa/microchip/ksz_common.c | 42 +++++++++++++++++++++++++++++++---
 1 file changed, 39 insertions(+), 3 deletions(-)

diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c
index 8a04302018dc..824430cdb9ac 100644
--- a/drivers/net/dsa/microchip/ksz_common.c
+++ b/drivers/net/dsa/microchip/ksz_common.c
@@ -20,13 +20,29 @@
 
 #include "ksz_common.h"
 
+static inline struct net_device *
+ksz_dsa_port_bridge_dev_get(const struct dsa_port *dp)
+{
+       return dp->bridge_dev;
+}
+
+static inline bool ksz_dsa_port_bridge_same(const struct dsa_port *a,
+                                       const struct dsa_port *b)
+{
+       struct net_device *br_a = ksz_dsa_port_bridge_dev_get(a);
+       struct net_device *br_b = ksz_dsa_port_bridge_dev_get(b);
+
+       /* Standalone ports are not in the same bridge with one another */
+       return (!br_a || !br_b) ? false : (br_a == br_b);
+}
+
 void ksz_update_port_member(struct ksz_device *dev, int port)
 {
 	struct ksz_port *p = &dev->ports[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 +61,33 @@ void ksz_update_port_member(struct ksz_device *dev, int port)
 			continue;
 		if (!dp->bridge_dev || dp->bridge_dev != other_dp->bridge_dev)
 			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 (ksz_dsa_port_bridge_same(other_dp, third_dp))
+				val |= BIT(j);
+		}
+
 		dev->dev_ops->cfg_port_member(dev, i, val | cpu_port);
 	}
 
-- 
2.16.4