@@ -123,12 +123,16 @@ static struct rtnl_link_stats64 *loopback_get_stats64(struct net_device *dev)
bytes += tbytes;
packets += tpackets;
}
+
+ netdev_stats_update_begin(dev);
stats->rx_packets = packets;
stats->tx_packets = packets;
stats->rx_dropped = drops;
stats->rx_errors = drops;
stats->rx_bytes = bytes;
stats->tx_bytes = bytes;
+ netdev_stats_update_end(dev);
+
return stats;
}
@@ -458,11 +458,13 @@ static struct rtnl_link_stats64 *macvlan_dev_get_stats64(struct net_device *dev)
/* rx_errors is an ulong, updated without syncp protection */
accum.rx_errors += p->rx_errors;
}
+ netdev_stats_update_begin(dev);
stats->rx_packets = accum.rx_packets;
stats->rx_bytes = accum.rx_bytes;
stats->rx_errors = accum.rx_errors;
stats->rx_dropped = accum.rx_errors;
stats->multicast = accum.rx_multicast;
+ netdev_stats_update_end(dev);
}
return stats;
}
@@ -1543,6 +1543,7 @@ static struct rtnl_link_stats64 *efx_net_stats(struct net_device *net_dev)
efx->type->update_stats(efx);
spin_unlock_bh(&efx->stats_lock);
+ netdev_stats_update_begin(net_dev);
stats->rx_packets = mac_stats->rx_packets;
stats->tx_packets = mac_stats->tx_packets;
stats->rx_bytes = mac_stats->rx_bytes;
@@ -1565,6 +1566,7 @@ static struct rtnl_link_stats64 *efx_net_stats(struct net_device *net_dev)
stats->tx_errors = (stats->tx_window_errors +
mac_stats->tx_bad);
+ netdev_stats_update_end(net_dev);
return stats;
}
@@ -886,6 +886,9 @@ struct net_device {
int ifindex;
int iflink;
+#if BITS_PER_LONG==32
+ seqlock_t stats_lock;
+#endif
union {
struct rtnl_link_stats64 stats64;
struct net_device_stats stats;
@@ -1080,6 +1083,46 @@ struct net_device {
};
#define to_net_dev(d) container_of(d, struct net_device, dev)
+
+static inline void netdev_stats_initlock(struct net_device *dev)
+{
+#if BITS_PER_LONG==32
+ seqlock_init(&dev->stats_lock);
+#endif
+}
+
+static inline void netdev_stats_update_begin(struct net_device *dev)
+{
+#if BITS_PER_LONG==32
+ write_seqlock(&dev->stats_lock);
+#endif
+}
+
+static inline void netdev_stats_update_end(struct net_device *dev)
+{
+#if BITS_PER_LONG==32
+ write_sequnlock(&dev->stats_lock);
+#endif
+}
+
+static inline unsigned int netdev_stats_fetch_begin(const struct net_device *dev)
+{
+#if BITS_PER_LONG==32
+ return read_seqbegin(&dev->stats_lock);
+#else
+ return 0;
+#endif
+}
+
+static bool inline netdev_stats_fetch_retry(const struct net_device *dev, unsigned int start)
+{
+#if BITS_PER_LONG==32
+ return read_seqretry(&dev->stats_lock, start);
+#else
+ return false;
+#endif
+}
+
#define NETDEV_ALIGN 32
static inline
@@ -830,10 +830,12 @@ static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev)
/* rx_errors is an ulong, not protected by syncp */
accum.rx_errors += p->rx_errors;
}
+ netdev_stats_update_begin(dev);
stats->rx_packets = accum.rx_packets;
stats->rx_bytes = accum.rx_bytes;
stats->rx_errors = accum.rx_errors;
stats->multicast = accum.rx_multicast;
+ netdev_stats_update_end(dev);
}
return stats;
}
@@ -3704,24 +3704,31 @@ void dev_seq_stop(struct seq_file *seq, void *v)
static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev)
{
const struct rtnl_link_stats64 *stats = dev_get_stats(dev);
+ struct rtnl_link_stats64 temp;
+ unsigned int start;
+
+ do {
+ start = netdev_stats_fetch_begin(dev);
+ memcpy(&temp, stats, sizeof(temp));
+ } while (netdev_stats_fetch_retry(dev, start));
seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu "
"%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n",
- dev->name, stats->rx_bytes, stats->rx_packets,
- stats->rx_errors,
- stats->rx_dropped + stats->rx_missed_errors,
- stats->rx_fifo_errors,
- stats->rx_length_errors + stats->rx_over_errors +
- stats->rx_crc_errors + stats->rx_frame_errors,
- stats->rx_compressed, stats->multicast,
- stats->tx_bytes, stats->tx_packets,
- stats->tx_errors, stats->tx_dropped,
- stats->tx_fifo_errors, stats->collisions,
- stats->tx_carrier_errors +
- stats->tx_aborted_errors +
- stats->tx_window_errors +
- stats->tx_heartbeat_errors,
- stats->tx_compressed);
+ dev->name, temp.rx_bytes, temp.rx_packets,
+ temp.rx_errors,
+ temp.rx_dropped + temp.rx_missed_errors,
+ temp.rx_fifo_errors,
+ temp.rx_length_errors + temp.rx_over_errors +
+ temp.rx_crc_errors + temp.rx_frame_errors,
+ temp.rx_compressed, temp.multicast,
+ temp.tx_bytes, temp.tx_packets,
+ temp.tx_errors, temp.tx_dropped,
+ temp.tx_fifo_errors, temp.collisions,
+ temp.tx_carrier_errors +
+ temp.tx_aborted_errors +
+ temp.tx_window_errors +
+ temp.tx_heartbeat_errors,
+ temp.tx_compressed);
}
/*
@@ -5386,6 +5393,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,
if (dev_addr_init(dev))
goto free_rx;
+ netdev_stats_initlock(dev);
dev_mc_init(dev);
dev_uc_init(dev);
@@ -610,37 +610,43 @@ static void copy_rtnl_link_stats(struct rtnl_link_stats *a,
a->tx_compressed = b->tx_compressed;
}
-static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b)
+static void copy_rtnl_link_stats64(void *v, const struct rtnl_link_stats64 *b,
+ struct net_device *dev)
{
struct rtnl_link_stats64 a;
-
- a.rx_packets = b->rx_packets;
- a.tx_packets = b->tx_packets;
- a.rx_bytes = b->rx_bytes;
- a.tx_bytes = b->tx_bytes;
- a.rx_errors = b->rx_errors;
- a.tx_errors = b->tx_errors;
- a.rx_dropped = b->rx_dropped;
- a.tx_dropped = b->tx_dropped;
-
- a.multicast = b->multicast;
- a.collisions = b->collisions;
-
- a.rx_length_errors = b->rx_length_errors;
- a.rx_over_errors = b->rx_over_errors;
- a.rx_crc_errors = b->rx_crc_errors;
- a.rx_frame_errors = b->rx_frame_errors;
- a.rx_fifo_errors = b->rx_fifo_errors;
- a.rx_missed_errors = b->rx_missed_errors;
-
- a.tx_aborted_errors = b->tx_aborted_errors;
- a.tx_carrier_errors = b->tx_carrier_errors;
- a.tx_fifo_errors = b->tx_fifo_errors;
- a.tx_heartbeat_errors = b->tx_heartbeat_errors;
- a.tx_window_errors = b->tx_window_errors;
-
- a.rx_compressed = b->rx_compressed;
- a.tx_compressed = b->tx_compressed;
+ unsigned int start;
+
+ do {
+ start = netdev_stats_fetch_begin(dev);
+
+ a.rx_packets = b->rx_packets;
+ a.tx_packets = b->tx_packets;
+ a.rx_bytes = b->rx_bytes;
+ a.tx_bytes = b->tx_bytes;
+ a.rx_errors = b->rx_errors;
+ a.tx_errors = b->tx_errors;
+ a.rx_dropped = b->rx_dropped;
+ a.tx_dropped = b->tx_dropped;
+
+ a.multicast = b->multicast;
+ a.collisions = b->collisions;
+
+ a.rx_length_errors = b->rx_length_errors;
+ a.rx_over_errors = b->rx_over_errors;
+ a.rx_crc_errors = b->rx_crc_errors;
+ a.rx_frame_errors = b->rx_frame_errors;
+ a.rx_fifo_errors = b->rx_fifo_errors;
+ a.rx_missed_errors = b->rx_missed_errors;
+
+ a.tx_aborted_errors = b->tx_aborted_errors;
+ a.tx_carrier_errors = b->tx_carrier_errors;
+ a.tx_fifo_errors = b->tx_fifo_errors;
+ a.tx_heartbeat_errors = b->tx_heartbeat_errors;
+ a.tx_window_errors = b->tx_window_errors;
+
+ a.rx_compressed = b->rx_compressed;
+ a.tx_compressed = b->tx_compressed;
+ } while (netdev_stats_fetch_retry(dev, start));
memcpy(v, &a, sizeof(a));
}
@@ -854,7 +860,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
sizeof(struct rtnl_link_stats64));
if (attr == NULL)
goto nla_put_failure;
- copy_rtnl_link_stats64(nla_data(attr), stats);
+ copy_rtnl_link_stats64(nla_data(attr), stats, dev);
if (dev->dev.parent)
NLA_PUT_U32(skb, IFLA_NUM_VF, dev_num_vf(dev->dev.parent));