macvlan: Precise RX stats accounting
[safe/jmp/linux-2.6] / drivers / net / macvlan.c
index 271aa7e..ae2b5c7 100644 (file)
@@ -38,12 +38,27 @@ struct macvlan_port {
        struct list_head        vlans;
 };
 
+/**
+ *     struct macvlan_rx_stats - MACVLAN percpu rx stats
+ *     @rx_packets: number of received packets
+ *     @rx_bytes: number of received bytes
+ *     @multicast: number of received multicast packets
+ *     @rx_errors: number of errors
+ */
+struct macvlan_rx_stats {
+       unsigned long rx_packets;
+       unsigned long rx_bytes;
+       unsigned long multicast;
+       unsigned long rx_errors;
+};
+
 struct macvlan_dev {
        struct net_device       *dev;
        struct list_head        list;
        struct hlist_node       hlist;
        struct macvlan_port     *port;
        struct net_device       *lowerdev;
+       struct macvlan_rx_stats *rx_stats;
 };
 
 
@@ -110,6 +125,7 @@ static void macvlan_broadcast(struct sk_buff *skb,
        struct net_device *dev;
        struct sk_buff *nskb;
        unsigned int i;
+       struct macvlan_rx_stats *rx_stats;
 
        if (skb->protocol == htons(ETH_P_PAUSE))
                return;
@@ -117,17 +133,17 @@ static void macvlan_broadcast(struct sk_buff *skb,
        for (i = 0; i < MACVLAN_HASH_SIZE; i++) {
                hlist_for_each_entry_rcu(vlan, n, &port->vlan_hash[i], hlist) {
                        dev = vlan->dev;
+                       rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id());
 
                        nskb = skb_clone(skb, GFP_ATOMIC);
                        if (nskb == NULL) {
-                               dev->stats.rx_errors++;
-                               dev->stats.rx_dropped++;
+                               rx_stats->rx_errors++;
                                continue;
                        }
 
-                       dev->stats.rx_bytes += skb->len + ETH_HLEN;
-                       dev->stats.rx_packets++;
-                       dev->stats.multicast++;
+                       rx_stats->rx_bytes += skb->len + ETH_HLEN;
+                       rx_stats->rx_packets++;
+                       rx_stats->multicast++;
 
                        nskb->dev = dev;
                        if (!compare_ether_addr_64bits(eth->h_dest, dev->broadcast))
@@ -147,6 +163,7 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
        const struct macvlan_port *port;
        const struct macvlan_dev *vlan;
        struct net_device *dev;
+       struct macvlan_rx_stats *rx_stats;
 
        port = rcu_dereference(skb->dev->macvlan_port);
        if (port == NULL)
@@ -166,16 +183,15 @@ static struct sk_buff *macvlan_handle_frame(struct sk_buff *skb)
                kfree_skb(skb);
                return NULL;
        }
-
+       rx_stats = per_cpu_ptr(vlan->rx_stats, smp_processor_id());
        skb = skb_share_check(skb, GFP_ATOMIC);
        if (skb == NULL) {
-               dev->stats.rx_errors++;
-               dev->stats.rx_dropped++;
+               rx_stats->rx_errors++;
                return NULL;
        }
 
-       dev->stats.rx_bytes += skb->len + ETH_HLEN;
-       dev->stats.rx_packets++;
+       rx_stats->rx_bytes += skb->len + ETH_HLEN;
+       rx_stats->rx_packets++;
 
        skb->dev = dev;
        skb->pkt_type = PACKET_HOST;
@@ -365,9 +381,47 @@ static int macvlan_init(struct net_device *dev)
 
        macvlan_set_lockdep_class(dev);
 
+       vlan->rx_stats = alloc_percpu(struct macvlan_rx_stats);
+       if (!vlan->rx_stats)
+               return -ENOMEM;
+
        return 0;
 }
 
+static void macvlan_uninit(struct net_device *dev)
+{
+       struct macvlan_dev *vlan = netdev_priv(dev);
+
+       free_percpu(vlan->rx_stats);
+}
+
+static struct net_device_stats *macvlan_dev_get_stats(struct net_device *dev)
+{
+       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};
+               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;
+               }
+               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;
+       }
+       return stats;
+}
+
 static void macvlan_ethtool_get_drvinfo(struct net_device *dev,
                                        struct ethtool_drvinfo *drvinfo)
 {
@@ -404,6 +458,7 @@ static const struct ethtool_ops macvlan_ethtool_ops = {
 
 static const struct net_device_ops macvlan_netdev_ops = {
        .ndo_init               = macvlan_init,
+       .ndo_uninit             = macvlan_uninit,
        .ndo_open               = macvlan_open,
        .ndo_stop               = macvlan_stop,
        .ndo_start_xmit         = macvlan_start_xmit,
@@ -411,6 +466,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_validate_addr      = eth_validate_addr,
 };