From 7173df9b203e328626df945d4a949a3070f65eba Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 11 Oct 2024 21:28:11 +0200 Subject: [PATCH] generic: qca8k: add bridge port isolation support Signed-off-by: Matthias Schiffer --- ...-not-write-port-mask-twice-in-bridge.patch | 58 +++++++ ...k-factor-out-bridge-join-leave-logic.patch | 153 ++++++++++++++++++ ...dd-support-for-bridge-port-isolation.patch | 91 +++++++++++ ...-qca8k-implement-lag_fdb_add-del-ops.patch | 4 +- ...-add-IPQ4019-built-in-switch-support.patch | 23 +-- 5 files changed, 311 insertions(+), 18 deletions(-) create mode 100644 target/linux/generic/backport-6.6/792-01-v6.11-net-dsa-qca8k-do-not-write-port-mask-twice-in-bridge.patch create mode 100644 target/linux/generic/backport-6.6/792-02-v6.11-net-dsa-qca8k-factor-out-bridge-join-leave-logic.patch create mode 100644 target/linux/generic/backport-6.6/792-03-v6.11-net-dsa-qca8k-add-support-for-bridge-port-isolation.patch diff --git a/target/linux/generic/backport-6.6/792-01-v6.11-net-dsa-qca8k-do-not-write-port-mask-twice-in-bridge.patch b/target/linux/generic/backport-6.6/792-01-v6.11-net-dsa-qca8k-do-not-write-port-mask-twice-in-bridge.patch new file mode 100644 index 0000000000..22b544c433 --- /dev/null +++ b/target/linux/generic/backport-6.6/792-01-v6.11-net-dsa-qca8k-do-not-write-port-mask-twice-in-bridge.patch @@ -0,0 +1,58 @@ +From e85d3e6fea05c8ae21a40809a3c6b7adc97411c7 Mon Sep 17 00:00:00 2001 +Message-ID: +From: Matthias Schiffer +Date: Thu, 20 Jun 2024 19:25:48 +0200 +Subject: [PATCH] net: dsa: qca8k: do not write port mask twice in bridge + join/leave + +qca8k_port_bridge_join() set QCA8K_PORT_LOOKUP_CTRL() for i == port twice, +once in the loop handling all other port's masks, and finally at the end +with the accumulated port_mask. + +The first time it would incorrectly set the port's own bit in the mask, +only to correct the mistake a moment later. qca8k_port_bridge_leave() had +the same issue, but here the regmap_clear_bits() was a no-op rather than +setting an unintended value. + +Remove the duplicate assignment by skipping the whole loop iteration for +i == port. The unintended bit setting doesn't seem to have any negative +effects (even when not reverted right away), so the change is submitted +as a simple cleanup rather than a fix. + +Signed-off-by: Matthias Schiffer +Reviewed-by: Wojciech Drewek +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-common.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -654,6 +654,8 @@ int qca8k_port_bridge_join(struct dsa_sw + port_mask = BIT(cpu_port); + + for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (i == port) ++ continue; + if (dsa_is_cpu_port(ds, i)) + continue; + if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) +@@ -666,8 +668,7 @@ int qca8k_port_bridge_join(struct dsa_sw + BIT(port)); + if (ret) + return ret; +- if (i != port) +- port_mask |= BIT(i); ++ port_mask |= BIT(i); + } + + /* Add all other ports to this ports portvlan mask */ +@@ -686,6 +687,8 @@ void qca8k_port_bridge_leave(struct dsa_ + cpu_port = dsa_to_port(ds, port)->cpu_dp->index; + + for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (i == port) ++ continue; + if (dsa_is_cpu_port(ds, i)) + continue; + if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) diff --git a/target/linux/generic/backport-6.6/792-02-v6.11-net-dsa-qca8k-factor-out-bridge-join-leave-logic.patch b/target/linux/generic/backport-6.6/792-02-v6.11-net-dsa-qca8k-factor-out-bridge-join-leave-logic.patch new file mode 100644 index 0000000000..fdb7e7b8b6 --- /dev/null +++ b/target/linux/generic/backport-6.6/792-02-v6.11-net-dsa-qca8k-factor-out-bridge-join-leave-logic.patch @@ -0,0 +1,153 @@ +From 412e1775f413c944b8c51bdadb675be957d83dc8 Mon Sep 17 00:00:00 2001 +Message-ID: <412e1775f413c944b8c51bdadb675be957d83dc8.1728674648.git.mschiffer@universe-factory.net> +In-Reply-To: +References: +From: Matthias Schiffer +Date: Thu, 20 Jun 2024 19:25:49 +0200 +Subject: [PATCH] net: dsa: qca8k: factor out bridge join/leave logic + +Most of the logic in qca8k_port_bridge_join() and qca8k_port_bridge_leave() +is the same. Refactor to reduce duplication and prepare for reusing the +code for implementing bridge port isolation. + +dsa_port_offloads_bridge_dev() is used instead of +dsa_port_offloads_bridge(), passing the bridge in as a struct netdevice *, +as we won't have a struct dsa_bridge in qca8k_port_bridge_flags(). + +The error handling is changed slightly in the bridge leave case, +returning early and emitting an error message when a regmap access fails. +This shouldn't matter in practice, as there isn't much we can do if +communication with the switch breaks down in the middle of reconfiguration. + +Signed-off-by: Matthias Schiffer +Reviewed-by: Wojciech Drewek +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-common.c | 101 ++++++++++++++--------------- + 1 file changed, 50 insertions(+), 51 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -615,6 +615,49 @@ void qca8k_port_stp_state_set(struct dsa + qca8k_port_configure_learning(ds, port, learning); + } + ++static int qca8k_update_port_member(struct qca8k_priv *priv, int port, ++ const struct net_device *bridge_dev, ++ bool join) ++{ ++ struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp; ++ u32 port_mask = BIT(dp->cpu_dp->index); ++ int i, ret; ++ ++ for (i = 0; i < QCA8K_NUM_PORTS; i++) { ++ if (i == port) ++ continue; ++ if (dsa_is_cpu_port(priv->ds, i)) ++ continue; ++ ++ other_dp = dsa_to_port(priv->ds, i); ++ if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev)) ++ continue; ++ ++ /* Add/remove this port to/from the portvlan mask of the other ++ * ports in the bridge ++ */ ++ if (join) { ++ port_mask |= BIT(i); ++ ret = regmap_set_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ } else { ++ ret = regmap_clear_bits(priv->regmap, ++ QCA8K_PORT_LOOKUP_CTRL(i), ++ BIT(port)); ++ } ++ ++ if (ret) ++ return ret; ++ } ++ ++ /* Add/remove all other ports to/from this port's portvlan mask */ ++ ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), ++ QCA8K_PORT_LOOKUP_MEMBER, port_mask); ++ ++ return ret; ++} ++ + int qca8k_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +@@ -647,65 +690,21 @@ int qca8k_port_bridge_join(struct dsa_sw + struct netlink_ext_ack *extack) + { + struct qca8k_priv *priv = ds->priv; +- int port_mask, cpu_port; +- int i, ret; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- port_mask = BIT(cpu_port); + +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (i == port) +- continue; +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) +- continue; +- /* Add this port to the portvlan mask of the other ports +- * in the bridge +- */ +- ret = regmap_set_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- if (ret) +- return ret; +- port_mask |= BIT(i); +- } +- +- /* Add all other ports to this ports portvlan mask */ +- ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, port_mask); +- +- return ret; ++ return qca8k_update_port_member(priv, port, bridge.dev, true); + } + + void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) + { + struct qca8k_priv *priv = ds->priv; +- int cpu_port, i; +- +- cpu_port = dsa_to_port(ds, port)->cpu_dp->index; +- +- for (i = 0; i < QCA8K_NUM_PORTS; i++) { +- if (i == port) +- continue; +- if (dsa_is_cpu_port(ds, i)) +- continue; +- if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) +- continue; +- /* Remove this port to the portvlan mask of the other ports +- * in the bridge +- */ +- regmap_clear_bits(priv->regmap, +- QCA8K_PORT_LOOKUP_CTRL(i), +- BIT(port)); +- } ++ int err; + +- /* Set the cpu port to be the only one in the portvlan mask of +- * this port +- */ +- qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), +- QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); ++ err = qca8k_update_port_member(priv, port, bridge.dev, false); ++ if (err) ++ dev_err(priv->dev, ++ "Failed to update switch config for bridge leave: %d\n", ++ err); + } + + void qca8k_port_fast_age(struct dsa_switch *ds, int port) diff --git a/target/linux/generic/backport-6.6/792-03-v6.11-net-dsa-qca8k-add-support-for-bridge-port-isolation.patch b/target/linux/generic/backport-6.6/792-03-v6.11-net-dsa-qca8k-add-support-for-bridge-port-isolation.patch new file mode 100644 index 0000000000..263fe10d4b --- /dev/null +++ b/target/linux/generic/backport-6.6/792-03-v6.11-net-dsa-qca8k-add-support-for-bridge-port-isolation.patch @@ -0,0 +1,91 @@ +From 422b64025ec10981c48f9367311846bf4bd38042 Mon Sep 17 00:00:00 2001 +Message-ID: <422b64025ec10981c48f9367311846bf4bd38042.1728674648.git.mschiffer@universe-factory.net> +In-Reply-To: +References: +From: Matthias Schiffer +Date: Thu, 20 Jun 2024 19:25:50 +0200 +Subject: [PATCH] net: dsa: qca8k: add support for bridge port isolation + +Remove a pair of ports from the port matrix when both ports have the +isolated flag set. + +Signed-off-by: Matthias Schiffer +Reviewed-by: Wojciech Drewek +Signed-off-by: David S. Miller +--- + drivers/net/dsa/qca/qca8k-common.c | 22 ++++++++++++++++++++-- + drivers/net/dsa/qca/qca8k.h | 1 + + 2 files changed, 21 insertions(+), 2 deletions(-) + +--- a/drivers/net/dsa/qca/qca8k-common.c ++++ b/drivers/net/dsa/qca/qca8k-common.c +@@ -619,6 +619,7 @@ static int qca8k_update_port_member(stru + const struct net_device *bridge_dev, + bool join) + { ++ bool isolated = !!(priv->port_isolated_map & BIT(port)), other_isolated; + struct dsa_port *dp = dsa_to_port(priv->ds, port), *other_dp; + u32 port_mask = BIT(dp->cpu_dp->index); + int i, ret; +@@ -633,10 +634,12 @@ static int qca8k_update_port_member(stru + if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev)) + continue; + ++ other_isolated = !!(priv->port_isolated_map & BIT(i)); ++ + /* Add/remove this port to/from the portvlan mask of the other + * ports in the bridge + */ +- if (join) { ++ if (join && !(isolated && other_isolated)) { + port_mask |= BIT(i); + ret = regmap_set_bits(priv->regmap, + QCA8K_PORT_LOOKUP_CTRL(i), +@@ -662,7 +665,7 @@ int qca8k_port_pre_bridge_flags(struct d + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) + { +- if (flags.mask & ~BR_LEARNING) ++ if (flags.mask & ~(BR_LEARNING | BR_ISOLATED)) + return -EINVAL; + + return 0; +@@ -672,6 +675,7 @@ int qca8k_port_bridge_flags(struct dsa_s + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) + { ++ struct qca8k_priv *priv = ds->priv; + int ret; + + if (flags.mask & BR_LEARNING) { +@@ -680,6 +684,20 @@ int qca8k_port_bridge_flags(struct dsa_s + if (ret) + return ret; + } ++ ++ if (flags.mask & BR_ISOLATED) { ++ struct dsa_port *dp = dsa_to_port(ds, port); ++ struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp); ++ ++ if (flags.val & BR_ISOLATED) ++ priv->port_isolated_map |= BIT(port); ++ else ++ priv->port_isolated_map &= ~BIT(port); ++ ++ ret = qca8k_update_port_member(priv, port, bridge_dev, true); ++ if (ret) ++ return ret; ++ } + + return 0; + } +--- a/drivers/net/dsa/qca/qca8k.h ++++ b/drivers/net/dsa/qca/qca8k.h +@@ -451,6 +451,7 @@ struct qca8k_priv { + * Bit 1: port enabled. Bit 0: port disabled. + */ + u8 port_enabled_map; ++ u8 port_isolated_map; + struct qca8k_ports_config ports_config; + struct regmap *regmap; + struct mii_bus *bus; diff --git a/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch b/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch index 3197aea091..8d815dd2f2 100644 --- a/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch +++ b/target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch @@ -27,7 +27,7 @@ Signed-off-by: Christian Marangi .port_mirror_add = qca8k_port_mirror_add, --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c -@@ -1215,6 +1215,42 @@ int qca8k_port_lag_leave(struct dsa_swit +@@ -1235,6 +1235,42 @@ int qca8k_port_lag_leave(struct dsa_swit return qca8k_lag_refresh_portmap(ds, port, lag, true); } @@ -72,7 +72,7 @@ Signed-off-by: Christian Marangi u32 val; --- a/drivers/net/dsa/qca/qca8k.h +++ b/drivers/net/dsa/qca/qca8k.h -@@ -590,5 +590,11 @@ int qca8k_port_lag_join(struct dsa_switc +@@ -591,5 +591,11 @@ int qca8k_port_lag_join(struct dsa_switc struct netlink_ext_ack *extack); int qca8k_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag); diff --git a/target/linux/ipq40xx/patches-6.6/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch b/target/linux/ipq40xx/patches-6.6/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch index 20dd345c69..4ecddd011d 100644 --- a/target/linux/ipq40xx/patches-6.6/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch +++ b/target/linux/ipq40xx/patches-6.6/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch @@ -67,24 +67,15 @@ Signed-off-by: Robert Marko mask = QCA8K_VTU_FUNC0_EG_MODE_PORT_NOT(i); if ((reg & mask) != mask) { -@@ -653,7 +653,7 @@ int qca8k_port_bridge_join(struct dsa_sw - cpu_port = dsa_to_port(ds, port)->cpu_dp->index; - port_mask = BIT(cpu_port); +@@ -624,7 +624,7 @@ static int qca8k_update_port_member(stru + u32 port_mask = BIT(dp->cpu_dp->index); + int i, ret; - for (i = 0; i < QCA8K_NUM_PORTS; i++) { -+ for (i = 0; i < ds->num_ports; i++) { - if (dsa_is_cpu_port(ds, i)) - continue; - if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) -@@ -685,7 +685,7 @@ void qca8k_port_bridge_leave(struct dsa_ - - cpu_port = dsa_to_port(ds, port)->cpu_dp->index; - -- for (i = 0; i < QCA8K_NUM_PORTS; i++) { -+ for (i = 0; i < ds->num_ports; i++) { - if (dsa_is_cpu_port(ds, i)) ++ for (i = 0; i < priv->ds->num_ports; i++) { + if (i == port) continue; - if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) + if (dsa_is_cpu_port(priv->ds, i)) --- /dev/null +++ b/drivers/net/dsa/qca/qca8k-ipq4019.c @@ -0,0 +1,946 @@ @@ -1117,7 +1108,7 @@ Signed-off-by: Robert Marko enum { QCA8K_PORT_SPEED_10M = 0, QCA8K_PORT_SPEED_100M = 1, -@@ -466,6 +518,10 @@ struct qca8k_priv { +@@ -467,6 +519,10 @@ struct qca8k_priv { struct qca8k_pcs pcs_port_6; const struct qca8k_match_data *info; struct qca8k_led ports_led[QCA8K_LED_COUNT]; -- 2.30.2