diff options
Diffstat (limited to 'drivers/net/macvlan.c')
-rw-r--r-- | drivers/net/macvlan.c | 105 |
1 files changed, 71 insertions, 34 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 87e8d4cb405..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,11 +514,11 @@ 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, }; -static void macvlan_setup(struct net_device *dev) +void macvlan_common_setup(struct net_device *dev) { ether_setup(dev); @@ -508,6 +527,12 @@ static void macvlan_setup(struct net_device *dev) dev->destructor = free_netdev; dev->header_ops = &macvlan_hard_header_ops, dev->ethtool_ops = &macvlan_ethtool_ops; +} +EXPORT_SYMBOL_GPL(macvlan_common_setup); + +static void macvlan_setup(struct net_device *dev) +{ + macvlan_common_setup(dev); dev->tx_queue_len = 0; } @@ -515,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; @@ -527,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]) { @@ -615,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; @@ -705,7 +744,6 @@ int macvlan_link_register(struct rtnl_link_ops *ops) /* common fields */ ops->priv_size = sizeof(struct macvlan_dev); ops->get_tx_queues = macvlan_get_tx_queues; - ops->setup = macvlan_setup; ops->validate = macvlan_validate; ops->maxtype = IFLA_MACVLAN_MAX; ops->policy = macvlan_policy; @@ -719,6 +757,7 @@ EXPORT_SYMBOL_GPL(macvlan_link_register); static struct rtnl_link_ops macvlan_link_ops = { .kind = "macvlan", + .setup = macvlan_setup, .newlink = macvlan_newlink, .dellink = macvlan_dellink, }; @@ -730,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) @@ -767,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; } @@ -782,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); } |