diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-06-20 01:48:34 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-06-20 01:48:34 -0400 |
commit | cf05c700cf6dd6f28bd95586d3040f809fd365f5 (patch) | |
tree | b21533b22f730e7aac74d5d2eb67d41b41482932 /drivers/net/veth.c | |
parent | 219eb47e6f356b138ea2fe1a32ba5a1b6b9093c0 (diff) |
veth: fix 64bit stats on 32bit arches
Using 64bit stats on 32bit arches must use a synchronization or readers
can get transient values.
Fixes bug introduced in commit 6311cc44a2 (veth: convert to 64 bit
statistics)
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/veth.c')
-rw-r--r-- | drivers/net/veth.c | 55 |
1 files changed, 37 insertions, 18 deletions
diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 8730d6494633..4b6db3b6c5d5 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/ethtool.h> | 13 | #include <linux/ethtool.h> |
14 | #include <linux/etherdevice.h> | 14 | #include <linux/etherdevice.h> |
15 | #include <linux/u64_stats_sync.h> | ||
15 | 16 | ||
16 | #include <net/dst.h> | 17 | #include <net/dst.h> |
17 | #include <net/xfrm.h> | 18 | #include <net/xfrm.h> |
@@ -24,12 +25,13 @@ | |||
24 | #define MAX_MTU 65535 /* Max L3 MTU (arbitrary) */ | 25 | #define MAX_MTU 65535 /* Max L3 MTU (arbitrary) */ |
25 | 26 | ||
26 | struct veth_net_stats { | 27 | struct veth_net_stats { |
27 | u64 rx_packets; | 28 | u64 rx_packets; |
28 | u64 tx_packets; | 29 | u64 tx_packets; |
29 | u64 rx_bytes; | 30 | u64 rx_bytes; |
30 | u64 tx_bytes; | 31 | u64 tx_bytes; |
31 | u64 tx_dropped; | 32 | u64 rx_dropped; |
32 | u64 rx_dropped; | 33 | u64 tx_dropped; |
34 | struct u64_stats_sync syncp; | ||
33 | }; | 35 | }; |
34 | 36 | ||
35 | struct veth_priv { | 37 | struct veth_priv { |
@@ -137,21 +139,29 @@ static netdev_tx_t veth_xmit(struct sk_buff *skb, struct net_device *dev) | |||
137 | if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS) | 139 | if (dev_forward_skb(rcv, skb) != NET_RX_SUCCESS) |
138 | goto rx_drop; | 140 | goto rx_drop; |
139 | 141 | ||
142 | u64_stats_update_begin(&stats->syncp); | ||
140 | stats->tx_bytes += length; | 143 | stats->tx_bytes += length; |
141 | stats->tx_packets++; | 144 | stats->tx_packets++; |
145 | u64_stats_update_end(&stats->syncp); | ||
142 | 146 | ||
147 | u64_stats_update_begin(&rcv_stats->syncp); | ||
143 | rcv_stats->rx_bytes += length; | 148 | rcv_stats->rx_bytes += length; |
144 | rcv_stats->rx_packets++; | 149 | rcv_stats->rx_packets++; |
150 | u64_stats_update_end(&rcv_stats->syncp); | ||
145 | 151 | ||
146 | return NETDEV_TX_OK; | 152 | return NETDEV_TX_OK; |
147 | 153 | ||
148 | tx_drop: | 154 | tx_drop: |
149 | kfree_skb(skb); | 155 | kfree_skb(skb); |
156 | u64_stats_update_begin(&stats->syncp); | ||
150 | stats->tx_dropped++; | 157 | stats->tx_dropped++; |
158 | u64_stats_update_end(&stats->syncp); | ||
151 | return NETDEV_TX_OK; | 159 | return NETDEV_TX_OK; |
152 | 160 | ||
153 | rx_drop: | 161 | rx_drop: |
162 | u64_stats_update_begin(&rcv_stats->syncp); | ||
154 | rcv_stats->rx_dropped++; | 163 | rcv_stats->rx_dropped++; |
164 | u64_stats_update_end(&rcv_stats->syncp); | ||
155 | return NETDEV_TX_OK; | 165 | return NETDEV_TX_OK; |
156 | } | 166 | } |
157 | 167 | ||
@@ -162,21 +172,30 @@ rx_drop: | |||
162 | static struct rtnl_link_stats64 *veth_get_stats64(struct net_device *dev, | 172 | static struct rtnl_link_stats64 *veth_get_stats64(struct net_device *dev, |
163 | struct rtnl_link_stats64 *tot) | 173 | struct rtnl_link_stats64 *tot) |
164 | { | 174 | { |
165 | struct veth_priv *priv; | 175 | struct veth_priv *priv = netdev_priv(dev); |
166 | int cpu; | 176 | int cpu; |
167 | struct veth_net_stats *stats; | ||
168 | |||
169 | priv = netdev_priv(dev); | ||
170 | 177 | ||
171 | for_each_possible_cpu(cpu) { | 178 | for_each_possible_cpu(cpu) { |
172 | stats = per_cpu_ptr(priv->stats, cpu); | 179 | struct veth_net_stats *stats = per_cpu_ptr(priv->stats, cpu); |
173 | 180 | u64 rx_packets, rx_bytes, rx_dropped; | |
174 | tot->rx_packets += stats->rx_packets; | 181 | u64 tx_packets, tx_bytes, tx_dropped; |
175 | tot->tx_packets += stats->tx_packets; | 182 | unsigned int start; |
176 | tot->rx_bytes += stats->rx_bytes; | 183 | |
177 | tot->tx_bytes += stats->tx_bytes; | 184 | do { |
178 | tot->tx_dropped += stats->tx_dropped; | 185 | start = u64_stats_fetch_begin_bh(&stats->syncp); |
179 | tot->rx_dropped += stats->rx_dropped; | 186 | rx_packets = stats->rx_packets; |
187 | tx_packets = stats->tx_packets; | ||
188 | rx_bytes = stats->rx_bytes; | ||
189 | tx_bytes = stats->tx_bytes; | ||
190 | rx_dropped = stats->rx_dropped; | ||
191 | tx_dropped = stats->tx_dropped; | ||
192 | } while (u64_stats_fetch_retry_bh(&stats->syncp, start)); | ||
193 | tot->rx_packets += rx_packets; | ||
194 | tot->tx_packets += tx_packets; | ||
195 | tot->rx_bytes += rx_bytes; | ||
196 | tot->tx_bytes += tx_bytes; | ||
197 | tot->rx_dropped += rx_dropped; | ||
198 | tot->tx_dropped += tx_dropped; | ||
180 | } | 199 | } |
181 | 200 | ||
182 | return tot; | 201 | return tot; |