浏览代码

net: bridge: disable bridge MTU auto tuning if it was set manually

As Roopa noted today the biggest source of problems when configuring
bridge and ports is that the bridge MTU keeps changing automatically on
port events (add/del/changemtu). That leads to inconsistent behaviour
and network config software needs to chase the MTU and fix it on each
such event. Let's improve on that situation and allow for the user to
set any MTU within ETH_MIN/MAX limits, but once manually configured it
is the user's responsibility to keep it correct afterwards.

In case the MTU isn't manually set - the behaviour reverts to the
previous and the bridge follows the minimum MTU.

Signed-off-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Nikolay Aleksandrov 7 年之前
父节点
当前提交
804b854d37
共有 4 个文件被更改,包括 26 次插入20 次删除
  1. 1 1
      net/bridge/br.c
  2. 2 3
      net/bridge/br_device.c
  3. 21 15
      net/bridge/br_if.c
  4. 2 1
      net/bridge/br_private.h

+ 1 - 1
net/bridge/br.c

@@ -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_mtu(br, false));
+		br_mtu_auto_adjust(br);
 		break;
 		break;
 
 
 	case NETDEV_CHANGEADDR:
 	case NETDEV_CHANGEADDR:

+ 2 - 3
net/bridge/br_device.c

@@ -225,11 +225,10 @@ 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_mtu(br, br_vlan_enabled(dev)))
-		return -EINVAL;
-
 	dev->mtu = new_mtu;
 	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)
 #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
 	/* remember the MTU in the rtable for PMTU */
 	/* remember the MTU in the rtable for PMTU */
 	dst_metric_set(&br->fake_rtable.dst, RTAX_MTU, new_mtu);
 	dst_metric_set(&br->fake_rtable.dst, RTAX_MTU, new_mtu);

+ 21 - 15
net/bridge/br_if.c

@@ -424,28 +424,34 @@ int br_del_bridge(struct net *net, const char *name)
 	return ret;
 	return ret;
 }
 }
 
 
-/* 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)
+/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
+static int br_mtu_min(const struct net_bridge *br)
 {
 {
 	const struct net_bridge_port *p;
 	const struct net_bridge_port *p;
 	int ret_mtu = 0;
 	int ret_mtu = 0;
 
 
-	ASSERT_RTNL();
-
-	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) {
+	list_for_each_entry(p, &br->port_list, list)
+		if (!ret_mtu || ret_mtu > p->dev->mtu)
 			ret_mtu = p->dev->mtu;
 			ret_mtu = p->dev->mtu;
-		}
-	}
 
 
 	return ret_mtu ? ret_mtu : ETH_DATA_LEN;
 	return ret_mtu ? ret_mtu : ETH_DATA_LEN;
 }
 }
 
 
+void br_mtu_auto_adjust(struct net_bridge *br)
+{
+	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)
 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;
@@ -597,7 +603,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_mtu(br, false));
+	br_mtu_auto_adjust(br);
 	br_set_gso_limits(br);
 	br_set_gso_limits(br);
 
 
 	kobject_uevent(&p->kobj, KOBJ_ADD);
 	kobject_uevent(&p->kobj, KOBJ_ADD);
@@ -644,7 +650,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_mtu(br, false));
+	br_mtu_auto_adjust(br);
 	br_set_gso_limits(br);
 	br_set_gso_limits(br);
 
 
 	spin_lock_bh(&br->lock);
 	spin_lock_bh(&br->lock);

+ 2 - 1
net/bridge/br_private.h

@@ -410,6 +410,7 @@ struct net_bridge {
 	int offload_fwd_mark;
 	int offload_fwd_mark;
 #endif
 #endif
 	bool				neigh_suppress_enabled;
 	bool				neigh_suppress_enabled;
+	bool				mtu_set_by_user;
 	struct hlist_head		fdb_list;
 	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,
 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_mtu(const struct net_bridge *br, bool max);
+void br_mtu_auto_adjust(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);