184 lines
5.2 KiB
Diff
184 lines
5.2 KiB
Diff
From fccaf71011b171883efee5bae321eac4760584d1 Mon Sep 17 00:00:00 2001
|
|
From: Eric Dumazet <eric.dumazet@gmail.com>
|
|
Date: Tue, 17 Nov 2009 08:53:49 +0000
|
|
Subject: [PATCH 1/4] 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>
|
|
---
|
|
drivers/net/macvlan.c | 76 ++++++++++++++++++++++++++++++++++++++++++------
|
|
1 files changed, 66 insertions(+), 10 deletions(-)
|
|
|
|
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
|
|
index 271aa7e..ae2b5c7 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,
|
|
};
|
|
|
|
--
|
|
1.6.6.2
|
|
|