net: bridge: move DSA master bridging restriction to DSA
authorVladimir Oltean <vladimir.oltean@nxp.com>
Fri, 19 Aug 2022 17:48:14 +0000 (20:48 +0300)
committerPaolo Abeni <pabeni@redhat.com>
Tue, 23 Aug 2022 09:39:22 +0000 (11:39 +0200)
When DSA gains support for multiple CPU ports in a LAG, it will become
mandatory to monitor the changeupper events for the DSA master.

In fact, there are already some restrictions to be imposed in that area,
namely that a DSA master cannot be a bridge port except in some special
circumstances.

Centralize the restrictions at the level of the DSA layer as a
preliminary step.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Acked-by: Nikolay Aleksandrov <razor@blackwall.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/bridge/br_if.c
net/dsa/slave.c

index a84a7cfb9d6dc856070dc2f24fa9ab0444dad6c7..efbd93e92ce287dd69280f099ea235fb1411148d 100644 (file)
@@ -568,26 +568,6 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
            !is_valid_ether_addr(dev->dev_addr))
                return -EINVAL;
 
-       /* Also don't allow bridging of net devices that are DSA masters, since
-        * the bridge layer rx_handler prevents the DSA fake ethertype handler
-        * to be invoked, so we don't get the chance to strip off and parse the
-        * DSA switch tag protocol header (the bridge layer just returns
-        * RX_HANDLER_CONSUMED, stopping RX processing for these frames).
-        * The only case where that would not be an issue is when bridging can
-        * already be offloaded, such as when the DSA master is itself a DSA
-        * or plain switchdev port, and is bridged only with other ports from
-        * the same hardware device.
-        */
-       if (netdev_uses_dsa(dev)) {
-               list_for_each_entry(p, &br->port_list, list) {
-                       if (!netdev_port_same_parent_id(dev, p->dev)) {
-                               NL_SET_ERR_MSG(extack,
-                                              "Cannot do software bridging with a DSA master");
-                               return -EINVAL;
-                       }
-               }
-       }
-
        /* No bridging of bridges */
        if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) {
                NL_SET_ERR_MSG(extack,
index 85cffd8443641c60f271c246d604f535f4ca9371..aa8bf89ec12759f4d30faa1efcc284adbe12f72f 100644 (file)
@@ -2699,6 +2699,46 @@ dsa_slave_prechangeupper_sanity_check(struct net_device *dev,
        return NOTIFY_DONE;
 }
 
+/* Don't allow bridging of DSA masters, since the bridge layer rx_handler
+ * prevents the DSA fake ethertype handler to be invoked, so we don't get the
+ * chance to strip off and parse the DSA switch tag protocol header (the bridge
+ * layer just returns RX_HANDLER_CONSUMED, stopping RX processing for these
+ * frames).
+ * The only case where that would not be an issue is when bridging can already
+ * be offloaded, such as when the DSA master is itself a DSA or plain switchdev
+ * port, and is bridged only with other ports from the same hardware device.
+ */
+static int
+dsa_bridge_prechangelower_sanity_check(struct net_device *new_lower,
+                                      struct netdev_notifier_changeupper_info *info)
+{
+       struct net_device *br = info->upper_dev;
+       struct netlink_ext_ack *extack;
+       struct net_device *lower;
+       struct list_head *iter;
+
+       if (!netif_is_bridge_master(br))
+               return NOTIFY_DONE;
+
+       if (!info->linking)
+               return NOTIFY_DONE;
+
+       extack = netdev_notifier_info_to_extack(&info->info);
+
+       netdev_for_each_lower_dev(br, lower, iter) {
+               if (!netdev_uses_dsa(new_lower) && !netdev_uses_dsa(lower))
+                       continue;
+
+               if (!netdev_port_same_parent_id(lower, new_lower)) {
+                       NL_SET_ERR_MSG(extack,
+                                      "Cannot do software bridging with a DSA master");
+                       return notifier_from_errno(-EINVAL);
+               }
+       }
+
+       return NOTIFY_DONE;
+}
+
 static int dsa_slave_netdevice_event(struct notifier_block *nb,
                                     unsigned long event, void *ptr)
 {
@@ -2713,6 +2753,10 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
                if (notifier_to_errno(err))
                        return err;
 
+               err = dsa_bridge_prechangelower_sanity_check(dev, info);
+               if (notifier_to_errno(err))
+                       return err;
+
                err = dsa_slave_prechangeupper(dev, ptr);
                if (notifier_to_errno(err))
                        return err;