]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
generic: qca8k: backport bridge port isolation support
authorMatthias Schiffer <mschiffer@universe-factory.net>
Sat, 29 Mar 2025 20:31:49 +0000 (21:31 +0100)
committerChristian Marangi <ansuelsmth@gmail.com>
Mon, 7 Apr 2025 21:13:25 +0000 (23:13 +0200)
Bridge port isolation offload support has been added to the bridge core
and many DSA drivers. mt7530 support was backported in OpenWrt commit
c4e6a147a6c0 ("generic: 6.6: mt7530: add support for bridge port
isolation").

Backport qca8k support as well.

Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
Link: https://github.com/openwrt/openwrt/pull/18375
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
target/linux/generic/backport-6.6/793-01-v6.11-net-dsa-qca8k-do-not-write-port-mask-twice-in-bridge.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/793-02-v6.11-net-dsa-qca8k-factor-out-bridge-join-leave-logic.patch [new file with mode: 0644]
target/linux/generic/backport-6.6/793-03-v6.11-net-dsa-qca8k-add-support-for-bridge-port-isolation.patch [new file with mode: 0644]
target/linux/generic/pending-6.6/711-01-net-dsa-qca8k-implement-lag_fdb_add-del-ops.patch
target/linux/ipq40xx/patches-6.6/706-net-dsa-qca8k-add-IPQ4019-built-in-switch-support.patch

diff --git a/target/linux/generic/backport-6.6/793-01-v6.11-net-dsa-qca8k-do-not-write-port-mask-twice-in-bridge.patch b/target/linux/generic/backport-6.6/793-01-v6.11-net-dsa-qca8k-do-not-write-port-mask-twice-in-bridge.patch
new file mode 100644 (file)
index 0000000..22b544c
--- /dev/null
@@ -0,0 +1,58 @@
+From e85d3e6fea05c8ae21a40809a3c6b7adc97411c7 Mon Sep 17 00:00:00 2001
+Message-ID: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+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 <mschiffer@universe-factory.net>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ 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/793-02-v6.11-net-dsa-qca8k-factor-out-bridge-join-leave-logic.patch b/target/linux/generic/backport-6.6/793-02-v6.11-net-dsa-qca8k-factor-out-bridge-join-leave-logic.patch
new file mode 100644 (file)
index 0000000..fdb7e7b
--- /dev/null
@@ -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: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
+References: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+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 <mschiffer@universe-factory.net>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ 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/793-03-v6.11-net-dsa-qca8k-add-support-for-bridge-port-isolation.patch b/target/linux/generic/backport-6.6/793-03-v6.11-net-dsa-qca8k-add-support-for-bridge-port-isolation.patch
new file mode 100644 (file)
index 0000000..263fe10
--- /dev/null
@@ -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: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
+References: <e85d3e6fea05c8ae21a40809a3c6b7adc97411c7.1728674648.git.mschiffer@universe-factory.net>
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+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 <mschiffer@universe-factory.net>
+Reviewed-by: Wojciech Drewek <wojciech.drewek@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ 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;
index 3197aea0914b36d034229d293137c166e1eb3210..8d815dd2f2bc329522fa35fd715fbe29caf7ec46 100644 (file)
@@ -27,7 +27,7 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
        .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 <ansuelsmth@gmail.com>
        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);
index 9bd5ca515ee5d748e73a1b75538623d1da02ebe1..76539cea88c38fff4165b3c0e5f5c86ea131419c 100644 (file)
@@ -67,24 +67,15 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
                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,948 @@
@@ -1119,7 +1110,7 @@ Signed-off-by: Robert Marko <robert.marko@sartura.hr>
  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];