OSDN Git Service

net: dsa: keep the bridge_dev and bridge_num as part of the same structure
authorVladimir Oltean <vladimir.oltean@nxp.com>
Mon, 6 Dec 2021 16:57:56 +0000 (18:57 +0200)
committerJakub Kicinski <kuba@kernel.org>
Wed, 8 Dec 2021 22:31:16 +0000 (14:31 -0800)
The main desire behind this is to provide coherent bridge information to
the fast path without locking.

For example, right now we set dp->bridge_dev and dp->bridge_num from
separate code paths, it is theoretically possible for a packet
transmission to read these two port properties consecutively and find a
bridge number which does not correspond with the bridge device.

Another desire is to start passing more complex bridge information to
dsa_switch_ops functions. For example, with FDB isolation, it is
expected that drivers will need to be passed the bridge which requested
an FDB/MDB entry to be offloaded, and along with that bridge_dev, the
associated bridge_num should be passed too, in case the driver might
want to implement an isolation scheme based on that number.

We already pass the {bridge_dev, bridge_num} pair to the TX forwarding
offload switch API, however we'd like to remove that and squash it into
the basic bridge join/leave API. So that means we need to pass this
pair to the bridge join/leave API.

During dsa_port_bridge_leave, first we unset dp->bridge_dev, then we
call the driver's .port_bridge_leave with what used to be our
dp->bridge_dev, but provided as an argument.

When bridge_dev and bridge_num get folded into a single structure, we
need to preserve this behavior in dsa_port_bridge_leave: we need a copy
of what used to be in dp->bridge.

Switch drivers check bridge membership by comparing dp->bridge_dev with
the provided bridge_dev, but now, if we provide the struct dsa_bridge as
a pointer, they cannot keep comparing dp->bridge to the provided
pointer, since this only points to an on-stack copy. To make this
obvious and prevent driver writers from forgetting and doing stupid
things, in this new API, the struct dsa_bridge is provided as a full
structure (not very large, contains an int and a pointer) instead of a
pointer. An explicit comparison function needs to be used to determine
bridge membership: dsa_port_offloads_bridge().

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
23 files changed:
drivers/net/dsa/b53/b53_common.c
drivers/net/dsa/b53/b53_priv.h
drivers/net/dsa/dsa_loop.c
drivers/net/dsa/hirschmann/hellcreek.c
drivers/net/dsa/lan9303-core.c
drivers/net/dsa/lantiq_gswip.c
drivers/net/dsa/microchip/ksz_common.c
drivers/net/dsa/microchip/ksz_common.h
drivers/net/dsa/mt7530.c
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/ocelot/felix.c
drivers/net/dsa/qca8k.c
drivers/net/dsa/rtl8366rb.c
drivers/net/dsa/sja1105/sja1105_main.c
drivers/net/dsa/xrs700x/xrs700x.c
include/linux/dsa/8021q.h
include/net/dsa.h
net/dsa/dsa2.c
net/dsa/dsa_priv.h
net/dsa/port.c
net/dsa/slave.c
net/dsa/switch.c
net/dsa/tag_8021q.c

index d5e78f5..4e41b1a 100644 (file)
@@ -1860,7 +1860,7 @@ int b53_mdb_del(struct dsa_switch *ds, int port,
 }
 EXPORT_SYMBOL(b53_mdb_del);
 
-int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br)
+int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge)
 {
        struct b53_device *dev = ds->priv;
        s8 cpu_port = dsa_to_port(ds, port)->cpu_dp->index;
@@ -1887,7 +1887,7 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br)
        b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan);
 
        b53_for_each_port(dev, i) {
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != br)
+               if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
                        continue;
 
                /* Add this local port to the remote port VLAN control
@@ -1911,7 +1911,7 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br)
 }
 EXPORT_SYMBOL(b53_br_join);
 
-void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br)
+void b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge)
 {
        struct b53_device *dev = ds->priv;
        struct b53_vlan *vl = &dev->vlans[0];
@@ -1923,7 +1923,7 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br)
 
        b53_for_each_port(dev, i) {
                /* Don't touch the remaining ports */
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != br)
+               if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
                        continue;
 
                b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), &reg);
index 579da74..ee17f8b 100644 (file)
@@ -324,8 +324,8 @@ void b53_get_strings(struct dsa_switch *ds, int port, u32 stringset,
 void b53_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data);
 int b53_get_sset_count(struct dsa_switch *ds, int port, int sset);
 void b53_get_ethtool_phy_stats(struct dsa_switch *ds, int port, uint64_t *data);
