net: bridge: set min MTU on port events and allow user to set max
authorNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Fri, 30 Mar 2018 10:46:18 +0000 (13:46 +0300)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Apr 2018 02:19:00 +0000 (22:19 -0400)
Recently the bridge was changed to automatically set maximum MTU on port
events (add/del/changemtu) when vlan filtering is enabled, but that
actually changes behaviour in a way which breaks some setups and can lead
to packet drops. In order to still allow that maximum to be set while being
compatible, we add the ability for the user to tune the bridge MTU up to
the maximum when vlan filtering is enabled, but that has to be done
explicitly and all port events (add/del/changemtu) lead to resetting that
MTU to the minimum as before.

Suggested-by: Roopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/bridge/br.c
net/bridge/br_device.c
net/bridge/br_if.c
net/bridge/br_private.h

index 26e1616b2c9052038c6c2810303adaf7d9c48423..565ff055813b30bc87daaed0815e0b7e47f2eb6f 100644 (file)
@@ -52,7 +52,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
 
        switch (event) {
        case NETDEV_CHANGEMTU:
-               dev_set_mtu(br->dev, br_mtu(br));
+               dev_set_mtu(br->dev, br_mtu(br, false));
                break;
 
        case NETDEV_CHANGEADDR:
index 278fc999d3550f278acdd336233d9cf8b8d47062..edb9967eb165c987e55bc822ad9a057fab0483b9 100644 (file)
@@ -224,7 +224,8 @@ static void br_get_stats64(struct net_device *dev,
 static int br_change_mtu(struct net_device *dev, int new_mtu)
 {
        struct net_bridge *br = netdev_priv(dev);
-       if (new_mtu > br_mtu(br))
+
+       if (new_mtu > br_mtu(br, br_vlan_enabled(dev)))
                return -EINVAL;
 
        dev->mtu = new_mtu;
index 87b2afd455c7f7fa2e720672741e0dd2d85fe244..7d5dc5a91084034c6f91de6722c29297caa043c8 100644 (file)
@@ -424,41 +424,26 @@ int br_del_bridge(struct net *net, const char *name)
        return ret;
 }
 
-static bool min_mtu(int a, int b)
-{
-       return a < b ? 1 : 0;
-}
-
-static bool max_mtu(int a, int b)
-{
-       return a > b ? 1 : 0;
-}
-
-/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
-static int __br_mtu(const struct net_bridge *br, bool (compare_fn)(int, int))
+/* MTU of the bridge pseudo-device: ETH_DATA_LEN if there are no ports, the
+ * minimum of the ports if @max is false or the maximum if it's true
+ */
+int br_mtu(const struct net_bridge *br, bool max)
 {
        const struct net_bridge_port *p;
-       int mtu = 0;
+       int ret_mtu = 0;
 
        ASSERT_RTNL();
 
-       if (list_empty(&br->port_list))
-               mtu = ETH_DATA_LEN;
-       else {
-               list_for_each_entry(p, &br->port_list, list) {
-                       if (!mtu || compare_fn(p->dev->mtu, mtu))
-                               mtu = p->dev->mtu;
+       list_for_each_entry(p, &br->port_list, list) {
+               if (!max) {
+                       if (!ret_mtu || ret_mtu > p->dev->mtu)
+                               ret_mtu = p->dev->mtu;
+               } else if (p->dev->mtu > ret_mtu) {
+                       ret_mtu = p->dev->mtu;
                }
        }
-       return mtu;
-}
 
-int br_mtu(const struct net_bridge *br)
-{
-       if (br_vlan_enabled(br->dev))
-               return __br_mtu(br, max_mtu);
-       else
-               return __br_mtu(br, min_mtu);
+       return ret_mtu ? ret_mtu : ETH_DATA_LEN;
 }
 
 static void br_set_gso_limits(struct net_bridge *br)
@@ -612,7 +597,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
        if (changed_addr)
                call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
 
-       dev_set_mtu(br->dev, br_mtu(br));
+       dev_set_mtu(br->dev, br_mtu(br, false));
        br_set_gso_limits(br);
 
        kobject_uevent(&p->kobj, KOBJ_ADD);
@@ -659,7 +644,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
         */
        del_nbp(p);
 
-       dev_set_mtu(br->dev, br_mtu(br));
+       dev_set_mtu(br->dev, br_mtu(br, false));
        br_set_gso_limits(br);
 
        spin_lock_bh(&br->lock);
index 048d5b51813bac1e295c65b43d598d997892e95c..586f84b9670d97402bf0a60efbb144224b83ce01 100644 (file)
@@ -578,7 +578,7 @@ int br_del_bridge(struct net *net, const char *name);
 int br_add_if(struct net_bridge *br, struct net_device *dev,
              struct netlink_ext_ack *extack);
 int br_del_if(struct net_bridge *br, struct net_device *dev);
-int br_mtu(const struct net_bridge *br);
+int br_mtu(const struct net_bridge *br, bool max);
 netdev_features_t br_features_recompute(struct net_bridge *br,
                                        netdev_features_t features);
 void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);