VLAN MTU propagation
Subsystems (2)
How about this patch (still compiling, so untested)?
Its based on Rick's suggestion of using an operational and a desired MTU. The operational MTU is simply the net_device's MTU and is always set to the minimum of the lower device's MTU and the desired MTU. The desired MTU can be changed by configuring a MTU for the VLAN device manually.
The remaining question is whether we should report the desired MTU to userspace using the netlink interface, or maybe print a message when the MTU is limited by the lower device.
include/linux/netdevice.h +1 -0
@@ -1226,6 +1226,7 @@ extern int dev_change_flags(struct net_device *, unsigned);extern int dev_change_name (struct net_device *,char *);extern int dev_change_net_namespace (struct net_device *,struct net *,const char *); +extern void dev_notify_mtu (struct net_device *);extern int dev_set_mtu (struct net_device *,int );extern int dev_set_mac_address (struct net_device *,struct sockaddr *);
net/8021q/vlan.c +13 -0
@@ -347,6 +347,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) new_dev->mtu = real_dev->mtu;vlan_dev_info (new_dev)->vlan_id = vlan_id; +vlan_dev_info (new_dev)->mtu = new_dev->mtu;vlan_dev_info (new_dev)->real_dev = real_dev;vlan_dev_info (new_dev)->dent =NULL ;vlan_dev_info (new_dev)->flags =VLAN_FLAG_REORDER_HDR ;
@@ -477,6 +478,18 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,break ; +case NETDEV_CHANGEMTU : +for (i =0 ; i <VLAN_GROUP_ARRAY_LEN ; i++) { + vlandev =vlan_group_get_device (grp, i); +if (!vlandev) +continue ; + + vlandev->mtu =min (vlan_dev_info (vlandev)->mtu, + dev->mtu); +dev_notify_mtu (vlandev); + } +break ; +case NETDEV_DOWN :/* Put all VLANs for this dev in the down state too. */ for (i =0 ; i <VLAN_GROUP_ARRAY_LEN ; i++) {
net/8021q/vlan.h +2 -0
@@ -24,6 +24,7 @@ struct vlan_priority_tci_mapping {* @egress_priority_map: hash of egress priority mappings * @vlan_id: VLAN identifier * @flags: device flags +* @mtu: desired MTU - effective MTU is limited by MTU of real device * @real_dev: underlying netdevice * @real_dev_addr: address of underlying netdevice * @dent: proc dir entry
@@ -38,6 +39,7 @@ struct vlan_dev_info {u16 vlan_id;u16 flags; +unsigned int mtu;struct net_device *real_dev;unsigned char real_dev_addr[ETH_ALEN ];
net/8021q/vlan_dev.c +4 -5
@@ -347,14 +347,13 @@ static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,static int vlan_dev_change_mtu (struct net_device *dev,int new_mtu) { +struct vlan_dev_info *vlan =vlan_dev_info (dev); +/* TODO: gotta make sure the underlying layer can handle it, * maybe an IFF_VLAN_CAPABLE flag for devices? */ -if (vlan_dev_info (dev)->real_dev->mtu < new_mtu) -return -ERANGE ; - - dev->mtu = new_mtu; - + vlan->mtu = new_mtu; + dev->mtu = min_t(unsigned int, vlan->real_dev->mtu, new_mtu);return 0 ; }
net/8021q/vlan_netlink.c +3 -3
@@ -125,9 +125,9 @@ static int vlan_newlink(struct net_device *dev,return err;if (!tb[IFLA_MTU ]) - dev->mtu = real_dev->mtu; -else if (dev->mtu > real_dev->mtu) -return -EINVAL ; + dev->mtu = vlan->mtu = real_dev->mtu; +else + vlan->mtu =nla_get_u32 (tb[IFLA_MTU ]); err =vlan_changelink (dev, tb, data);if (err <0 )
net/core/dev.c +9 -2
@@ -3399,6 +3399,13 @@ int dev_change_flags(struct net_device *dev, unsigned flags)return ret; } +void dev_notify_mtu (struct net_device *dev) +{ +if (dev->flags &IFF_UP ) +call_netdevice_notifiers (NETDEV_CHANGEMTU , dev); +} +EXPORT_SYMBOL_GPL (dev_notify_mtu); +int dev_set_mtu (struct net_device *dev,int new_mtu) {int err;
@@ -3418,8 +3425,8 @@ int dev_set_mtu(struct net_device *dev, int new_mtu) err = dev->change_mtu (dev, new_mtu); else dev->mtu = new_mtu; -if (!err && dev->flags &IFF_UP ) -call_netdevice_notifiers (NETDEV_CHANGEMTU , dev); +if (!err) +dev_notify_mtu (dev);return err; }