-int b53_br_join(struct dsa_switch *ds, int port, struct net_device *bridge);
-void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *bridge);
+int b53_br_join(struct dsa_switch *ds, int port, struct dsa_bridge bridge);
+void b53_br_leave(struct dsa_switch *ds, int port, struct dsa_bridge bridge);
 void b53_br_set_stp_state(struct dsa_switch *ds, int port, u8 state);
 void b53_br_fast_age(struct dsa_switch *ds, int port);
 int b53_br_flags_pre(struct dsa_switch *ds, int port,
index e638e3e..70db3a9 100644 (file)
@@ -167,19 +167,19 @@ static int dsa_loop_phy_write(struct dsa_switch *ds, int port,
 }
 
 static int dsa_loop_port_bridge_join(struct dsa_switch *ds, int port,
-                                    struct net_device *bridge)
+                                    struct dsa_bridge bridge)
 {
        dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n",
-               __func__, port, bridge->name);
+               __func__, port, bridge.dev->name);
 
        return 0;
 }
 
 static void dsa_loop_port_bridge_leave(struct dsa_switch *ds, int port,
-                                      struct net_device *bridge)
+                                      struct dsa_bridge bridge)
 {
        dev_dbg(ds->dev, "%s: port: %d, bridge: %s\n",
-               __func__, port, bridge->name);
+               __func__, port, bridge.dev->name);
 }
 
 static void dsa_loop_port_stp_state_set(struct dsa_switch *ds, int port,
index 86839b4..c8dc83c 100644 (file)
@@ -674,7 +674,7 @@ static int hellcreek_bridge_flags(struct dsa_switch *ds, int port,
 }
 
 static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
-                                     struct net_device *br)
+                                     struct dsa_bridge bridge)
 {
        struct hellcreek *hellcreek = ds->priv;
 
@@ -691,7 +691,7 @@ static int hellcreek_port_bridge_join(struct dsa_switch *ds, int port,
 }
 
 static void hellcreek_port_bridge_leave(struct dsa_switch *ds, int port,
-                                       struct net_device *br)
+                                       struct dsa_bridge bridge)
 {
        struct hellcreek *hellcreek = ds->priv;
 
index 1c2bdcd..29d9094 100644 (file)
@@ -1103,7 +1103,7 @@ static void lan9303_port_disable(struct dsa_switch *ds, int port)
 }
 
 static int lan9303_port_bridge_join(struct dsa_switch *ds, int port,
-                                   struct net_device *br)
+                                   struct dsa_bridge bridge)
 {
        struct lan9303 *chip = ds->priv;
 
@@ -1117,7 +1117,7 @@ static int lan9303_port_bridge_join(struct dsa_switch *ds, int port,
 }
 
 static void lan9303_port_bridge_leave(struct dsa_switch *ds, int port,
-                                     struct net_device *br)
+                                     struct dsa_bridge bridge)
 {
        struct lan9303 *chip = ds->priv;
 
index 6317d0a..1f59fef 100644 (file)
@@ -1146,16 +1146,17 @@ static int gswip_vlan_remove(struct gswip_priv *priv,
 }
 
 static int gswip_port_bridge_join(struct dsa_switch *ds, int port,
-                                 struct net_device *bridge)
+                                 struct dsa_bridge bridge)
 {
+       struct net_device *br = bridge.dev;
        struct gswip_priv *priv = ds->priv;
        int err;
 
        /* When the bridge uses VLAN filtering we have to configure VLAN
         * specific bridges. No bridge is configured here.
         */
-       if (!br_vlan_enabled(bridge)) {
-               err = gswip_vlan_add_unaware(priv, bridge, port);
+       if (!br_vlan_enabled(br)) {
+               err = gswip_vlan_add_unaware(priv, br, port);
                if (err)
                        return err;
                priv->port_vlan_filter &= ~BIT(port);
@@ -1166,8 +1167,9 @@ static int gswip_port_bridge_join(struct dsa_switch *ds, int port,
 }
 
 static void gswip_port_bridge_leave(struct dsa_switch *ds, int port,
-                                   struct net_device *bridge)
+                                   struct dsa_bridge bridge)
 {
+       struct net_device *br = bridge.dev;
        struct gswip_priv *priv = ds->priv;
 
        gswip_add_single_port_br(priv, port, true);
@@ -1175,8 +1177,8 @@ static void gswip_port_bridge_leave(struct dsa_switch *ds, int port,
        /* When the bridge uses VLAN filtering we have to configure VLAN
         * specific bridges. No bridge is configured here.
         */
-       if (!br_vlan_enabled(bridge))
-               gswip_vlan_remove(priv, bridge, port, 0, true, false);
+       if (!br_vlan_enabled(br))
+               gswip_vlan_remove(priv, br, port, 0, true, false);
 }
 
 static int gswip_port_vlan_prepare(struct dsa_switch *ds, int port,
index cebcb73..40d6e3f 100644 (file)
@@ -192,7 +192,7 @@ void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf)
 EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats);
 
 int ksz_port_bridge_join(struct dsa_switch *ds, int port,
-                        struct net_device *br)
+                        struct dsa_bridge bridge)
 {
        /* port_stp_state_set() will be called after to put the port in
         * appropriate state so there is no need to do anything.
@@ -203,7 +203,7 @@ int ksz_port_bridge_join(struct dsa_switch *ds, int port,
 EXPORT_SYMBOL_GPL(ksz_port_bridge_join);
 
 void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
-                          struct net_device *br)
+                          struct dsa_bridge bridge)
 {
        /* port_stp_state_set() will be called after to put the port in
         * forwarding state so there is no need to do anything.
index 54b456b..88e5a5d 100644 (file)
@@ -155,9 +155,9 @@ void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode,
 int ksz_sset_count(struct dsa_switch *ds, int port, int sset);
 void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf);
 int ksz_port_bridge_join(struct dsa_switch *ds, int port,
-                        struct net_device *br);
+                        struct dsa_bridge bridge);
 void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
-                          struct net_device *br);
+                          struct dsa_bridge bridge);
 void ksz_port_fast_age(struct dsa_switch *ds, int port);
 int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb,
                      void *data);
index 73c9f79..5b74c54 100644 (file)
@@ -1186,7 +1186,7 @@ mt7530_port_bridge_flags(struct dsa_switch *ds, int port,
 
 static int
 mt7530_port_bridge_join(struct dsa_switch *ds, int port,
-                       struct net_device *bridge)
+                       struct dsa_bridge bridge)
 {
        struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
        u32 port_bitmap = BIT(MT7530_CPU_PORT);
@@ -1204,7 +1204,7 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
                 * same bridge. If the port is disabled, port matrix is kept
                 * and not being setup until the port becomes enabled.
                 */
-               if (dsa_port_bridge_dev_get(other_dp) != bridge)
+               if (!dsa_port_offloads_bridge(other_dp, &bridge))
                        continue;
 
                if (priv->ports[other_port].enable)
@@ -1303,7 +1303,7 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port)
 
 static void
 mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
-                        struct net_device *bridge)
+                        struct dsa_bridge bridge)
 {
        struct dsa_port *dp = dsa_to_port(ds, port), *other_dp;
        struct mt7530_priv *priv = ds->priv;
@@ -1320,7 +1320,7 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port,
                 * in the same bridge. If the port is disabled, port matrix
                 * is kept and not being setup until the port becomes enabled.
                 */
-               if (dsa_port_bridge_dev_get(other_dp) != bridge)
+               if (!dsa_port_offloads_bridge(other_dp, &bridge))
                        continue;
 
                if (priv->ports[other_port].enable)
index 5afc7a1..aa5c5d4 100644 (file)
@@ -2410,7 +2410,7 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
 }
 
 static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
