diff options
Diffstat (limited to 'drivers/net/macvlan.c')
| -rw-r--r-- | drivers/net/macvlan.c | 94 | 
1 files changed, 77 insertions, 17 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index b34eaaae03fd..72b801803aa4 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -39,16 +39,20 @@  #define MACVLAN_HASH_SIZE	(1<<MACVLAN_HASH_BITS)  #define MACVLAN_BC_QUEUE_LEN	1000 +#define MACVLAN_F_PASSTHRU	1 +#define MACVLAN_F_ADDRCHANGE	2 +  struct macvlan_port {  	struct net_device	*dev;  	struct hlist_head	vlan_hash[MACVLAN_HASH_SIZE];  	struct list_head	vlans;  	struct sk_buff_head	bc_queue;  	struct work_struct	bc_work; -	bool 			passthru; +	u32			flags;  	int			count;  	struct hlist_head	vlan_source_hash[MACVLAN_HASH_SIZE];  	DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ); +	unsigned char           perm_addr[ETH_ALEN];  };  struct macvlan_source_entry { @@ -66,6 +70,31 @@ struct macvlan_skb_cb {  static void macvlan_port_destroy(struct net_device *dev); +static inline bool macvlan_passthru(const struct macvlan_port *port) +{ +	return port->flags & MACVLAN_F_PASSTHRU; +} + +static inline void macvlan_set_passthru(struct macvlan_port *port) +{ +	port->flags |= MACVLAN_F_PASSTHRU; +} + +static inline bool macvlan_addr_change(const struct macvlan_port *port) +{ +	return port->flags & MACVLAN_F_ADDRCHANGE; +} + +static inline void macvlan_set_addr_change(struct macvlan_port *port) +{ +	port->flags |= MACVLAN_F_ADDRCHANGE; +} + +static inline void macvlan_clear_addr_change(struct macvlan_port *port) +{ +	port->flags &= ~MACVLAN_F_ADDRCHANGE; +} +  /* Hash Ethernet address */  static u32 macvlan_eth_hash(const unsigned char *addr)  { @@ -181,11 +210,12 @@ static void macvlan_hash_change_addr(struct macvlan_dev *vlan,  static bool macvlan_addr_busy(const struct macvlan_port *port,  			      const unsigned char *addr)  { -	/* Test to see if the specified multicast address is +	/* Test to see if the specified address is  	 * currently in use by the underlying device or  	 * another macvlan.  	 */ -	if (ether_addr_equal_64bits(port->dev->dev_addr, addr)) +	if (!macvlan_passthru(port) && !macvlan_addr_change(port) && +	    ether_addr_equal_64bits(port->dev->dev_addr, addr))  		return true;  	if (macvlan_hash_lookup(port, addr)) @@ -445,7 +475,7 @@ static rx_handler_result_t macvlan_handle_frame(struct sk_buff **pskb)  	}  	macvlan_forward_source(skb, port, eth->h_source); -	if (port->passthru) +	if (macvlan_passthru(port))  		vlan = list_first_or_null_rcu(&port->vlans,  					      struct macvlan_dev, list);  	else @@ -574,7 +604,7 @@ static int macvlan_open(struct net_device *dev)  	struct net_device *lowerdev = vlan->lowerdev;  	int err; -	if (vlan->port->passthru) { +	if (macvlan_passthru(vlan->port)) {  		if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC)) {  			err = dev_set_promiscuity(lowerdev, 1);  			if (err < 0) @@ -649,7 +679,7 @@ static int macvlan_stop(struct net_device *dev)  	dev_uc_unsync(lowerdev, dev);  	dev_mc_unsync(lowerdev, dev); -	if (vlan->port->passthru) { +	if (macvlan_passthru(vlan->port)) {  		if (!(vlan->flags & MACVLAN_FLAG_NOPROMISC))  			dev_set_promiscuity(lowerdev, -1);  		goto hash_del; @@ -672,6 +702,7 @@ static int macvlan_sync_address(struct net_device *dev, unsigned char *addr)  {  	struct macvlan_dev *vlan = netdev_priv(dev);  	struct net_device *lowerdev = vlan->lowerdev; +	struct macvlan_port *port = vlan->port;  	int err;  	if (!(dev->flags & IFF_UP)) { @@ -682,7 +713,7 @@ static int macvlan_sync_address(struct net_device *dev, unsigned char *addr)  		if (macvlan_addr_busy(vlan->port, addr))  			return -EBUSY; -		if (!vlan->port->passthru) { +		if (!macvlan_passthru(port)) {  			err = dev_uc_add(lowerdev, addr);  			if (err)  				return err; @@ -692,6 +723,15 @@ static int macvlan_sync_address(struct net_device *dev, unsigned char *addr)  		macvlan_hash_change_addr(vlan, addr);  	} +	if (macvlan_passthru(port) && !macvlan_addr_change(port)) { +		/* Since addr_change isn't set, we are here due to lower +		 * device change.  Save the lower-dev address so we can +		 * restore it later. +		 */ +		ether_addr_copy(vlan->port->perm_addr, +				lowerdev->dev_addr); +	} +	macvlan_clear_addr_change(port);  	return 0;  } @@ -703,7 +743,12 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)  	if (!is_valid_ether_addr(addr->sa_data))  		return -EADDRNOTAVAIL; +	/* If the addresses are the same, this is a no-op */ +	if (ether_addr_equal(dev->dev_addr, addr->sa_data)) +		return 0; +  	if (vlan->mode == MACVLAN_MODE_PASSTHRU) { +		macvlan_set_addr_change(vlan->port);  		dev_set_mac_address(vlan->lowerdev, addr);  		return 0;  	} @@ -789,10 +834,12 @@ static int macvlan_change_mtu(struct net_device *dev, int new_mtu)   */  static struct lock_class_key macvlan_netdev_addr_lock_key; -#define ALWAYS_ON_FEATURES \ -	(NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | NETIF_F_LLTX | \ +#define ALWAYS_ON_OFFLOADS \ +	(NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE | \  	 NETIF_F_GSO_ROBUST) +#define ALWAYS_ON_FEATURES (ALWAYS_ON_OFFLOADS | NETIF_F_LLTX) +  #define MACVLAN_FEATURES \  	(NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | \  	 NETIF_F_GSO | NETIF_F_TSO | NETIF_F_UFO | NETIF_F_LRO | \ @@ -827,6 +874,7 @@ static int macvlan_init(struct net_device *dev)  	dev->features		|= ALWAYS_ON_FEATURES;  	dev->hw_features	|= NETIF_F_LRO;  	dev->vlan_features	= lowerdev->vlan_features & MACVLAN_FEATURES; +	dev->vlan_features	|= ALWAYS_ON_OFFLOADS;  	dev->gso_max_size	= lowerdev->gso_max_size;  	dev->gso_max_segs	= lowerdev->gso_max_segs;  	dev->hard_header_len	= lowerdev->hard_header_len; @@ -925,7 +973,7 @@ static int macvlan_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],  	/* Support unicast filter only on passthru devices.  	 * Multicast filter should be allowed on all devices.  	 */ -	if (!vlan->port->passthru && is_unicast_ether_addr(addr)) +	if (!macvlan_passthru(vlan->port) && is_unicast_ether_addr(addr))  		return -EOPNOTSUPP;  	if (flags & NLM_F_REPLACE) @@ -949,7 +997,7 @@ static int macvlan_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],  	/* Support unicast filter only on passthru devices.  	 * Multicast filter should be allowed on all devices.  	 */ -	if (!vlan->port->passthru && is_unicast_ether_addr(addr)) +	if (!macvlan_passthru(vlan->port) && is_unicast_ether_addr(addr))  		return -EOPNOTSUPP;  	if (is_unicast_ether_addr(addr)) @@ -1089,7 +1137,7 @@ void macvlan_common_setup(struct net_device *dev)  	netif_keep_dst(dev);  	dev->priv_flags	       |= IFF_UNICAST_FLT;  	dev->netdev_ops		= &macvlan_netdev_ops; -	dev->destructor		= free_netdev; +	dev->needs_free_netdev	= true;  	dev->header_ops		= &macvlan_hard_header_ops;  	dev->ethtool_ops	= &macvlan_ethtool_ops;  } @@ -1117,8 +1165,8 @@ static int macvlan_port_create(struct net_device *dev)  	if (port == NULL)  		return -ENOMEM; -	port->passthru = false;  	port->dev = dev; +	ether_addr_copy(port->perm_addr, dev->dev_addr);  	INIT_LIST_HEAD(&port->vlans);  	for (i = 0; i < MACVLAN_HASH_SIZE; i++)  		INIT_HLIST_HEAD(&port->vlan_hash[i]); @@ -1158,6 +1206,18 @@ static void macvlan_port_destroy(struct net_device *dev)  		kfree_skb(skb);  	} +	/* If the lower device address has been changed by passthru +	 * macvlan, put it back. +	 */ +	if (macvlan_passthru(port) && +	    !ether_addr_equal(port->dev->dev_addr, port->perm_addr)) { +		struct sockaddr sa; + +		sa.sa_family = port->dev->type; +		memcpy(&sa.sa_data, port->perm_addr, port->dev->addr_len); +		dev_set_mac_address(port->dev, &sa); +	} +  	kfree(port);  } @@ -1323,7 +1383,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,  	port = macvlan_port_get_rtnl(lowerdev);  	/* Only 1 macvlan device can be created in passthru mode */ -	if (port->passthru) { +	if (macvlan_passthru(port)) {  		/* The macvlan port must be not created this time,  		 * still goto destroy_macvlan_port for readability.  		 */ @@ -1349,7 +1409,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,  			err = -EINVAL;  			goto destroy_macvlan_port;  		} -		port->passthru = true; +		macvlan_set_passthru(port);  		eth_hw_addr_inherit(dev, lowerdev);  	} @@ -1431,7 +1491,7 @@ static int macvlan_changelink(struct net_device *dev,  	if (data && data[IFLA_MACVLAN_FLAGS]) {  		__u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);  		bool promisc = (flags ^ vlan->flags) & MACVLAN_FLAG_NOPROMISC; -		if (vlan->port->passthru && promisc) { +		if (macvlan_passthru(vlan->port) && promisc) {  			int err;  			if (flags & MACVLAN_FLAG_NOPROMISC) @@ -1594,7 +1654,7 @@ static int macvlan_device_event(struct notifier_block *unused,  		}  		break;  	case NETDEV_CHANGEADDR: -		if (!port->passthru) +		if (!macvlan_passthru(port))  			return NOTIFY_DONE;  		vlan = list_first_entry_or_null(&port->vlans,  | 
