diff --git a/net/bridge/br.c b/net/bridge/br.c index 7770481a6506..a3f95ab9d6a3 100644 --- a/net/bridge/br.c +++ b/net/bridge/br.c @@ -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: diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 1285ca30ab0a..278fc999d355 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -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; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 9ba4ed65c52b..48dc4d2e2be3 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -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); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 8e13a64d8c99..048d5b51813b 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -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);