-                               struct net_device *br)
+                               struct dsa_bridge bridge)
 {
        struct dsa_switch *ds = chip->ds;
        struct dsa_switch_tree *dst = ds->dst;
@@ -2418,7 +2418,7 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
        int err;
 
        list_for_each_entry(dp, &dst->ports, list) {
-               if (dsa_port_bridge_dev_get(dp) == br) {
+               if (dsa_port_offloads_bridge(dp, &bridge)) {
                        if (dp->ds == ds) {
                                /* This is a local bridge group member,
                                 * remap its Port VLAN Map.
@@ -2442,14 +2442,14 @@ static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
 }
 
 static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
-                                     struct net_device *br)
+                                     struct dsa_bridge bridge)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
 
        mv88e6xxx_reg_lock(chip);
 
-       err = mv88e6xxx_bridge_map(chip, br);
+       err = mv88e6xxx_bridge_map(chip, bridge);
        if (err)
                goto unlock;
 
@@ -2464,14 +2464,14 @@ unlock:
 }
 
 static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
-                                       struct net_device *br)
+                                       struct dsa_bridge bridge)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
 
        mv88e6xxx_reg_lock(chip);
 
-       if (mv88e6xxx_bridge_map(chip, br) ||
+       if (mv88e6xxx_bridge_map(chip, bridge) ||
            mv88e6xxx_port_vlan_map(chip, port))
                dev_err(ds->dev, "failed to remap in-chip Port VLAN\n");
 
@@ -2486,7 +2486,7 @@ static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
 
 static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,
                                           int tree_index, int sw_index,
-                                          int port, struct net_device *br)
+                                          int port, struct dsa_bridge bridge)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
        int err;
@@ -2503,7 +2503,7 @@ static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds,
 
 static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds,
                                             int tree_index, int sw_index,
-                                            int port, struct net_device *br)
+                                            int port, struct dsa_bridge bridge)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
 
@@ -2535,19 +2535,17 @@ static int mv88e6xxx_map_virtual_bridge_to_pvt(struct dsa_switch *ds,
 }
 
 static int mv88e6xxx_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
-                                          struct net_device *br,
-                                          unsigned int bridge_num)
+                                          struct dsa_bridge bridge)
 {
-       return mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge_num);
+       return mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num);
 }
 
 static void mv88e6xxx_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,
-                                             struct net_device *br,
-                                             unsigned int bridge_num)
+                                             struct dsa_bridge bridge)
 {
        int err;
 
-       err = mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge_num);
+       err = mv88e6xxx_map_virtual_bridge_to_pvt(ds, bridge.num);
        if (err) {
                dev_err(ds->dev, "failed to remap cross-chip Port VLAN: %pe\n",
                        ERR_PTR(err));
index 57beab3..b7e5a5c 100644 (file)
@@ -706,21 +706,21 @@ static int felix_bridge_flags(struct dsa_switch *ds, int port,
 }
 
 static int felix_bridge_join(struct dsa_switch *ds, int port,
-                            struct net_device *br)
+                            struct dsa_bridge bridge)
 {
        struct ocelot *ocelot = ds->priv;
 
-       ocelot_port_bridge_join(ocelot, port, br);
+       ocelot_port_bridge_join(ocelot, port, bridge.dev);
 
        return 0;
 }
 
 static void felix_bridge_leave(struct dsa_switch *ds, int port,
-                              struct net_device *br)
+                              struct dsa_bridge bridge)
 {
        struct ocelot *ocelot = ds->priv;
 
-       ocelot_port_bridge_leave(ocelot, port, br);
+       ocelot_port_bridge_leave(ocelot, port, bridge.dev);
 }
 
 static int felix_lag_join(struct dsa_switch *ds, int port,
index 7053a35..dc983f7 100644 (file)
@@ -1810,8 +1810,8 @@ qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
                  QCA8K_PORT_LOOKUP_STATE_MASK, stp_state);
 }
 
-static int
-qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
+static int qca8k_port_bridge_join(struct dsa_switch *ds, int port,
+                                 struct dsa_bridge bridge)
 {
        struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
        int port_mask, cpu_port;
@@ -1823,7 +1823,7 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
        for (i = 0; i < QCA8K_NUM_PORTS; i++) {
                if (dsa_is_cpu_port(ds, i))
                        continue;
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != br)
+               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
@@ -1844,8 +1844,8 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br)
        return ret;
 }
 
-static void
-qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
+static void qca8k_port_bridge_leave(struct dsa_switch *ds, int port,
+                                   struct dsa_bridge bridge)
 {
        struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv;
        int cpu_port, i;
@@ -1855,7 +1855,7 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br)
        for (i = 0; i < QCA8K_NUM_PORTS; i++) {
                if (dsa_is_cpu_port(ds, i))
                        continue;
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != br)
+               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
index b6f277a..fac2333 100644 (file)
@@ -1186,7 +1186,7 @@ rtl8366rb_port_disable(struct dsa_switch *ds, int port)
 
 static int
 rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,
-                          struct net_device *bridge)
+                          struct dsa_bridge bridge)
 {
        struct realtek_smi *smi = ds->priv;
        unsigned int port_bitmap = 0;
@@ -1198,7 +1198,7 @@ rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,
                if (i == port)
                        continue;
                /* Not on this bridge */
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != bridge)
+               if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
                        continue;
                /* Join this port to each other port on the bridge */
                ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i),
@@ -1218,7 +1218,7 @@ rtl8366rb_port_bridge_join(struct dsa_switch *ds, int port,
 
 static void
 rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port,
-                           struct net_device *bridge)
+                           struct dsa_bridge bridge)
 {
        struct realtek_smi *smi = ds->priv;
        unsigned int port_bitmap = 0;
@@ -1230,7 +1230,7 @@ rtl8366rb_port_bridge_leave(struct dsa_switch *ds, int port,
                if (i == port)
                        continue;
                /* Not on this bridge */
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != bridge)
+               if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
                        continue;
                /* Remove this port from any other port on the bridge */
                ret = regmap_update_bits(smi->map, RTL8366RB_PORT_ISO(i),
index 5e03eda..24584fe 100644 (file)
@@ -1980,7 +1980,7 @@ static int sja1105_manage_flood_domains(struct sja1105_private *priv)
 }
 
 static int sja1105_bridge_member(struct dsa_switch *ds, int port,
-                                struct net_device *br, bool member)
+                                struct dsa_bridge bridge, bool member)
 {
        struct sja1105_l2_forwarding_entry *l2_fwd;
        struct sja1105_private *priv = ds->priv;
@@ -2005,7 +2005,7 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port,
                 */
                if (i == port)
                        continue;
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != br)
+               if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
                        continue;
                sja1105_port_allow_traffic(l2_fwd, i, port, member);
                sja1105_port_allow_traffic(l2_fwd, port, i, member);
@@ -2074,15 +2074,15 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,
 }
 
 static int sja1105_bridge_join(struct dsa_switch *ds, int port,
-                              struct net_device *br)
+                              struct dsa_bridge bridge)
 {
-       return sja1105_bridge_member(ds, port, br, true);
+       return sja1105_bridge_member(ds, port, bridge, true);
 }
 
 static void sja1105_bridge_leave(struct dsa_switch *ds, int port,
-                                struct net_device *br)
+                                struct dsa_bridge bridge)
 {
-       sja1105_bridge_member(ds, port, br, false);
+       sja1105_bridge_member(ds, port, bridge, false);
 }
 
 #define BYTES_PER_KBIT (1000LL / 8)
