aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/dummy.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dummy.c')
-rw-r--r--drivers/net/dummy.c113
1 files changed, 85 insertions, 28 deletions
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index 84e14f397d9..0932ffbf381 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -36,51 +36,106 @@
#include <linux/moduleparam.h>
#include <linux/rtnetlink.h>
#include <net/rtnetlink.h>
+#include <linux/u64_stats_sync.h>
static int numdummies = 1;
-static int dummy_xmit(struct sk_buff *skb, struct net_device *dev);
+/* fake multicast ability */
+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_irq(&dstats->syncp);
+ tbytes = dstats->tx_bytes;
+ tpackets = dstats->tx_packets;
+ } while (u64_stats_fetch_retry_irq(&dstats->syncp, start));
+ stats->tx_bytes += tbytes;
+ stats->tx_packets += tpackets;
+ }
+ return stats;
+}
-static int dummy_set_address(struct net_device *dev, void *p)
+static netdev_tx_t dummy_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct sockaddr *sa = p;
+ struct pcpu_dstats *dstats = this_cpu_ptr(dev->dstats);
- if (!is_valid_ether_addr(sa->sa_data))
- return -EADDRNOTAVAIL;
+ 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 = netdev_alloc_pcpu_stats(struct pcpu_dstats);
+ if (!dev->dstats)
+ return -ENOMEM;
- memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
return 0;
}
-/* fake multicast ability */
-static void set_multicast_list(struct net_device *dev)
+static void dummy_dev_uninit(struct net_device *dev)
{
+ free_percpu(dev->dstats);
}
+static int dummy_change_carrier(struct net_device *dev, bool new_carrier)
+{
+ if (new_carrier)
+ netif_carrier_on(dev);
+ else
+ netif_carrier_off(dev);
+ return 0;
+}
+
+static const struct net_device_ops dummy_netdev_ops = {
+ .ndo_init = dummy_dev_init,
+ .ndo_uninit = dummy_dev_uninit,
+ .ndo_start_xmit = dummy_xmit,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_set_rx_mode = set_multicast_list,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_get_stats64 = dummy_get_stats64,
+ .ndo_change_carrier = dummy_change_carrier,
+};
+
static void dummy_setup(struct net_device *dev)
{
+ ether_setup(dev);
+
/* Initialize the device structure. */
- dev->hard_start_xmit = dummy_xmit;
- dev->set_multicast_list = set_multicast_list;
- dev->set_mac_address = dummy_set_address;
+ dev->netdev_ops = &dummy_netdev_ops;
dev->destructor = free_netdev;
/* Fill in device structure with ethernet-generic values. */
- ether_setup(dev);
dev->tx_queue_len = 0;
- dev->change_mtu = NULL;
dev->flags |= IFF_NOARP;
dev->flags &= ~IFF_MULTICAST;
- random_ether_addr(dev->dev_addr);
-}
-
-static int dummy_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- dev_kfree_skb(skb);
- return 0;
+ dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ dev->features |= NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_TSO;
+ dev->features |= NETIF_F_HW_CSUM | NETIF_F_HIGHDMA | NETIF_F_LLTX;
+ eth_hw_addr_random(dev);
}
static int dummy_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -113,10 +168,6 @@ static int __init dummy_init_one(void)
if (!dev_dummy)
return -ENOMEM;
- err = dev_alloc_name(dev_dummy, dev_dummy->name);
- if (err < 0)
- goto err;
-
dev_dummy->rtnl_link_ops = &dummy_link_ops;
err = register_netdevice(dev_dummy);
if (err < 0)
@@ -134,11 +185,17 @@ static int __init dummy_init_module(void)
rtnl_lock();
err = __rtnl_link_register(&dummy_link_ops);
+ if (err < 0)
+ goto out;
- for (i = 0; i < numdummies && !err; i++)
+ for (i = 0; i < numdummies && !err; i++) {
err = dummy_init_one();
+ cond_resched();
+ }
if (err < 0)
__rtnl_link_unregister(&dummy_link_ops);
+
+out:
rtnl_unlock();
return err;