Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[cascardo/linux.git] / drivers / net / macvlan.c
index 53422ce..0ef0eb0 100644 (file)
@@ -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)
 {
@@ -152,9 +158,10 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *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 = rcu_dereference(skb->dev->macvlan_port);
+       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)
@@ -189,14 +196,16 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
        }
        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;
 }
 
@@ -425,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;
 }
@@ -496,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);
 
@@ -509,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;
 }
 
@@ -529,25 +553,30 @@ 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);
 
-       err = netdev_rx_handler_register(dev, macvlan_handle_frame);
-       if (err) {
-               rcu_assign_pointer(dev->macvlan_port, NULL);
+       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_rcu_free(struct rcu_head *head)
+{
+       struct macvlan_port *port;
+
+       port = container_of(head, struct macvlan_port, rcu);
+       kfree(port);
+}
+
 static void macvlan_port_destroy(struct net_device *dev)
 {
-       struct macvlan_port *port = dev->macvlan_port;
+       struct macvlan_port *port = macvlan_port_get(dev);
 
+       dev->priv_flags &= ~IFF_MACVLAN_PORT;
        netdev_rx_handler_unregister(dev);
-       rcu_assign_pointer(dev->macvlan_port, NULL);
-       synchronize_rcu();
-       kfree(port);
+       call_rcu(&port->rcu, macvlan_port_rcu_free);
 }
 
 static int macvlan_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -625,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;
@@ -715,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;
@@ -729,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,
 };
@@ -740,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)