index 7c2b6c3..ebb55df 100644 (file)
@@ -501,7 +501,7 @@ static void xrs700x_mac_link_up(struct dsa_switch *ds, int port,
 }
 
 static int xrs700x_bridge_common(struct dsa_switch *ds, int port,
-                                struct net_device *bridge, bool join)
+                                struct dsa_bridge bridge, bool join)
 {
        unsigned int i, cpu_mask = 0, mask = 0;
        struct xrs700x *priv = ds->priv;
@@ -513,14 +513,14 @@ static int xrs700x_bridge_common(struct dsa_switch *ds, int port,
 
                cpu_mask |= BIT(i);
 
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) == bridge)
+               if (dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
                        continue;
 
                mask |= BIT(i);
        }
 
        for (i = 0; i < ds->num_ports; i++) {
-               if (dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != bridge)
+               if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge))
                        continue;
 
                /* 1 = Disable forwarding to the port */
@@ -540,13 +540,13 @@ static int xrs700x_bridge_common(struct dsa_switch *ds, int port,
 }
 
 static int xrs700x_bridge_join(struct dsa_switch *ds, int port,
-                              struct net_device *bridge)
+                              struct dsa_bridge bridge)
 {
        return xrs700x_bridge_common(ds, port, bridge, true);
 }
 
 static void xrs700x_bridge_leave(struct dsa_switch *ds, int port,
-                                struct net_device *bridge)
+                                struct dsa_bridge bridge)
 {
        xrs700x_bridge_common(ds, port, bridge, false);
 }
