diff options
Diffstat (limited to 'net/core/dev.c')
| -rw-r--r-- | net/core/dev.c | 91 | 
1 files changed, 80 insertions, 11 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index b65a5051361f..cf8a95f48cff 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2587,13 +2587,19 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)  		return harmonize_features(skb, features);  	} -	features &= (skb->dev->vlan_features | NETIF_F_HW_VLAN_CTAG_TX | -					       NETIF_F_HW_VLAN_STAG_TX); +	features = netdev_intersect_features(features, +					     skb->dev->vlan_features | +					     NETIF_F_HW_VLAN_CTAG_TX | +					     NETIF_F_HW_VLAN_STAG_TX);  	if (protocol == htons(ETH_P_8021Q) || protocol == htons(ETH_P_8021AD)) -		features &= NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | -				NETIF_F_GEN_CSUM | NETIF_F_HW_VLAN_CTAG_TX | -				NETIF_F_HW_VLAN_STAG_TX; +		features = netdev_intersect_features(features, +						     NETIF_F_SG | +						     NETIF_F_HIGHDMA | +						     NETIF_F_FRAGLIST | +						     NETIF_F_GEN_CSUM | +						     NETIF_F_HW_VLAN_CTAG_TX | +						     NETIF_F_HW_VLAN_STAG_TX);  	return harmonize_features(skb, features);  } @@ -4803,9 +4809,14 @@ static void netdev_adjacent_sysfs_del(struct net_device *dev,  	sysfs_remove_link(&(dev->dev.kobj), linkname);  } -#define netdev_adjacent_is_neigh_list(dev, dev_list) \ -		(dev_list == &dev->adj_list.upper || \ -		 dev_list == &dev->adj_list.lower) +static inline bool netdev_adjacent_is_neigh_list(struct net_device *dev, +						 struct net_device *adj_dev, +						 struct list_head *dev_list) +{ +	return (dev_list == &dev->adj_list.upper || +		dev_list == &dev->adj_list.lower) && +		net_eq(dev_net(dev), dev_net(adj_dev)); +}  static int __netdev_adjacent_dev_insert(struct net_device *dev,  					struct net_device *adj_dev, @@ -4835,7 +4846,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,  	pr_debug("dev_hold for %s, because of link added from %s to %s\n",  		 adj_dev->name, dev->name, adj_dev->name); -	if (netdev_adjacent_is_neigh_list(dev, dev_list)) { +	if (netdev_adjacent_is_neigh_list(dev, adj_dev, dev_list)) {  		ret = netdev_adjacent_sysfs_add(dev, adj_dev, dev_list);  		if (ret)  			goto free_adj; @@ -4856,7 +4867,7 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev,  	return 0;  remove_symlinks: -	if (netdev_adjacent_is_neigh_list(dev, dev_list)) +	if (netdev_adjacent_is_neigh_list(dev, adj_dev, dev_list))  		netdev_adjacent_sysfs_del(dev, adj_dev->name, dev_list);  free_adj:  	kfree(adj); @@ -4889,7 +4900,7 @@ static void __netdev_adjacent_dev_remove(struct net_device *dev,  	if (adj->master)  		sysfs_remove_link(&(dev->dev.kobj), "master"); -	if (netdev_adjacent_is_neigh_list(dev, dev_list)) +	if (netdev_adjacent_is_neigh_list(dev, adj_dev, dev_list))  		netdev_adjacent_sysfs_del(dev, adj_dev->name, dev_list);  	list_del_rcu(&adj->list); @@ -5159,11 +5170,65 @@ void netdev_upper_dev_unlink(struct net_device *dev,  }  EXPORT_SYMBOL(netdev_upper_dev_unlink); +void netdev_adjacent_add_links(struct net_device *dev) +{ +	struct netdev_adjacent *iter; + +	struct net *net = dev_net(dev); + +	list_for_each_entry(iter, &dev->adj_list.upper, list) { +		if (!net_eq(net,dev_net(iter->dev))) +			continue; +		netdev_adjacent_sysfs_add(iter->dev, dev, +					  &iter->dev->adj_list.lower); +		netdev_adjacent_sysfs_add(dev, iter->dev, +					  &dev->adj_list.upper); +	} + +	list_for_each_entry(iter, &dev->adj_list.lower, list) { +		if (!net_eq(net,dev_net(iter->dev))) +			continue; +		netdev_adjacent_sysfs_add(iter->dev, dev, +					  &iter->dev->adj_list.upper); +		netdev_adjacent_sysfs_add(dev, iter->dev, +					  &dev->adj_list.lower); +	} +} + +void netdev_adjacent_del_links(struct net_device *dev) +{ +	struct netdev_adjacent *iter; + +	struct net *net = dev_net(dev); + +	list_for_each_entry(iter, &dev->adj_list.upper, list) { +		if (!net_eq(net,dev_net(iter->dev))) +			continue; +		netdev_adjacent_sysfs_del(iter->dev, dev->name, +					  &iter->dev->adj_list.lower); +		netdev_adjacent_sysfs_del(dev, iter->dev->name, +					  &dev->adj_list.upper); +	} + +	list_for_each_entry(iter, &dev->adj_list.lower, list) { +		if (!net_eq(net,dev_net(iter->dev))) +			continue; +		netdev_adjacent_sysfs_del(iter->dev, dev->name, +					  &iter->dev->adj_list.upper); +		netdev_adjacent_sysfs_del(dev, iter->dev->name, +					  &dev->adj_list.lower); +	} +} +  void netdev_adjacent_rename_links(struct net_device *dev, char *oldname)  {  	struct netdev_adjacent *iter; +	struct net *net = dev_net(dev); +  	list_for_each_entry(iter, &dev->adj_list.upper, list) { +		if (!net_eq(net,dev_net(iter->dev))) +			continue;  		netdev_adjacent_sysfs_del(iter->dev, oldname,  					  &iter->dev->adj_list.lower);  		netdev_adjacent_sysfs_add(iter->dev, dev, @@ -5171,6 +5236,8 @@ void netdev_adjacent_rename_links(struct net_device *dev, char *oldname)  	}  	list_for_each_entry(iter, &dev->adj_list.lower, list) { +		if (!net_eq(net,dev_net(iter->dev))) +			continue;  		netdev_adjacent_sysfs_del(iter->dev, oldname,  					  &iter->dev->adj_list.upper);  		netdev_adjacent_sysfs_add(iter->dev, dev, @@ -6773,6 +6840,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char  	/* Send a netdev-removed uevent to the old namespace */  	kobject_uevent(&dev->dev.kobj, KOBJ_REMOVE); +	netdev_adjacent_del_links(dev);  	/* Actually switch the network namespace */  	dev_net_set(dev, net); @@ -6787,6 +6855,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char  	/* Send a netdev-add uevent to the new namespace */  	kobject_uevent(&dev->dev.kobj, KOBJ_ADD); +	netdev_adjacent_add_links(dev);  	/* Fixup kobjects */  	err = device_rename(&dev->dev, dev->name);  |