Commit 24197ee2 authored by David S. Miller's avatar David S. Miller

Merge branch 'bridge-mtu'

Nikolay Aleksandrov says:

====================
net: bridge: MTU handling changes

As previously discussed the recent changes break some setups and could lead
to packet drops. Thus the first patch reverts the behaviour for the bridge
to follow the minimum MTU but also keeps the ability to set the MTU to the
maximum (out of all ports) if vlan filtering is enabled. Patch 02 is the
bigger change in behaviour - we've always had trouble when configuring
bridges and their MTU which is auto tuning on port events
(add/del/changemtu), which means config software needs to chase it and fix
it after each such event, after patch 02 we allow the user to configure any
MTU (ETH_MIN/MAX limited) but once that is done the bridge stops auto
tuning and relies on the user to keep the MTU correct.
This should be compatible with cases that don't touch the MTU (or set it
to the same value), while allowing to configure the MTU and not worry
about it changing afterwards.

The patches are intentionally split like this, so that if they get accepted
and there are any complaints patch 02 can be reverted.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 56c03cbf 804b854d
...@@ -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_mtu(br)); br_mtu_auto_adjust(br);
break; break;
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
......
...@@ -224,11 +224,11 @@ static void br_get_stats64(struct net_device *dev, ...@@ -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) 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))
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);
......
...@@ -424,41 +424,32 @@ int br_del_bridge(struct net *net, const char *name) ...@@ -424,41 +424,32 @@ 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 */
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; 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)) return ret_mtu ? ret_mtu : ETH_DATA_LEN;
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;
} }
int br_mtu(const struct net_bridge *br) void br_mtu_auto_adjust(struct net_bridge *br)
{ {
if (br_vlan_enabled(br->dev)) ASSERT_RTNL();
return __br_mtu(br, max_mtu);
else /* if the bridge MTU was manually configured don't mess with it */
return __br_mtu(br, min_mtu); 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)
...@@ -612,7 +603,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev, ...@@ -612,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)); 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);
...@@ -659,7 +650,7 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) ...@@ -659,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)); 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);
......
...@@ -410,6 +410,7 @@ struct net_bridge { ...@@ -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); ...@@ -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); 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);
......
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