index 0af4371..939a1be 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/refcount.h>
 #include <linux/types.h>
+#include <net/dsa.h>
 
 struct dsa_switch;
 struct dsa_port;
@@ -37,12 +38,10 @@ struct sk_buff *dsa_8021q_xmit(struct sk_buff *skb, struct net_device *netdev,
 void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id);
 
 int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
-                                       struct net_device *br,
-                                       unsigned int bridge_num);
+                                       struct dsa_bridge bridge);
 
 void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,
-                                          struct net_device *br,
-                                          unsigned int bridge_num);
+                                          struct dsa_bridge bridge);
 
 u16 dsa_8021q_bridge_tx_fwd_offload_vid(unsigned int bridge_num);
 
index 899e13d..b9789c0 100644 (file)
@@ -219,6 +219,11 @@ struct dsa_mall_tc_entry {
        };
 };
 
+struct dsa_bridge {
+       struct net_device *dev;
+       unsigned int num;
+       refcount_t refcount;
+};
 
 struct dsa_port {
        /* A CPU port is physically connected to a master device.
@@ -256,8 +261,7 @@ struct dsa_port {
        /* Managed by DSA on user ports and by drivers on CPU and DSA ports */
        bool                    learning;
        u8                      stp_state;
-       struct net_device       *bridge_dev;
-       unsigned int            bridge_num;
+       struct dsa_bridge       *bridge;
        struct devlink_port     devlink_port;
        bool                    devlink_port_setup;
        struct phylink          *pl;
@@ -588,7 +592,7 @@ static inline bool dsa_port_is_vlan_filtering(const struct dsa_port *dp)
 static inline
 struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp)
 {
-       if (!dp->bridge_dev)
+       if (!dp->bridge)
                return NULL;
 
        if (dp->lag_dev)
@@ -602,12 +606,12 @@ struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp)
 static inline struct net_device *
 dsa_port_bridge_dev_get(const struct dsa_port *dp)
 {
-       return dp->bridge_dev;
+       return dp->bridge ? dp->bridge->dev : NULL;
 }
 
 static inline unsigned int dsa_port_bridge_num_get(struct dsa_port *dp)
 {
-       return dp->bridge_num;
+       return dp->bridge ? dp->bridge->num : 0;
 }
 
 static inline bool dsa_port_bridge_same(const struct dsa_port *a,
@@ -636,6 +640,12 @@ dsa_port_offloads_bridge_dev(struct dsa_port *dp,
        return dsa_port_bridge_dev_get(dp) == bridge_dev;
 }
 
+static inline bool dsa_port_offloads_bridge(struct dsa_port *dp,
+                                           const struct dsa_bridge *bridge)
+{
+       return dsa_port_bridge_dev_get(dp) == bridge->dev;
+}
+
 /* Returns true if any port of this tree offloads the given net_device */
 static inline bool dsa_tree_offloads_bridge_port(struct dsa_switch_tree *dst,
                                                 const struct net_device *dev)
@@ -812,17 +822,15 @@ struct dsa_switch_ops {
         */
        int     (*set_ageing_time)(struct dsa_switch *ds, unsigned int msecs);
        int     (*port_bridge_join)(struct dsa_switch *ds, int port,
-                                   struct net_device *bridge);
+                                   struct dsa_bridge bridge);
        void    (*port_bridge_leave)(struct dsa_switch *ds, int port,
-                                    struct net_device *bridge);
+                                    struct dsa_bridge bridge);
        /* Called right after .port_bridge_join() */
        int     (*port_bridge_tx_fwd_offload)(struct dsa_switch *ds, int port,
-                                             struct net_device *bridge,
-                                             unsigned int bridge_num);
+                                             struct dsa_bridge bridge);
        /* Called right before .port_bridge_leave() */
        void    (*port_bridge_tx_fwd_unoffload)(struct dsa_switch *ds, int port,
-                                               struct net_device *bridge,
-                                               unsigned int bridge_num);
+                                               struct dsa_bridge bridge);
        void    (*port_stp_state_set)(struct dsa_switch *ds, int port,
                                      u8 state);
        void    (*port_fast_age)(struct dsa_switch *ds, int port);
