bridge: Allow max MTU when multiple VLANs present
authorChas Williams <3chas3@gmail.com>
Thu, 22 Mar 2018 15:34:06 +0000 (11:34 -0400)
committerDavid S. Miller <davem@davemloft.net>
Fri, 23 Mar 2018 16:17:30 +0000 (12:17 -0400)
If the bridge is allowing multiple VLANs, some VLANs may have
different MTUs.  Instead of choosing the minimum MTU for the
bridge interface, choose the maximum MTU of the bridge members.
With this the user only needs to set a larger MTU on the member
ports that are participating in the large MTU VLANS.

Signed-off-by: Chas Williams <3chas3@gmail.com>
Reviewed-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Acked-by: Roopa Prabhu <roopa@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 7770481a65065cfd997660db99218b452fdb44b3..a3f95ab9d6a38c67e6c0b599f9009ae9a727bc1b 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_min_mtu(br));
+               dev_set_mtu(br->dev, br_mtu(br));
                break;
 
        case NETDEV_CHANGEADDR:
index 1285ca30ab0a0b3c6c7acc0fefca166a209d0a94..278fc999d3550f278acdd336233d9cf8b8d47062 100644 (file)
@@ -224,7 +224,7 @@ 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_min_mtu(br))
+       if (new_mtu > br_mtu(br))
                return -EINVAL;
 
        dev->mtu = new_mtu;
index 9ba4ed65c52ba83435b708db7d2643179336a8a5..48dc4d2e2be3af69db13b4662424c421af5982fc 100644 (file)
@@ -424,8 +424,18 @@ 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 */
-int br_min_mtu(const struct net_bridge *br)
+static int __br_mtu(const struct net_bridge *br, bool (compare_fn)(int, int))
 {
        const struct net_bridge_port *p;
        int mtu = 0;
@@ -436,13 +446,21 @@ int br_min_mtu(const struct net_bridge *br)
                mtu = ETH_DATA_LEN;
        else {
                list_for_each_entry(p, &br->port_list, list) {
-                       if (!mtu  || p->dev->mtu < mtu)
+                       if (!mtu || compare_fn(p->dev->mtu, mtu))
                                mtu = p->dev->mtu;
                }
        }
        return mtu;
 }
 
+int br_mtu(const struct net_bridge *br)
+{
+       if (br->vlan_enabled)
+               return __br_mtu(br, max_mtu);
+       else
+               return __br_mtu(br, min_mtu);
+}
+
 static void br_set_gso_limits(struct net_bridge *br)
 {
        unsigned int gso_max_size = GSO_MAX_SIZE;
@@ -594,7 +612,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_min_mtu(br));
+       dev_set_mtu(br->dev, br_mtu(br));
        br_set_gso_limits(br);
 
        kobject_uevent(&p->kobj, KOBJ_ADD);
@@ -641,7 +659,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
         */
        del_nbp(p);
 
-       dev_set_mtu(br->dev, br_min_mtu(br));
+       dev_set_mtu(br->dev, br_mtu(br));
        br_set_gso_limits(br);
 
        spin_lock_bh(&br->lock);
index 8e13a64d8c99e382f094480dda40c8eb8d43df8d..048d5b51813bac1e295c65b43d598d997892e95c 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_min_mtu(const struct net_bridge *br);
+int br_mtu(const struct net_bridge *br);
 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);