realtek: dsa: Share port member configuration code
authorSven Eckelmann <[email protected]>
Thu, 9 Oct 2025 12:56:39 +0000 (14:56 +0200)
committerRobert Marko <[email protected]>
Sun, 12 Oct 2025 10:49:49 +0000 (12:49 +0200)
The leave and join callbacks for DSA were using their own implementation of
the port member handling code. This makes the implementation of additional
functionality based on the port member matrix complicated because it needs
to be implemented in both places and also in the new code path for the
introduced feature.

By sharing this code, it is much easier to guarantee that all code paths
behave the same. This approach is already implemented by other DSA drivers
like qca8k, mt7530 or ksz.

Signed-off-by: Sven Eckelmann <[email protected]>
Link: https://github.com/openwrt/openwrt/pull/20360
Signed-off-by: Robert Marko <[email protected]>
target/linux/realtek/files-6.12/drivers/net/dsa/rtl83xx/dsa.c

index 508119ff01318b690d8bc663d7796b53568fda5e..d97283065f6d66bb9fcc9dd85aca422e6eb028a9 100644 (file)
@@ -1528,15 +1528,56 @@ static int rtl83xx_set_ageing_time(struct dsa_switch *ds, unsigned int msec)
        return 0;
 }
 
+static void rtldsa_update_port_member(struct rtl838x_switch_priv *priv, int port,
+                                     const struct net_device *bridge_dev, bool join)
+                                     __must_hold(&priv->reg_mutex)
+{
+       struct dsa_port *dp = dsa_to_port(priv->ds, port);
+       struct rtl838x_port *p = &priv->ports[port];
+       struct dsa_port *cpu_dp = dp->cpu_dp;
+       u64 port_mask = BIT_ULL(cpu_dp->index);
+       struct rtl838x_port *other_p;
+       struct dsa_port *other_dp;
+       int other_port;
+
+       dsa_switch_for_each_user_port(other_dp, priv->ds) {
+               other_port = other_dp->index;
+               other_p = &priv->ports[other_port];
+
+               if (dp == other_dp)
+                       continue;
+
+               if (!dsa_port_offloads_bridge_dev(other_dp, bridge_dev))
+                       continue;
+
+               if (join && priv->is_lagmember[other_port])
+                       continue;
+
+               if (join) {
+                       port_mask |= BIT_ULL(other_port);
+                       other_p->pm |= BIT_ULL(port);
+               } else {
+                       other_p->pm &= ~BIT_ULL(port);
+               }
+
+               if (other_p->enable)
+                       priv->r->traffic_set(other_port, other_p->pm);
+       }
+
+       p->pm = port_mask;
+
+       if (p->enable)
+               priv->r->traffic_set(port, port_mask);
+}
+
 static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
                                    struct dsa_bridge bridge,
                                    bool *tx_fwd_offload,
                                    struct netlink_ext_ack *extack)
 {
        struct rtl838x_switch_priv *priv = ds->priv;
-       u64 port_bitmap = BIT_ULL(priv->cpu_port), v;
 
-       pr_debug("%s %x: %d %llx", __func__, (u32)priv, port, port_bitmap);
+       pr_debug("%s %x: %d", __func__, (u32)priv, port);
 
        if (priv->is_lagmember[port]) {
                pr_debug("%s: %d is lag slave. ignore\n", __func__, port);
@@ -1544,30 +1585,8 @@ static int rtl83xx_port_bridge_join(struct dsa_switch *ds, int port,
        }
 
        mutex_lock(&priv->reg_mutex);
-       for (int i = 0; i < ds->num_ports; i++) {
-               /* Add this port to the port matrix of the other ports in the
-                * same bridge. If the port is disabled, port matrix is kept
-                * and not being setup until the port becomes enabled.
-                */
-               if (dsa_is_user_port(ds, i) && !priv->is_lagmember[i] && i != port) {
-                       if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
-                               continue;
-                       if (priv->ports[i].enable)
-                               priv->r->traffic_enable(i, port);
-
-                       priv->ports[i].pm |= BIT_ULL(port);
-                       port_bitmap |= BIT_ULL(i);
-               }
-       }
 
-       /* Add all other ports to this port matrix. */
-       if (priv->ports[port].enable) {
-               priv->r->traffic_enable(priv->cpu_port, port);
-               v = priv->r->traffic_get(port);
-               v |= port_bitmap;
-               priv->r->traffic_set(port, v);
-       }
-       priv->ports[port].pm |= port_bitmap;
+       rtldsa_update_port_member(priv, port, bridge.dev, true);
 
        if (priv->r->set_static_move_action)
                priv->r->set_static_move_action(port, false);
@@ -1581,35 +1600,12 @@ static void rtl83xx_port_bridge_leave(struct dsa_switch *ds, int port,
                                      struct dsa_bridge bridge)
 {
        struct rtl838x_switch_priv *priv = ds->priv;
-       u64 port_bitmap = 0, v;
 
        pr_debug("%s %x: %d", __func__, (u32)priv, port);
+
        mutex_lock(&priv->reg_mutex);
-       for (int i = 0; i < ds->num_ports; i++) {
-               /* Remove this port from the port matrix of the other ports
-                * in the same bridge. If the port is disabled, port matrix
-                * is kept and not being setup until the port becomes enabled.
-                * And the other port's port matrix cannot be broken when the
-                * other port is still a VLAN-aware port.
-                */
-               if (dsa_is_user_port(ds, i) && i != port) {
-                       if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
-                               continue;
-                       if (priv->ports[i].enable)
-                               priv->r->traffic_disable(i, port);
-
-                       priv->ports[i].pm &= ~BIT_ULL(port);
-                       port_bitmap |= BIT_ULL(i);
-               }
-       }
 
-       /* Remove all other ports from this port matrix. */
-       if (priv->ports[port].enable) {
-               v = priv->r->traffic_get(port);
-               v &= ~port_bitmap;
-               priv->r->traffic_set(port, v);
-       }
-       priv->ports[port].pm &= ~port_bitmap;
+       rtldsa_update_port_member(priv, port, bridge.dev, false);
 
        if (priv->r->set_static_move_action)
                priv->r->set_static_move_action(port, true);