|
@@ -382,6 +382,24 @@ static void vlan_sync_address(struct net_device *dev,
|
|
memcpy(vlan->real_dev_addr, dev->dev_addr, ETH_ALEN);
|
|
memcpy(vlan->real_dev_addr, dev->dev_addr, ETH_ALEN);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void vlan_transfer_features(struct net_device *dev,
|
|
|
|
+ struct net_device *vlandev)
|
|
|
|
+{
|
|
|
|
+ unsigned long old_features = vlandev->features;
|
|
|
|
+
|
|
|
|
+ if (dev->features & NETIF_F_VLAN_TSO) {
|
|
|
|
+ vlandev->features &= ~VLAN_TSO_FEATURES;
|
|
|
|
+ vlandev->features |= dev->features & VLAN_TSO_FEATURES;
|
|
|
|
+ }
|
|
|
|
+ if (dev->features & NETIF_F_VLAN_CSUM) {
|
|
|
|
+ vlandev->features &= ~NETIF_F_ALL_CSUM;
|
|
|
|
+ vlandev->features |= dev->features & NETIF_F_ALL_CSUM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (old_features != vlandev->features)
|
|
|
|
+ netdev_features_change(vlandev);
|
|
|
|
+}
|
|
|
|
+
|
|
static void __vlan_device_event(struct net_device *dev, unsigned long event)
|
|
static void __vlan_device_event(struct net_device *dev, unsigned long event)
|
|
{
|
|
{
|
|
switch (event) {
|
|
switch (event) {
|
|
@@ -448,6 +466,18 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
|
|
}
|
|
}
|
|
break;
|
|
break;
|
|
|
|
|
|
|
|
+ case NETDEV_FEAT_CHANGE:
|
|
|
|
+ /* Propagate device features to underlying device */
|
|
|
|
+ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
|
|
|
|
+ vlandev = vlan_group_get_device(grp, i);
|
|
|
|
+ if (!vlandev)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ vlan_transfer_features(dev, vlandev);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
case NETDEV_DOWN:
|
|
case NETDEV_DOWN:
|
|
/* Put all VLANs for this dev in the down state too. */
|
|
/* Put all VLANs for this dev in the down state too. */
|
|
for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
|
|
for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
|