dummy: percpu stats and lockless xmit
authorEric Dumazet <eric.dumazet@gmail.com>
Mon, 27 Sep 2010 20:50:33 +0000 (20:50 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 29 Sep 2010 20:23:30 +0000 (13:23 -0700)
Converts dummy network device driver to :

- percpu stats

- 64bit stats

- lockless xmit (NETIF_F_LLTX)

- performance features added (NETIF_F_SG | NETIF_F_FRAGLIST |
NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_HIGHDMA)

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dummy.c
include/linux/netdevice.h

index 37dcfdc..ff2d29b 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/moduleparam.h>
 #include <linux/rtnetlink.h>
 #include <net/rtnetlink.h>
+#include <linux/u64_stats_sync.h>
 
 static int numdummies = 1;
 
@@ -55,21 +56,69 @@ static void set_multicast_list(struct net_device *dev)
 {
 }
 
+struct pcpu_dstats {
+       u64                     tx_packets;
+       u64                     tx_bytes;
+       struct u64_stats_sync   syncp;
+};
+
+static struct rtnl_link_stats64 *dummy_get_stats64(struct net_device *dev,
+                                                  struct rtnl_link_stats64 *stats)
+{
+       int i;
+
+       for_each_possible_cpu(i) {
+               const struct pcpu_dstats *dstats;
+               u64 tbytes, tpackets;
+               unsigned int start;
+
+               dstats = per_cpu_ptr(dev->dstats, i);
+               do {
+                       start = u64_stats_fetch_begin(&dstats->syncp);
+                       tbytes = dstats->tx_bytes;
+                       tpackets = dstats->tx_packets;
+               } while (u64_stats_fetch_retry(&dstats->syncp, start));
+               stats->tx_bytes += tbytes;
+               stats->tx_packets += tpackets;
+       }
+       return stats;
+}
 
 static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       dev->stats.tx_packets++;
-       dev->stats.tx_bytes += skb->len;
+       struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
+
+       u64_stats_update_begin(&dstats->syncp);
+       dstats->tx_packets++;
+       dstats->tx_bytes += skb->len;
+       u64_stats_update_end(&dstats->syncp);
 
        dev_kfree_skb(skb);
        return NETDEV_TX_OK;
 }
 
+static int dummy_dev_init(struct net_device *dev)
+{
+       dev->dstats = alloc_percpu(struct pcpu_dstats);
+       if (!dev->dstats)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void dummy_dev_free(struct net_device *dev)
+{
+       free_percpu(dev->dstats);
+       free_netdev(dev);
+}
+
 static const struct net_device_ops dummy_netdev_ops = {
+       .ndo_init               = dummy_dev_init,
        .ndo_start_xmit         = dummy_xmit,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_multicast_list = set_multicast_list,
        .ndo_set_mac_address    = dummy_set_address,
+       .ndo_get_stats64        = dummy_get_stats64,
 };
 
 static void dummy_setup(struct net_device *dev)
@@ -78,14 +127,17 @@ static void dummy_setup(struct net_device *dev)
 
        /* Initialize the device structure. */
        dev->netdev_ops = &dummy_netdev_ops;
-       dev->destructor = free_netdev;
+       dev->destructor = dummy_dev_free;
 
        /* Fill in device structure with ethernet-generic values. */
        dev->tx_queue_len = 0;
        dev->flags |= IFF_NOARP;
        dev->flags &= ~IFF_MULTICAST;
+       dev->features   |= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO;
+       dev->features   |= NETIF_F_NO_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
        random_ether_addr(dev->dev_addr);
 }
+
 static int dummy_validate(struct nlattr *tb[], struct nlattr *data[])
 {
        if (tb[IFLA_ADDRESS]) {
index c2bec99..6f0845e 100644 (file)
@@ -1057,6 +1057,7 @@ struct net_device {
                void                            *ml_priv;
                struct pcpu_lstats __percpu     *lstats; /* loopback stats */
                struct pcpu_tstats __percpu     *tstats; /* tunnel stats */
+               struct pcpu_dstats __percpu     *dstats; /* dummy stats */
        };
        /* GARP */
        struct garp_port        *garp_port;