diff options
Diffstat (limited to 'drivers/net/macvlan.c')
| -rw-r--r-- | drivers/net/macvlan.c | 95 | 
1 files changed, 63 insertions, 32 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index f15fe2cf72a..0ef0eb0db94 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -37,8 +37,14 @@ struct macvlan_port {  	struct net_device	*dev;  	struct hlist_head	vlan_hash[MACVLAN_HASH_SIZE];  	struct list_head	vlans; +	struct rcu_head		rcu;  }; +#define macvlan_port_get_rcu(dev) \ +	((struct macvlan_port *) rcu_dereference(dev->rx_handler_data)) +#define macvlan_port_get(dev) ((struct macvlan_port *) dev->rx_handler_data) +#define macvlan_port_exists(dev) (dev->priv_flags & IFF_MACVLAN_PORT) +  static struct macvlan_dev *macvlan_hash_lookup(const struct macvlan_port *port,  					       const unsigned char *addr)  { @@ -145,15 +151,17 @@ static void macvlan_broadcast(struct sk_buff *skb,  }  /* called under rcu_read_lock() from netif_receive_skb */ -static struct sk_buff *macvlan_handle_frame(struct macvlan_port *port, -					    struct sk_buff *skb) +static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)  { +	struct macvlan_port *port;  	const struct ethhdr *eth = eth_hdr(skb);  	const struct macvlan_dev *vlan;  	const struct macvlan_dev *src;  	struct net_device *dev; -	unsigned int len; +	unsigned int len = 0; +	int ret = NET_RX_DROP; +	port = macvlan_port_get_rcu(skb->dev);  	if (is_multicast_ether_addr(eth->h_dest)) {  		src = macvlan_hash_lookup(port, eth->h_source);  		if (!src) @@ -188,14 +196,16 @@ static struct sk_buff *macvlan_handle_frame(struct macvlan_port *port,  	}  	len = skb->len + ETH_HLEN;  	skb = skb_share_check(skb, GFP_ATOMIC); -	macvlan_count_rx(vlan, len, skb != NULL, 0);  	if (!skb) -		return NULL; +		goto out;  	skb->dev = dev;  	skb->pkt_type = PACKET_HOST; -	vlan->receive(skb); +	ret = vlan->receive(skb); + +out: +	macvlan_count_rx(vlan, len, ret == NET_RX_SUCCESS, 0);  	return NULL;  } @@ -424,29 +434,38 @@ static void macvlan_uninit(struct net_device *dev)  	free_percpu(vlan->rx_stats);  } -static struct net_device_stats *macvlan_dev_get_stats(struct net_device *dev) +static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev, +							 struct rtnl_link_stats64 *stats)  { -	struct net_device_stats *stats = &dev->stats;  	struct macvlan_dev *vlan = netdev_priv(dev);  	dev_txq_stats_fold(dev, stats);  	if (vlan->rx_stats) { -		struct macvlan_rx_stats *p, rx = {0}; +		struct macvlan_rx_stats *p, accum = {0}; +		u64 rx_packets, rx_bytes, rx_multicast; +		unsigned int start;  		int i;  		for_each_possible_cpu(i) {  			p = per_cpu_ptr(vlan->rx_stats, i); -			rx.rx_packets += p->rx_packets; -			rx.rx_bytes   += p->rx_bytes; -			rx.rx_errors  += p->rx_errors; -			rx.multicast  += p->multicast; +			do { +				start = u64_stats_fetch_begin_bh(&p->syncp); +				rx_packets	= p->rx_packets; +				rx_bytes	= p->rx_bytes; +				rx_multicast	= p->rx_multicast; +			} while (u64_stats_fetch_retry_bh(&p->syncp, start)); +			accum.rx_packets	+= rx_packets; +			accum.rx_bytes		+= rx_bytes; +			accum.rx_multicast	+= rx_multicast; +			/* rx_errors is an ulong, updated without syncp protection */ +			accum.rx_errors		+= p->rx_errors;  		} -		stats->rx_packets = rx.rx_packets; -		stats->rx_bytes   = rx.rx_bytes; -		stats->rx_errors  = rx.rx_errors; -		stats->rx_dropped = rx.rx_errors; -		stats->multicast  = rx.multicast; +		stats->rx_packets = accum.rx_packets; +		stats->rx_bytes   = accum.rx_bytes; +		stats->rx_errors  = accum.rx_errors; +		stats->rx_dropped = accum.rx_errors; +		stats->multicast  = accum.rx_multicast;  	}  	return stats;  } @@ -495,7 +514,7 @@ static const struct net_device_ops macvlan_netdev_ops = {  	.ndo_change_rx_flags	= macvlan_change_rx_flags,  	.ndo_set_mac_address	= macvlan_set_mac_address,  	.ndo_set_multicast_list	= macvlan_set_multicast_list, -	.ndo_get_stats		= macvlan_dev_get_stats, +	.ndo_get_stats64	= macvlan_dev_get_stats64,  	.ndo_validate_addr	= eth_validate_addr,  }; @@ -521,6 +540,7 @@ static int macvlan_port_create(struct net_device *dev)  {  	struct macvlan_port *port;  	unsigned int i; +	int err;  	if (dev->type != ARPHRD_ETHER || dev->flags & IFF_LOOPBACK)  		return -EINVAL; @@ -533,19 +553,32 @@ static int macvlan_port_create(struct net_device *dev)  	INIT_LIST_HEAD(&port->vlans);  	for (i = 0; i < MACVLAN_HASH_SIZE; i++)  		INIT_HLIST_HEAD(&port->vlan_hash[i]); -	rcu_assign_pointer(dev->macvlan_port, port); -	return 0; + +	err = netdev_rx_handler_register(dev, macvlan_handle_frame, port); +	if (err) +		kfree(port); + +	dev->priv_flags |= IFF_MACVLAN_PORT; +	return err;  } -static void macvlan_port_destroy(struct net_device *dev) +static void macvlan_port_rcu_free(struct rcu_head *head)  { -	struct macvlan_port *port = dev->macvlan_port; +	struct macvlan_port *port; -	rcu_assign_pointer(dev->macvlan_port, NULL); -	synchronize_rcu(); +	port = container_of(head, struct macvlan_port, rcu);  	kfree(port);  } +static void macvlan_port_destroy(struct net_device *dev) +{ +	struct macvlan_port *port = macvlan_port_get(dev); + +	dev->priv_flags &= ~IFF_MACVLAN_PORT; +	netdev_rx_handler_unregister(dev); +	call_rcu(&port->rcu, macvlan_port_rcu_free); +} +  static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])  {  	if (tb[IFLA_ADDRESS]) { @@ -621,12 +654,12 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev,  	if (!tb[IFLA_ADDRESS])  		random_ether_addr(dev->dev_addr); -	if (lowerdev->macvlan_port == NULL) { +	if (!macvlan_port_exists(lowerdev)) {  		err = macvlan_port_create(lowerdev);  		if (err < 0)  			return err;  	} -	port = lowerdev->macvlan_port; +	port = macvlan_port_get(lowerdev);  	vlan->lowerdev = lowerdev;  	vlan->dev      = dev; @@ -736,10 +769,11 @@ static int macvlan_device_event(struct notifier_block *unused,  	struct macvlan_dev *vlan, *next;  	struct macvlan_port *port; -	port = dev->macvlan_port; -	if (port == NULL) +	if (!macvlan_port_exists(dev))  		return NOTIFY_DONE; +	port = macvlan_port_get(dev); +  	switch (event) {  	case NETDEV_CHANGE:  		list_for_each_entry(vlan, &port->vlans, list) @@ -773,14 +807,12 @@ static int __init macvlan_init_module(void)  	int err;  	register_netdevice_notifier(&macvlan_notifier_block); -	macvlan_handle_frame_hook = macvlan_handle_frame;  	err = macvlan_link_register(&macvlan_link_ops);  	if (err < 0)  		goto err1;  	return 0;  err1: -	macvlan_handle_frame_hook = NULL;  	unregister_netdevice_notifier(&macvlan_notifier_block);  	return err;  } @@ -788,7 +820,6 @@ err1:  static void __exit macvlan_cleanup_module(void)  {  	rtnl_link_unregister(&macvlan_link_ops); -	macvlan_handle_frame_hook = NULL;  	unregister_netdevice_notifier(&macvlan_notifier_block);  }  |