Commit 419d14af authored by Chas Williams's avatar Chas Williams Committed by David S. Miller

bridge: Allow max MTU when multiple VLANs present

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: default avatarChas Williams <3chas3@gmail.com>
Reviewed-by: default avatarNikolay Aleksandrov <nikolay@cumulusnetworks.com>
Acked-by: default avatarRoopa Prabhu <roopa@cumulusnetworks.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent bda7fab5
...@@ -52,7 +52,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v ...@@ -52,7 +52,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
switch (event) { switch (event) {
case NETDEV_CHANGEMTU: case NETDEV_CHANGEMTU:
dev_set_mtu(br->dev, br_min_mtu(br)); dev_set_mtu(br->dev, br_mtu(br));
break; break;
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
......
...@@ -224,7 +224,7 @@ static void br_get_stats64(struct net_device *dev, ...@@ -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) static int br_change_mtu(struct net_device *dev, int new_mtu)
{ {
struct net_bridge *br = netdev_priv(dev); struct net_bridge *br = netdev_priv(dev);
if (new_mtu > br_min_mtu(br)) if (new_mtu > br_mtu(br))
return -EINVAL; return -EINVAL;
dev->mtu = new_mtu; dev->mtu = new_mtu;
......
...@@ -424,8 +424,18 @@ int br_del_bridge(struct net *net, const char *name) ...@@ -424,8 +424,18 @@ int br_del_bridge(struct net *net, const char *name)
return ret; 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 */ /* 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; const struct net_bridge_port *p;
int mtu = 0; int mtu = 0;
...@@ -436,13 +446,21 @@ int br_min_mtu(const struct net_bridge *br) ...@@ -436,13 +446,21 @@ int br_min_mtu(const struct net_bridge *br)
mtu = ETH_DATA_LEN; mtu = ETH_DATA_LEN;
else { else {
list_for_each_entry(p, &br->port_list, list) { 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; mtu = p->dev->mtu;
} }
} }
return 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) static void br_set_gso_limits(struct net_bridge *br)
{ {
unsigned int gso_max_size = GSO_MAX_SIZE; unsigned int gso_max_size = GSO_MAX_SIZE;
...@@ -594,7 +612,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, ...@@ -594,7 +612,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev,
if (changed_addr) if (changed_addr)
call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); 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); br_set_gso_limits(br);
kobject_uevent(&p->kobj, KOBJ_ADD); kobject_uevent(&p->kobj, KOBJ_ADD);
...@@ -641,7 +659,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) ...@@ -641,7 +659,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
*/ */
del_nbp(p); 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); br_set_gso_limits(br);
spin_lock_bh(&br->lock); spin_lock_bh(&br->lock);
......
...@@ -578,7 +578,7 @@ int br_del_bridge(struct net *net, const char *name); ...@@ -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, int br_add_if(struct net_bridge *br, struct net_device *dev,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
int br_del_if(struct net_bridge *br, struct net_device *dev); 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 br_features_recompute(struct net_bridge *br,
netdev_features_t features); netdev_features_t features);
void br_port_flags_change(struct net_bridge_port *port, unsigned long mask); void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment