static int veth_dev_init(struct net_device *dev)
 {
-       int err;
-
-       dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats);
-       if (!dev->lstats)
-               return -ENOMEM;
-
-       err = veth_alloc_queues(dev);
-       if (err) {
-               free_percpu(dev->lstats);
-               return err;
-       }
-
-       return 0;
+       return veth_alloc_queues(dev);
 }
 
 static void veth_dev_free(struct net_device *dev)
 {
        veth_free_queues(dev);
-       free_percpu(dev->lstats);
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
                               NETIF_F_HW_VLAN_STAG_RX);
        dev->needs_free_netdev = true;
        dev->priv_destructor = veth_dev_free;
+       dev->pcpu_stat_type = NETDEV_PCPU_STAT_LSTATS;
        dev->max_mtu = ETH_MAX_MTU;
 
        dev->hw_features = VETH_FEATURES;
 
 
        vrf_rtable_release(dev, vrf);
        vrf_rt6_release(dev, vrf);
-
-       free_percpu(dev->dstats);
-       dev->dstats = NULL;
 }
 
 static int vrf_dev_init(struct net_device *dev)
 {
        struct net_vrf *vrf = netdev_priv(dev);
 
-       dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
-       if (!dev->dstats)
-               goto out_nomem;
-
        /* create the default dst which points back to us */
        if (vrf_rtable_create(dev) != 0)
-               goto out_stats;
+               goto out_nomem;
 
        if (vrf_rt6_create(dev) != 0)
                goto out_rth;
 
 out_rth:
        vrf_rtable_release(dev, vrf);
-out_stats:
-       free_percpu(dev->dstats);
-       dev->dstats = NULL;
 out_nomem:
        return -ENOMEM;
 }
        dev->min_mtu = IPV6_MIN_MTU;
        dev->max_mtu = IP6_MAX_MTU;
        dev->mtu = dev->max_mtu;
+
+       dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS;
 }
 
 static int vrf_validate(struct nlattr *tb[], struct nlattr *data[],
 
        ML_PRIV_CAN,
 };
 
+enum netdev_stat_type {
+       NETDEV_PCPU_STAT_NONE,
+       NETDEV_PCPU_STAT_LSTATS, /* struct pcpu_lstats */
+       NETDEV_PCPU_STAT_TSTATS, /* struct pcpu_sw_netstats */
+       NETDEV_PCPU_STAT_DSTATS, /* struct pcpu_dstats */
+};
+
 /**
  *     struct net_device - The DEVICE structure.
  *
  *
  *     @ml_priv:       Mid-layer private
  *     @ml_priv_type:  Mid-layer private type
- *     @lstats:        Loopback statistics
- *     @tstats:        Tunnel statistics
- *     @dstats:        Dummy statistics
- *     @vstats:        Virtual ethernet statistics
+ *
+ *     @pcpu_stat_type:        Type of device statistics which the core should
+ *                             allocate/free: none, lstats, tstats, dstats. none
+ *                             means the driver is handling statistics allocation/
+ *                             freeing internally.
+ *     @lstats:                Loopback statistics: packets, bytes
+ *     @tstats:                Tunnel statistics: RX/TX packets, RX/TX bytes
+ *     @dstats:                Dummy statistics: RX/TX/drop packets, RX/TX bytes
  *
  *     @garp_port:     GARP
  *     @mrp_port:      MRP
        void                            *ml_priv;
        enum netdev_ml_priv_type        ml_priv_type;
 
+       enum netdev_stat_type           pcpu_stat_type:8;
        union {
                struct pcpu_lstats __percpu             *lstats;
                struct pcpu_sw_netstats __percpu        *tstats;
 
 }
 EXPORT_SYMBOL(netif_tx_stop_all_queues);
 
+static int netdev_do_alloc_pcpu_stats(struct net_device *dev)
+{
+       void __percpu *v;
+
+       switch (dev->pcpu_stat_type) {
+       case NETDEV_PCPU_STAT_NONE:
+               return 0;
+       case NETDEV_PCPU_STAT_LSTATS:
+               v = dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats);
+               break;
+       case NETDEV_PCPU_STAT_TSTATS:
+               v = dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
+               break;
+       case NETDEV_PCPU_STAT_DSTATS:
+               v = dev->dstats = netdev_alloc_pcpu_stats(struct pcpu_dstats);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return v ? 0 : -ENOMEM;
+}
+
+static void netdev_do_free_pcpu_stats(struct net_device *dev)
+{
+       switch (dev->pcpu_stat_type) {
+       case NETDEV_PCPU_STAT_NONE:
+               return;
+       case NETDEV_PCPU_STAT_LSTATS:
+               free_percpu(dev->lstats);
+               break;
+       case NETDEV_PCPU_STAT_TSTATS:
+               free_percpu(dev->tstats);
+               break;
+       case NETDEV_PCPU_STAT_DSTATS:
+               free_percpu(dev->dstats);
+               break;
+       }
+}
+
 /**
  * register_netdevice() - register a network device
  * @dev: device to register
                goto err_uninit;
        }
 
+       ret = netdev_do_alloc_pcpu_stats(dev);
+       if (ret)
+               goto err_uninit;
+
        ret = dev_index_reserve(net, dev->ifindex);
        if (ret < 0)
-               goto err_uninit;
+               goto err_free_pcpu;
        dev->ifindex = ret;
 
        /* Transfer changeable features to wanted_features and enable
        call_netdevice_notifiers(NETDEV_PRE_UNINIT, dev);
 err_ifindex_release:
        dev_index_release(net, dev->ifindex);
+err_free_pcpu:
+       netdev_do_free_pcpu_stats(dev);
 err_uninit:
        if (dev->netdev_ops->ndo_uninit)
                dev->netdev_ops->ndo_uninit(dev);
                WARN_ON(rcu_access_pointer(dev->ip_ptr));
                WARN_ON(rcu_access_pointer(dev->ip6_ptr));
 
+               netdev_do_free_pcpu_stats(dev);
                if (dev->priv_destructor)
                        dev->priv_destructor(dev);
                if (dev->needs_free_netdev)