@@ -894,10 +902,10 @@ struct dsa_switch_ops {
         */
        int     (*crosschip_bridge_join)(struct dsa_switch *ds, int tree_index,
                                         int sw_index, int port,
-                                        struct net_device *br);
+                                        struct dsa_bridge bridge);
        void    (*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index,
                                          int sw_index, int port,
-                                         struct net_device *br);
+                                         struct dsa_bridge bridge);
        int     (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index,
                                        int port);
        int     (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index,
index 4901cdc..8814fa0 100644 (file)
@@ -129,20 +129,29 @@ void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag)
        }
 }
 
+struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst,
+                                       const struct net_device *br)
+{
+       struct dsa_port *dp;
+
+       list_for_each_entry(dp, &dst->ports, list)
+               if (dsa_port_bridge_dev_get(dp) == br)
+                       return dp->bridge;
+
+       return NULL;
+}
+
 static int dsa_bridge_num_find(const struct net_device *bridge_dev)
 {
        struct dsa_switch_tree *dst;
-       struct dsa_port *dp;
 
-       /* When preparing the offload for a port, it will have a valid
-        * dp->bridge_dev pointer but a not yet valid dp->bridge_num.
-        * However there might be other ports having the same dp->bridge_dev
-        * and a valid dp->bridge_num, so just ignore this port.
-        */
-       list_for_each_entry(dst, &dsa_tree_list, list)
-               list_for_each_entry(dp, &dst->ports, list)
-                       if (dp->bridge_dev == bridge_dev && dp->bridge_num)
-                               return dp->bridge_num;
+       list_for_each_entry(dst, &dsa_tree_list, list) {
+               struct dsa_bridge *bridge;
+
+               bridge = dsa_tree_bridge_find(dst, bridge_dev);
+               if (bridge)
+                       return bridge->num;
+       }
 
        return 0;
 }
@@ -151,6 +160,12 @@ unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max)
 {
        unsigned int bridge_num = dsa_bridge_num_find(bridge_dev);
 
+       /* Switches without FDB isolation support don't get unique
+        * bridge numbering
+        */
+       if (!max)
+               return 0;
+
        if (!bridge_num) {
                /* First port that requests FDB isolation or TX forwarding
                 * offload for this bridge
@@ -170,11 +185,11 @@ unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max)
 void dsa_bridge_num_put(const struct net_device *bridge_dev,
                        unsigned int bridge_num)
 {
-       /* Check if the bridge is still in use, otherwise it is time
-        * to clean it up so we can reuse this bridge_num later.
+       /* Since we refcount bridges, we know that when we call this function
+        * it is no longer in use, so we can just go ahead and remove it from
+        * the bit mask.
         */
-       if (!dsa_bridge_num_find(bridge_dev))
-               clear_bit(bridge_num, &dsa_fwd_offloading_bridges);
+       clear_bit(bridge_num, &dsa_fwd_offloading_bridges);
 }
 
 struct dsa_switch *dsa_switch_find(int tree_index, int sw_index)
index b4f9df4..da6ff99 100644 (file)
@@ -52,7 +52,7 @@ struct dsa_notifier_ageing_time_info {
 
 /* DSA_NOTIFIER_BRIDGE_* */
 struct dsa_notifier_bridge_info {
-       struct net_device *br;
+       struct dsa_bridge bridge;
        int tree_index;
        int sw_index;
        int port;
@@ -374,7 +374,7 @@ dsa_find_designated_bridge_port_by_vid(struct net_device *master, u16 vid)
                if (dp->type != DSA_PORT_TYPE_USER)
                        continue;
 
-               if (!dp->bridge_dev)
+               if (!dp->bridge)
                        continue;
 
                if (dp->stp_state != BR_STATE_LEARNING &&
@@ -403,7 +403,7 @@ dsa_find_designated_bridge_port_by_vid(struct net_device *master, u16 vid)
 /* If the ingress port offloads the bridge, we mark the frame as autonomously
  * forwarded by hardware, so the software bridge doesn't forward in twice, back
  * to us, because we already did. However, if we're in fallback mode and we do
- * software bridging, we are not offloading it, therefore the dp->bridge_dev
+ * software bridging, we are not offloading it, therefore the dp->bridge
  * pointer is not populated, and flooding needs to be done by software (we are
  * effectively operating in standalone ports mode).
  */
@@ -411,7 +411,7 @@ static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb)
 {
        struct dsa_port *dp = dsa_slave_to_port(skb->dev);
 
-       skb->offload_fwd_mark = !!(dp->bridge_dev);
+       skb->offload_fwd_mark = !!(dp->bridge);
 }
 
 /* Helper for removing DSA header tags from packets in the RX path.
@@ -508,6 +508,8 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
 unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max);
 void dsa_bridge_num_put(const struct net_device *bridge_dev,
                        unsigned int bridge_num);
+struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst,
+                                       const struct net_device *br);
 
 /* tag_8021q.c */
 int dsa_tag_8021q_bridge_join(struct dsa_switch *ds,
index f6ea41c..fbf2d7f 100644 (file)
@@ -130,7 +130,7 @@ int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
                        return err;
        }
 
-       if (!dp->bridge_dev)
+       if (!dp->bridge)
                dsa_port_set_state_now(dp, BR_STATE_FORWARDING, false);
 
        if (dp->pl)
@@ -158,7 +158,7 @@ void dsa_port_disable_rt(struct dsa_port *dp)
        if (dp->pl)
                phylink_stop(dp->pl);
 
-       if (!dp->bridge_dev)
+       if (!dp->bridge)
                dsa_port_set_state_now(dp, BR_STATE_DISABLED, false);
 
        if (ds->ops->port_disable)
@@ -271,36 +271,32 @@ static void dsa_port_switchdev_unsync_attrs(struct dsa_port *dp)
 }
 
 static void dsa_port_bridge_tx_fwd_unoffload(struct dsa_port *dp,
-                                            struct net_device *bridge_dev,
-                                            unsigned int bridge_num)
+                                            struct dsa_bridge bridge)
 {
        struct dsa_switch *ds = dp->ds;
 
        /* No bridge TX forwarding offload => do nothing */
-       if (!ds->ops->port_bridge_tx_fwd_unoffload || !bridge_num)
+       if (!ds->ops->port_bridge_tx_fwd_unoffload || !bridge.num)
                return;
 
        /* Notify the chips only once the offload has been deactivated, so
         * that they can update their configuration accordingly.
         */
-       ds->ops->port_bridge_tx_fwd_unoffload(ds, dp->index, bridge_dev,
-                                             bridge_num);
+       ds->ops->port_bridge_tx_fwd_unoffload(ds, dp->index, bridge);
 }
 
 static bool dsa_port_bridge_tx_fwd_offload(struct dsa_port *dp,
-                                          struct net_device *bridge_dev,
-                                          unsigned int bridge_num)
+                                          struct dsa_bridge bridge)
 {
        struct dsa_switch *ds = dp->ds;
        int err;
 
        /* FDB isolation is required for TX forwarding offload */
-       if (!ds->ops->port_bridge_tx_fwd_offload || !bridge_num)
+       if (!ds->ops->port_bridge_tx_fwd_offload || !bridge.num)
                return false;
 
        /* Notify the driver */
-       err = ds->ops->port_bridge_tx_fwd_offload(ds, dp->index, bridge_dev,
-                                                 bridge_num);
+       err = ds->ops->port_bridge_tx_fwd_offload(ds, dp->index, bridge);
 
        return err ? false : true;
 }
