From 6b10de38f0ef4e921a1f6e5cba2b6c92d6b46ecd Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 14 Jun 2010 05:59:22 +0000 Subject: loopback: Implement 64bit stats on 32bit arches Uses a seqcount_t to synchronize stat producer and consumer, for packets and bytes counter, now u64 types. (dropped counter being rarely used, stay a native "unsigned long" type) No noticeable performance impact on x86, as it only adds two increments per frame. It might be more expensive on arches where smp_wmb() is not free. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/loopback.c | 61 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 10 deletions(-) (limited to 'drivers/net/loopback.c') diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 72b7949c91b1..09334f8f148b 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -60,11 +60,51 @@ #include struct pcpu_lstats { - unsigned long packets; - unsigned long bytes; + u64 packets; + u64 bytes; +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) + seqcount_t seq; +#endif unsigned long drops; }; +#if BITS_PER_LONG==32 && defined(CONFIG_SMP) +static void inline lstats_update_begin(struct pcpu_lstats *lstats) +{ + write_seqcount_begin(&lstats->seq); +} +static void inline lstats_update_end(struct pcpu_lstats *lstats) +{ + write_seqcount_end(&lstats->seq); +} +static void inline lstats_fetch_and_add(u64 *packets, u64 *bytes, const struct pcpu_lstats *lstats) +{ + u64 tpackets, tbytes; + unsigned int seq; + + do { + seq = read_seqcount_begin(&lstats->seq); + tpackets = lstats->packets; + tbytes = lstats->bytes; + } while (read_seqcount_retry(&lstats->seq, seq)); + + *packets += tpackets; + *bytes += tbytes; +} +#else +static void inline lstats_update_begin(struct pcpu_lstats *lstats) +{ +} +static void inline lstats_update_end(struct pcpu_lstats *lstats) +{ +} +static void inline lstats_fetch_and_add(u64 *packets, u64 *bytes, const struct pcpu_lstats *lstats) +{ + *packets += lstats->packets; + *bytes += lstats->bytes; +} +#endif + /* * The higher levels take care of making this non-reentrant (it's * called with bh's disabled). @@ -86,21 +126,23 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, len = skb->len; if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { + lstats_update_begin(lb_stats); lb_stats->bytes += len; lb_stats->packets++; + lstats_update_end(lb_stats); } else lb_stats->drops++; return NETDEV_TX_OK; } -static struct net_device_stats *loopback_get_stats(struct net_device *dev) +static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev) { const struct pcpu_lstats __percpu *pcpu_lstats; - struct net_device_stats *stats = &dev->stats; - unsigned long bytes = 0; - unsigned long packets = 0; - unsigned long drops = 0; + struct rtnl_link_stats64 *stats = &dev->stats64; + u64 bytes = 0; + u64 packets = 0; + u64 drops = 0; int i; pcpu_lstats = (void __percpu __force *)dev->ml_priv; @@ -108,8 +150,7 @@ static struct net_device_stats *loopback_get_stats(struct net_device *dev) const struct pcpu_lstats *lb_stats; lb_stats = per_cpu_ptr(pcpu_lstats, i); - bytes += lb_stats->bytes; - packets += lb_stats->packets; + lstats_fetch_and_add(&packets, &bytes, lb_stats); drops += lb_stats->drops; } stats->rx_packets = packets; @@ -158,7 +199,7 @@ static void loopback_dev_free(struct net_device *dev) static const struct net_device_ops loopback_ops = { .ndo_init = loopback_dev_init, .ndo_start_xmit= loopback_xmit, - .ndo_get_stats = loopback_get_stats, + .ndo_get_stats64 = loopback_get_stats64, }; /* -- cgit v1.2.3 From 5eaa0bd81f93225b6d1972b373ed00ca763052b2 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Jun 2010 12:44:11 +0000 Subject: loopback: use u64_stats_sync infrastructure Commit 6b10de38f0ef (loopback: Implement 64bit stats on 32bit arches) introduced 64bit stats in loopback driver, using a private seqcount and private helpers. David suggested to introduce a generic infrastructure, added in (net: Introduce u64_stats_sync infrastructure) This patch reimplements loopback 64bit stats using the u64_stats_sync infrastructure. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/loopback.c | 62 +++++++++++++------------------------------------- 1 file changed, 16 insertions(+), 46 deletions(-) (limited to 'drivers/net/loopback.c') diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 09334f8f148b..4dd0510d7a99 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -58,53 +58,15 @@ #include #include #include +#include struct pcpu_lstats { - u64 packets; - u64 bytes; -#if BITS_PER_LONG==32 && defined(CONFIG_SMP) - seqcount_t seq; -#endif - unsigned long drops; + u64 packets; + u64 bytes; + struct u64_stats_sync syncp; + unsigned long drops; }; -#if BITS_PER_LONG==32 && defined(CONFIG_SMP) -static void inline lstats_update_begin(struct pcpu_lstats *lstats) -{ - write_seqcount_begin(&lstats->seq); -} -static void inline lstats_update_end(struct pcpu_lstats *lstats) -{ - write_seqcount_end(&lstats->seq); -} -static void inline lstats_fetch_and_add(u64 *packets, u64 *bytes, const struct pcpu_lstats *lstats) -{ - u64 tpackets, tbytes; - unsigned int seq; - - do { - seq = read_seqcount_begin(&lstats->seq); - tpackets = lstats->packets; - tbytes = lstats->bytes; - } while (read_seqcount_retry(&lstats->seq, seq)); - - *packets += tpackets; - *bytes += tbytes; -} -#else -static void inline lstats_update_begin(struct pcpu_lstats *lstats) -{ -} -static void inline lstats_update_end(struct pcpu_lstats *lstats) -{ -} -static void inline lstats_fetch_and_add(u64 *packets, u64 *bytes, const struct pcpu_lstats *lstats) -{ - *packets += lstats->packets; - *bytes += lstats->bytes; -} -#endif - /* * The higher levels take care of making this non-reentrant (it's * called with bh's disabled). @@ -126,10 +88,10 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, len = skb->len; if (likely(netif_rx(skb) == NET_RX_SUCCESS)) { - lstats_update_begin(lb_stats); + u64_stats_update_begin(&lb_stats->syncp); lb_stats->bytes += len; lb_stats->packets++; - lstats_update_end(lb_stats); + u64_stats_update_end(&lb_stats->syncp); } else lb_stats->drops++; @@ -148,10 +110,18 @@ static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev) pcpu_lstats = (void __percpu __force *)dev->ml_priv; for_each_possible_cpu(i) { const struct pcpu_lstats *lb_stats; + u64 tbytes, tpackets; + unsigned int start; lb_stats = per_cpu_ptr(pcpu_lstats, i); - lstats_fetch_and_add(&packets, &bytes, lb_stats); + do { + start = u64_stats_fetch_begin(&lb_stats->syncp); + tbytes = lb_stats->bytes; + tpackets = lb_stats->packets; + } while (u64_stats_fetch_retry(&lb_stats->syncp, start)); drops += lb_stats->drops; + bytes += tbytes; + packets += tpackets; } stats->rx_packets = packets; stats->tx_packets = packets; -- cgit v1.2.3 From 28172739f0a276eb8d6ca917b3974c2edb036da3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 7 Jul 2010 14:58:56 -0700 Subject: net: fix 64 bit counters on 32 bit arches There is a small possibility that a reader gets incorrect values on 32 bit arches. SNMP applications could catch incorrect counters when a 32bit high part is changed by another stats consumer/provider. One way to solve this is to add a rtnl_link_stats64 param to all ndo_get_stats64() methods, and also add such a parameter to dev_get_stats(). Rule is that we are not allowed to use dev->stats64 as a temporary storage for 64bit stats, but a caller provided area (usually on stack) Old drivers (only providing get_stats() method) need no changes. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- drivers/net/loopback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/loopback.c') diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 4dd0510d7a99..9a0996795321 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -98,10 +98,10 @@ static netdev_tx_t loopback_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev) +static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { const struct pcpu_lstats __percpu *pcpu_lstats; - struct rtnl_link_stats64 *stats = &dev->stats64; u64 bytes = 0; u64 packets = 0; u64 drops = 0; -- cgit v1.2.3