summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/bridge/br.c2
-rw-r--r--net/bridge/br_device.c4
-rw-r--r--net/bridge/br_if.c49
-rw-r--r--net/bridge/br_private.h3
4 files changed, 25 insertions, 33 deletions
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 26e1616b2c90..671d13c10f6f 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_mtu(br));
+ br_mtu_auto_adjust(br);
break;
case NETDEV_CHANGEADDR:
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 278fc999d355..e682a668ce57 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -224,11 +224,11 @@ 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))
- return -EINVAL;
dev->mtu = new_mtu;
+ /* this flag will be cleared if the MTU was automatically adjusted */
+ br->mtu_set_by_user = true;
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
/* remember the MTU in the rtable for PMTU */
dst_metric_set(&br->fake_rtable.dst, RTAX_MTU, new_mtu);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 87b2afd455c7..82c1a6f430b3 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -424,41 +424,32 @@ 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))
+static int br_mtu_min(const struct net_bridge *br)
{
const struct net_bridge_port *p;
- int mtu = 0;
+ int ret_mtu = 0;
- ASSERT_RTNL();
+ list_for_each_entry(p, &br->port_list, list)
+ if (!ret_mtu || ret_mtu > p->dev->mtu)
+ ret_mtu = p->dev->mtu;
- 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;
- }
- }
- return mtu;
+ return ret_mtu ? ret_mtu : ETH_DATA_LEN;
}
-int br_mtu(const struct net_bridge *br)
+void br_mtu_auto_adjust(struct net_bridge *br)
{
- if (br_vlan_enabled(br->dev))
- return __br_mtu(br, max_mtu);
- else
- return __br_mtu(br, min_mtu);
+ ASSERT_RTNL();
+
+ /* if the bridge MTU was manually configured don't mess with it */
+ if (br->mtu_set_by_user)
+ return;
+
+ /* change to the minimum MTU and clear the flag which was set by
+ * the bridge ndo_change_mtu callback
+ */
+ dev_set_mtu(br->dev, br_mtu_min(br));
+ br->mtu_set_by_user = false;
}
static void br_set_gso_limits(struct net_bridge *br)
@@ -612,7 +603,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));
+ br_mtu_auto_adjust(br);
br_set_gso_limits(br);
kobject_uevent(&p->kobj, KOBJ_ADD);
@@ -659,7 +650,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev)
*/
del_nbp(p);
- dev_set_mtu(br->dev, br_mtu(br));
+ br_mtu_auto_adjust(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 048d5b51813b..a7cb3ece5031 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -410,6 +410,7 @@ struct net_bridge {
int offload_fwd_mark;
#endif
bool neigh_suppress_enabled;
+ bool mtu_set_by_user;
struct hlist_head fdb_list;
};
@@ -578,7 +579,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);
+void br_mtu_auto_adjust(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);