@@ -310,21 +306,32 @@ static int dsa_port_bridge_create(struct dsa_port *dp,
                                  struct netlink_ext_ack *extack)
 {
        struct dsa_switch *ds = dp->ds;
-       unsigned int bridge_num;
+       struct dsa_bridge *bridge;
 
-       dp->bridge_dev = br;
-
-       if (!ds->max_num_bridges)
+       bridge = dsa_tree_bridge_find(ds->dst, br);
+       if (bridge) {
+               refcount_inc(&bridge->refcount);
+               dp->bridge = bridge;
                return 0;
+       }
+
+       bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+       if (!bridge)
+               return -ENOMEM;
+
+       refcount_set(&bridge->refcount, 1);
+
+       bridge->dev = br;
 
-       bridge_num = dsa_bridge_num_get(br, ds->max_num_bridges);
-       if (!bridge_num) {
+       bridge->num = dsa_bridge_num_get(br, ds->max_num_bridges);
+       if (ds->max_num_bridges && !bridge->num) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "Range of offloadable bridges exceeded");
+               kfree(bridge);
                return -EOPNOTSUPP;
        }
 
-       dp->bridge_num = bridge_num;
+       dp->bridge = bridge;
 
        return 0;
 }
@@ -332,16 +339,17 @@ static int dsa_port_bridge_create(struct dsa_port *dp,
 static void dsa_port_bridge_destroy(struct dsa_port *dp,
                                    const struct net_device *br)
 {
-       struct dsa_switch *ds = dp->ds;
+       struct dsa_bridge *bridge = dp->bridge;
+
+       dp->bridge = NULL;
 
-       dp->bridge_dev = NULL;
+       if (!refcount_dec_and_test(&bridge->refcount))
+               return;
 
-       if (ds->max_num_bridges) {
-               int bridge_num = dp->bridge_num;
+       if (bridge->num)
+               dsa_bridge_num_put(br, bridge->num);
 
-               dp->bridge_num = 0;
-               dsa_bridge_num_put(br, bridge_num);
-       }
+       kfree(bridge);
 }
 
 int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
@@ -351,7 +359,6 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
                .tree_index = dp->ds->dst->index,
                .sw_index = dp->ds->index,
                .port = dp->index,
-               .br = br,
        };
        struct net_device *dev = dp->slave;
        struct net_device *brport_dev;
@@ -367,12 +374,12 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
 
        brport_dev = dsa_port_to_bridge_port(dp);
 
