net: dsa: assign a bridge number even without TX forwarding offload
authorVladimir Oltean <vladimir.oltean@nxp.com>
Mon, 6 Dec 2021 16:57:48 +0000 (18:57 +0200)
committerJakub Kicinski <kuba@kernel.org>
Wed, 8 Dec 2021 22:31:14 +0000 (14:31 -0800)
The service where DSA assigns a unique bridge number for each forwarding
domain is useful even for drivers which do not implement the TX
forwarding offload feature.

For example, drivers might use the dp->bridge_num for FDB isolation.

So rename ds->num_fwd_offloading_bridges to ds->max_num_bridges, and
calculate a unique bridge_num for all drivers that set this value.

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>
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/sja1105/sja1105_main.c
include/net/dsa.h
net/dsa/dsa2.c
net/dsa/port.c

index de3401b2c86cb6046e0c1e088d9218d4f9e5f107..a818df35b239995d179a8676fb498e072a75f469 100644 (file)
@@ -3186,8 +3186,8 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
         * time.
         */
        if (mv88e6xxx_has_pvt(chip))
-               ds->num_fwd_offloading_bridges = MV88E6XXX_MAX_PVT_SWITCHES -
-                                                ds->dst->last_switch - 1;
+               ds->max_num_bridges = MV88E6XXX_MAX_PVT_SWITCHES -
+                                     ds->dst->last_switch - 1;
 
        mv88e6xxx_reg_lock(chip);
 
index c343effe2e967a84ce441fb5ce58b70bc2493f72..355b56cf94d819341f980aba3151e75053722ea1 100644 (file)
@@ -3139,7 +3139,7 @@ static int sja1105_setup(struct dsa_switch *ds)
        ds->vlan_filtering_is_global = true;
        ds->untag_bridge_pvid = true;
        /* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */
-       ds->num_fwd_offloading_bridges = 7;
+       ds->max_num_bridges = 7;
 
        /* Advertise the 8 egress queues */
        ds->num_tx_queues = SJA1105_NUM_TC;
index a23cfbaa09d6c23a6ca19ddb57f957de0059bed4..00fbd87ae4ff71a260ce27cf94a9767da62416c7 100644 (file)
@@ -413,12 +413,12 @@ struct dsa_switch {
         */
        unsigned int            num_lag_ids;
 
-       /* Drivers that support bridge forwarding offload should set this to
-        * the maximum number of bridges spanning the same switch tree (or all
-        * trees, in the case of cross-tree bridging support) that can be
-        * offloaded.
+       /* Drivers that support bridge forwarding offload or FDB isolation
+        * should set this to the maximum number of bridges spanning the same
+        * switch tree (or all trees, in the case of cross-tree bridging
+        * support) that can be offloaded.
         */
-       unsigned int            num_fwd_offloading_bridges;
+       unsigned int            max_num_bridges;
 
        size_t num_ports;
 };
