Merge git://git.kernel.org/pub/scm/linux/kernel/git/lethal/sh-2.6
[cascardo/linux.git] / drivers / net / macvlan.c
index f15fe2c..0fc9dc7 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)
 {
@@ -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)
@@ -754,6 +788,10 @@ static int macvlan_device_event(struct notifier_block *unused,
                }
                break;
        case NETDEV_UNREGISTER:
+               /* twiddle thumbs on netns device moves */
+               if (dev->reg_state != NETREG_UNREGISTERING)
+                       break;
+
                list_for_each_entry_safe(vlan, next, &port->vlans, list)
                        vlan->dev->rtnl_link_ops->dellink(vlan->dev, NULL);
                break;
@@ -773,14 +811,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 +824,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);
 }