+       info.bridge = *dp->bridge;
        err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info);
        if (err)
                goto out_rollback;
 
-       tx_fwd_offload = dsa_port_bridge_tx_fwd_offload(dp, br,
-                                                       dsa_port_bridge_num_get(dp));
+       tx_fwd_offload = dsa_port_bridge_tx_fwd_offload(dp, info.bridge);
 
        err = switchdev_bridge_port_offload(brport_dev, dev, dp,
                                            &dsa_slave_switchdev_notifier,
@@ -415,12 +422,11 @@ void dsa_port_pre_bridge_leave(struct dsa_port *dp, struct net_device *br)
 
 void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
 {
-       unsigned int bridge_num = dsa_port_bridge_num_get(dp);
        struct dsa_notifier_bridge_info info = {
                .tree_index = dp->ds->dst->index,
                .sw_index = dp->ds->index,
                .port = dp->index,
-               .br = br,
+               .bridge = *dp->bridge,
        };
        int err;
 
@@ -429,7 +435,7 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
         */
        dsa_port_bridge_destroy(dp, br);
 
-       dsa_port_bridge_tx_fwd_unoffload(dp, br, bridge_num);
+       dsa_port_bridge_tx_fwd_unoffload(dp, info.bridge);
 
        err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
        if (err)
index 4a4c31c..88f7b86 100644 (file)
@@ -1564,7 +1564,7 @@ static void dsa_bridge_mtu_normalization(struct dsa_port *dp)
        if (!dp->ds->mtu_enforcement_ingress)
                return;
 
-       if (!dp->bridge_dev)
+       if (!dp->bridge)
                return;
 
        INIT_LIST_HEAD(&hw_port_list);
index 7993192..cd0630d 100644 (file)
@@ -95,7 +95,7 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds,
                if (!ds->ops->port_bridge_join)
                        return -EOPNOTSUPP;
 
-               err = ds->ops->port_bridge_join(ds, info->port, info->br);
+               err = ds->ops->port_bridge_join(ds, info->port, info->bridge);
                if (err)
                        return err;
        }
@@ -104,7 +104,7 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds,
            ds->ops->crosschip_bridge_join) {
                err = ds->ops->crosschip_bridge_join(ds, info->tree_index,
                                                     info->sw_index,
-                                                    info->port, info->br);
+                                                    info->port, info->bridge);
                if (err)
                        return err;
        }
@@ -124,19 +124,20 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds,
 
        if (dst->index == info->tree_index && ds->index == info->sw_index &&
            ds->ops->port_bridge_leave)
-               ds->ops->port_bridge_leave(ds, info->port, info->br);
+               ds->ops->port_bridge_leave(ds, info->port, info->bridge);
 
        if ((dst->index != info->tree_index || ds->index != info->sw_index) &&
            ds->ops->crosschip_bridge_leave)
                ds->ops->crosschip_bridge_leave(ds, info->tree_index,
                                                info->sw_index, info->port,
-                                               info->br);
+                                               info->bridge);
 
-       if (ds->needs_standalone_vlan_filtering && !br_vlan_enabled(info->br)) {
+       if (ds->needs_standalone_vlan_filtering &&
+           !br_vlan_enabled(info->bridge.dev)) {
                change_vlan_filtering = true;
                vlan_filtering = true;
        } else if (!ds->needs_standalone_vlan_filtering &&
-                  br_vlan_enabled(info->br)) {
+                  br_vlan_enabled(info->bridge.dev)) {
                change_vlan_filtering = true;
                vlan_filtering = false;
        }
index e9d5e56..27712a8 100644 (file)
@@ -337,7 +337,7 @@ dsa_port_tag_8021q_bridge_match(struct dsa_port *dp,
                return false;
 
        if (dsa_port_is_user(dp))
-               return dsa_port_bridge_dev_get(dp) == info->br;
+               return dsa_port_offloads_bridge(dp, &info->bridge);
 
        return false;
 }
@@ -410,10 +410,9 @@ int dsa_tag_8021q_bridge_leave(struct dsa_switch *ds,
 }
 
 int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
-                                       struct net_device *br,
-                                       unsigned int bridge_num)
+                                       struct dsa_bridge bridge)
 {
-       u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge_num);
+       u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
 
        return dsa_port_tag_8021q_vlan_add(dsa_to_port(ds, port), tx_vid,
                                           true);
@@ -421,10 +420,9 @@ int dsa_tag_8021q_bridge_tx_fwd_offload(struct dsa_switch *ds, int port,
 EXPORT_SYMBOL_GPL(dsa_tag_8021q_bridge_tx_fwd_offload);
 
 void dsa_tag_8021q_bridge_tx_fwd_unoffload(struct dsa_switch *ds, int port,
-                                          struct net_device *br,
-                                          unsigned int bridge_num)
+                                          struct dsa_bridge bridge)
 {
-       u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge_num);
+       u16 tx_vid = dsa_8021q_bridge_tx_fwd_offload_vid(bridge.num);
 
        dsa_port_tag_8021q_vlan_del(dsa_to_port(ds, port), tx_vid, true);
 }