dummy: percpu stats and lockless xmit
[deliverable/linux.git] / drivers / net / dummy.c
index 37dcfdc63456bb521bfd0e74ec58f65ad403dea5..ff2d29b178587eebfd22feba9e21aae496f12b17 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]) {
This page took 0.032442 seconds and 5 git commands to generate.