|
@@ -6288,6 +6288,44 @@ static void rollback_registered(struct net_device *dev)
|
|
|
list_del(&single);
|
|
|
}
|
|
|
|
|
|
+static netdev_features_t netdev_sync_upper_features(struct net_device *lower,
|
|
|
+ struct net_device *upper, netdev_features_t features)
|
|
|
+{
|
|
|
+ netdev_features_t upper_disables = NETIF_F_UPPER_DISABLES;
|
|
|
+ netdev_features_t feature;
|
|
|
+
|
|
|
+ for_each_netdev_feature(&upper_disables, feature) {
|
|
|
+ if (!(upper->wanted_features & feature)
|
|
|
+ && (features & feature)) {
|
|
|
+ netdev_dbg(lower, "Dropping feature %pNF, upper dev %s has it off.\n",
|
|
|
+ &feature, upper->name);
|
|
|
+ features &= ~feature;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return features;
|
|
|
+}
|
|
|
+
|
|
|
+static void netdev_sync_lower_features(struct net_device *upper,
|
|
|
+ struct net_device *lower, netdev_features_t features)
|
|
|
+{
|
|
|
+ netdev_features_t upper_disables = NETIF_F_UPPER_DISABLES;
|
|
|
+ netdev_features_t feature;
|
|
|
+
|
|
|
+ for_each_netdev_feature(&upper_disables, feature) {
|
|
|
+ if (!(features & feature) && (lower->features & feature)) {
|
|
|
+ netdev_dbg(upper, "Disabling feature %pNF on lower dev %s.\n",
|
|
|
+ &feature, lower->name);
|
|
|
+ lower->wanted_features &= ~feature;
|
|
|
+ netdev_update_features(lower);
|
|
|
+
|
|
|
+ if (unlikely(lower->features & feature))
|
|
|
+ netdev_WARN(upper, "failed to disable %pNF on %s!\n",
|
|
|
+ &feature, lower->name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static netdev_features_t netdev_fix_features(struct net_device *dev,
|
|
|
netdev_features_t features)
|
|
|
{
|
|
@@ -6357,7 +6395,9 @@ static netdev_features_t netdev_fix_features(struct net_device *dev,
|
|
|
|
|
|
int __netdev_update_features(struct net_device *dev)
|
|
|
{
|
|
|
+ struct net_device *upper, *lower;
|
|
|
netdev_features_t features;
|
|
|
+ struct list_head *iter;
|
|
|
int err = 0;
|
|
|
|
|
|
ASSERT_RTNL();
|
|
@@ -6370,6 +6410,10 @@ int __netdev_update_features(struct net_device *dev)
|
|
|
/* driver might be less strict about feature dependencies */
|
|
|
features = netdev_fix_features(dev, features);
|
|
|
|
|
|
+ /* some features can't be enabled if they're off an an upper device */
|
|
|
+ netdev_for_each_upper_dev_rcu(dev, upper, iter)
|
|
|
+ features = netdev_sync_upper_features(dev, upper, features);
|
|
|
+
|
|
|
if (dev->features == features)
|
|
|
return 0;
|
|
|
|
|
@@ -6386,6 +6430,12 @@ int __netdev_update_features(struct net_device *dev)
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
|
+ /* some features must be disabled on lower devices when disabled
|
|
|
+ * on an upper device (think: bonding master or bridge)
|
|
|
+ */
|
|
|
+ netdev_for_each_lower_dev(dev, lower, iter)
|
|
|
+ netdev_sync_lower_features(dev, lower, features);
|
|
|
+
|
|
|
if (!err)
|
|
|
dev->features = features;
|
|
|
|