aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2009-11-17 08:53:49 +0000
committerDavid S. Miller <davem@davemloft.net>2009-11-17 23:51:57 -0800
commitfccaf71011b171883efee5bae321eac4760584d1 (patch)
treea44bb3d88d778891b29049700bd9611d255f8045
parent9793241fe92f7d9303fb221e43fc598eb065f267 (diff)
macvlan: Precise RX stats accounting
With multi queue devices, its possible that several cpus call macvlan RX routines simultaneously for the same macvlan device. We update RX stats counter without any locking, so we can get slightly wrong counters. One possible fix is to use percpu counters, to get precise accounting and also get guarantee of no cache line ping pongs between cpus. Note: this adds 16 bytes (32 bytes on 64bit arches) of percpu data per macvlan device. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/macvlan.c76
1 files changed, 66 insertions, 10 deletions
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index 271aa7e1d03..ae2b5c79c55 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -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,
};