index 9606e56710a54fa6aa31d2543283ad0b5164cb57..4901cdc264ee7b32f83975b0bb050cad3ff87fb0 100644 (file)
@@ -152,7 +152,9 @@ unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max)
        unsigned int bridge_num = dsa_bridge_num_find(bridge_dev);
 
        if (!bridge_num) {
-               /* First port that offloads TX forwarding for this bridge */
+               /* First port that requests FDB isolation or TX forwarding
+                * offload for this bridge
+                */
                bridge_num = find_next_zero_bit(&dsa_fwd_offloading_bridges,
                                                DSA_MAX_NUM_OFFLOADING_BRIDGES,
                                                1);
index 9a77bd1373e264e2724c961f76655ff70ec09b9e..199a56faf46045e38d04f4c050b26c26bd9a6669 100644 (file)
@@ -271,19 +271,15 @@ 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)
+                                            struct net_device *bridge_dev,
+                                            unsigned int bridge_num)
 {
-       unsigned int bridge_num = dp->bridge_num;
        struct dsa_switch *ds = dp->ds;
 
        /* No bridge TX forwarding offload => do nothing */
-       if (!ds->ops->port_bridge_tx_fwd_unoffload || !dp->bridge_num)
+       if (!ds->ops->port_bridge_tx_fwd_unoffload || !bridge_num)
                return;
 
-       dp->bridge_num = 0;
-
-       dsa_bridge_num_put(bridge_dev, bridge_num);
-
        /* Notify the chips only once the offload has been deactivated, so
         * that they can update their configuration accordingly.
         */
@@ -292,31 +288,60 @@ static void dsa_port_bridge_tx_fwd_unoffload(struct dsa_port *dp,
 }
 
 static bool dsa_port_bridge_tx_fwd_offload(struct dsa_port *dp,
-                                          struct net_device *bridge_dev)
+                                          struct net_device *bridge_dev,
+                                          unsigned int bridge_num)
 {
        struct dsa_switch *ds = dp->ds;
-       unsigned int bridge_num;
        int err;
 
-       if (!ds->ops->port_bridge_tx_fwd_offload)
-               return false;
-
-       bridge_num = dsa_bridge_num_get(bridge_dev,
-                                       ds->num_fwd_offloading_bridges);
-       if (!bridge_num)
+       /* FDB isolation is required for TX forwarding offload */
+       if (!ds->ops->port_bridge_tx_fwd_offload || !bridge_num)
                return false;
 
-       dp->bridge_num = bridge_num;
-
        /* Notify the driver */
        err = ds->ops->port_bridge_tx_fwd_offload(ds, dp->index, bridge_dev,
                                                  bridge_num);
-       if (err) {
-               dsa_port_bridge_tx_fwd_unoffload(dp, bridge_dev);
-               return false;
+
+       return err ? false : true;
+}
+
+static int dsa_port_bridge_create(struct dsa_port *dp,
+                                 struct net_device *br,
+                                 struct netlink_ext_ack *extack)
+{
+       struct dsa_switch *ds = dp->ds;
+       unsigned int bridge_num;
+
+       dp->bridge_dev = br;
+
+       if (!ds->max_num_bridges)
+               return 0;
+
+       bridge_num = dsa_bridge_num_get(br, ds->max_num_bridges);
+       if (!bridge_num) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Range of offloadable bridges exceeded");
+               return -EOPNOTSUPP;
        }
 
-       return true;
+       dp->bridge_num = bridge_num;
+
+       return 0;
+}
+
+static void dsa_port_bridge_destroy(struct dsa_port *dp,
+                                   const struct net_device *br)
+{
+       struct dsa_switch *ds = dp->ds;
+
+       dp->bridge_dev = NULL;
+
+       if (ds->max_num_bridges) {
+               int bridge_num = dp->bridge_num;
+
+               dp->bridge_num = 0;
+               dsa_bridge_num_put(br, bridge_num);
+       }
 }
 
 int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
@@ -336,7 +361,9 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
        /* Here the interface is already bridged. Reflect the current
         * configuration so that drivers can program their chips accordingly.
         */
-       dp->bridge_dev = br;
+       err = dsa_port_bridge_create(dp, br, extack);
+       if (err)
+               return err;
 
        brport_dev = dsa_port_to_bridge_port(dp);
 
@@ -344,7 +371,8 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br,
        if (err)
                goto out_rollback;
 
-       tx_fwd_offload = dsa_port_bridge_tx_fwd_offload(dp, br);
+       tx_fwd_offload = dsa_port_bridge_tx_fwd_offload(dp, br,
+                                                       dp->bridge_num);
 
        err = switchdev_bridge_port_offload(brport_dev, dev, dp,
                                            &dsa_slave_switchdev_notifier,
@@ -366,7 +394,7 @@ out_rollback_unoffload:
 out_rollback_unbridge:
        dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
 out_rollback:
-       dp->bridge_dev = NULL;
+       dsa_port_bridge_destroy(dp, br);
        return err;
 }
 
@@ -393,14 +421,15 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
                .port = dp->index,
                .br = br,
        };
+       int bridge_num = dp->bridge_num;
        int err;
 
        /* Here the port is already unbridged. Reflect the current configuration
         * so that drivers can program their chips accordingly.
         */
-       dp->bridge_dev = NULL;
+       dsa_port_bridge_destroy(dp, br);
 
-       dsa_port_bridge_tx_fwd_unoffload(dp, br);
+       dsa_port_bridge_tx_fwd_unoffload(dp, br, bridge_num);
 
        err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_LEAVE, &info);
        if (err)