diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2010-07-07 17:58:56 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-07-07 17:58:56 -0400 |
commit | 28172739f0a276eb8d6ca917b3974c2edb036da3 (patch) | |
tree | b1dc00cfa20c209992e247c6f73601f609f9ca3b /net | |
parent | 217d32dc5f299c483ca0d3c8cc6811c72c0339c4 (diff) |
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 <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/8021q/vlan_dev.c | 6 | ||||
-rw-r--r-- | net/8021q/vlanproc.c | 3 | ||||
-rw-r--r-- | net/bridge/br_device.c | 4 | ||||
-rw-r--r-- | net/core/dev.c | 25 | ||||
-rw-r--r-- | net/core/net-sysfs.c | 4 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 3 |
6 files changed, 27 insertions, 18 deletions
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index c6456cb842fa..7865a4ce5250 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c | |||
@@ -803,11 +803,9 @@ static u32 vlan_ethtool_get_flags(struct net_device *dev) | |||
803 | return dev_ethtool_get_flags(vlan->real_dev); | 803 | return dev_ethtool_get_flags(vlan->real_dev); |
804 | } | 804 | } |
805 | 805 | ||
806 | static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev) | 806 | static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) |
807 | { | 807 | { |
808 | struct rtnl_link_stats64 *stats = &dev->stats64; | 808 | dev_txq_stats_fold(dev, (struct net_device_stats *)stats); |
809 | |||
810 | dev_txq_stats_fold(dev, &dev->stats); | ||
811 | 809 | ||
812 | if (vlan_dev_info(dev)->vlan_rx_stats) { | 810 | if (vlan_dev_info(dev)->vlan_rx_stats) { |
813 | struct vlan_rx_stats *p, accum = {0}; | 811 | struct vlan_rx_stats *p, accum = {0}; |
diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c index df56f5ce887c..80e280f56686 100644 --- a/net/8021q/vlanproc.c +++ b/net/8021q/vlanproc.c | |||
@@ -278,6 +278,7 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) | |||
278 | { | 278 | { |
279 | struct net_device *vlandev = (struct net_device *) seq->private; | 279 | struct net_device *vlandev = (struct net_device *) seq->private; |
280 | const struct vlan_dev_info *dev_info = vlan_dev_info(vlandev); | 280 | const struct vlan_dev_info *dev_info = vlan_dev_info(vlandev); |
281 | struct rtnl_link_stats64 temp; | ||
281 | const struct rtnl_link_stats64 *stats; | 282 | const struct rtnl_link_stats64 *stats; |
282 | static const char fmt[] = "%30s %12lu\n"; | 283 | static const char fmt[] = "%30s %12lu\n"; |
283 | static const char fmt64[] = "%30s %12llu\n"; | 284 | static const char fmt64[] = "%30s %12llu\n"; |
@@ -286,7 +287,7 @@ static int vlandev_seq_show(struct seq_file *seq, void *offset) | |||
286 | if (!is_vlan_dev(vlandev)) | 287 | if (!is_vlan_dev(vlandev)) |
287 | return 0; | 288 | return 0; |
288 | 289 | ||
289 | stats = dev_get_stats(vlandev); | 290 | stats = dev_get_stats(vlandev, &temp); |
290 | seq_printf(seq, | 291 | seq_printf(seq, |
291 | "%s VID: %d REORDER_HDR: %i dev->priv_flags: %hx\n", | 292 | "%s VID: %d REORDER_HDR: %i dev->priv_flags: %hx\n", |
292 | vlandev->name, dev_info->vlan_id, | 293 | vlandev->name, dev_info->vlan_id, |
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index edf639e96281..075c435ad22d 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -98,10 +98,10 @@ static int br_dev_stop(struct net_device *dev) | |||
98 | return 0; | 98 | return 0; |
99 | } | 99 | } |
100 | 100 | ||
101 | static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev) | 101 | static struct rtnl_link_stats64 *br_get_stats64(struct net_device *dev, |
102 | struct rtnl_link_stats64 *stats) | ||
102 | { | 103 | { |
103 | struct net_bridge *br = netdev_priv(dev); | 104 | struct net_bridge *br = netdev_priv(dev); |
104 | struct rtnl_link_stats64 *stats = &dev->stats64; | ||
105 | struct br_cpu_netstats tmp, sum = { 0 }; | 105 | struct br_cpu_netstats tmp, sum = { 0 }; |
106 | unsigned int cpu; | 106 | unsigned int cpu; |
107 | 107 | ||
diff --git a/net/core/dev.c b/net/core/dev.c index 93b8929fa21d..92482d7a87a9 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -3703,7 +3703,8 @@ void dev_seq_stop(struct seq_file *seq, void *v) | |||
3703 | 3703 | ||
3704 | static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) | 3704 | static void dev_seq_printf_stats(struct seq_file *seq, struct net_device *dev) |
3705 | { | 3705 | { |
3706 | const struct rtnl_link_stats64 *stats = dev_get_stats(dev); | 3706 | struct rtnl_link_stats64 temp; |
3707 | const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); | ||
3707 | 3708 | ||
3708 | seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu " | 3709 | seq_printf(seq, "%6s: %7llu %7llu %4llu %4llu %4llu %5llu %10llu %9llu " |
3709 | "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n", | 3710 | "%8llu %7llu %4llu %4llu %4llu %5llu %7llu %10llu\n", |
@@ -5281,23 +5282,29 @@ EXPORT_SYMBOL(dev_txq_stats_fold); | |||
5281 | /** | 5282 | /** |
5282 | * dev_get_stats - get network device statistics | 5283 | * dev_get_stats - get network device statistics |
5283 | * @dev: device to get statistics from | 5284 | * @dev: device to get statistics from |
5285 | * @storage: place to store stats | ||
5284 | * | 5286 | * |
5285 | * Get network statistics from device. The device driver may provide | 5287 | * Get network statistics from device. The device driver may provide |
5286 | * its own method by setting dev->netdev_ops->get_stats64 or | 5288 | * its own method by setting dev->netdev_ops->get_stats64 or |
5287 | * dev->netdev_ops->get_stats; otherwise the internal statistics | 5289 | * dev->netdev_ops->get_stats; otherwise the internal statistics |
5288 | * structure is used. | 5290 | * structure is used. |
5289 | */ | 5291 | */ |
5290 | const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev) | 5292 | const struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, |
5293 | struct rtnl_link_stats64 *storage) | ||
5291 | { | 5294 | { |
5292 | const struct net_device_ops *ops = dev->netdev_ops; | 5295 | const struct net_device_ops *ops = dev->netdev_ops; |
5293 | 5296 | ||
5294 | if (ops->ndo_get_stats64) | 5297 | if (ops->ndo_get_stats64) { |
5295 | return ops->ndo_get_stats64(dev); | 5298 | memset(storage, 0, sizeof(*storage)); |
5296 | if (ops->ndo_get_stats) | 5299 | return ops->ndo_get_stats64(dev, storage); |
5297 | return (struct rtnl_link_stats64 *)ops->ndo_get_stats(dev); | 5300 | } |
5298 | 5301 | if (ops->ndo_get_stats) { | |
5299 | dev_txq_stats_fold(dev, &dev->stats); | 5302 | memcpy(storage, ops->ndo_get_stats(dev), sizeof(*storage)); |
5300 | return &dev->stats64; | 5303 | return storage; |
5304 | } | ||
5305 | memcpy(storage, &dev->stats, sizeof(*storage)); | ||
5306 | dev_txq_stats_fold(dev, (struct net_device_stats *)storage); | ||
5307 | return storage; | ||
5301 | } | 5308 | } |
5302 | EXPORT_SYMBOL(dev_get_stats); | 5309 | EXPORT_SYMBOL(dev_get_stats); |
5303 | 5310 | ||
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index ea3bb4c3b87d..914f42b0f039 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c | |||
@@ -330,7 +330,9 @@ static ssize_t netstat_show(const struct device *d, | |||
330 | 330 | ||
331 | read_lock(&dev_base_lock); | 331 | read_lock(&dev_base_lock); |
332 | if (dev_isalive(dev)) { | 332 | if (dev_isalive(dev)) { |
333 | const struct rtnl_link_stats64 *stats = dev_get_stats(dev); | 333 | struct rtnl_link_stats64 temp; |
334 | const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); | ||
335 | |||
334 | ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *) stats) + offset)); | 336 | ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *) stats) + offset)); |
335 | } | 337 | } |
336 | read_unlock(&dev_base_lock); | 338 | read_unlock(&dev_base_lock); |
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e645778e9b7e..5e773ea2201d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -791,6 +791,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
791 | { | 791 | { |
792 | struct ifinfomsg *ifm; | 792 | struct ifinfomsg *ifm; |
793 | struct nlmsghdr *nlh; | 793 | struct nlmsghdr *nlh; |
794 | struct rtnl_link_stats64 temp; | ||
794 | const struct rtnl_link_stats64 *stats; | 795 | const struct rtnl_link_stats64 *stats; |
795 | struct nlattr *attr; | 796 | struct nlattr *attr; |
796 | 797 | ||
@@ -847,7 +848,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, | |||
847 | if (attr == NULL) | 848 | if (attr == NULL) |
848 | goto nla_put_failure; | 849 | goto nla_put_failure; |
849 | 850 | ||
850 | stats = dev_get_stats(dev); | 851 | stats = dev_get_stats(dev, &temp); |
851 | copy_rtnl_link_stats(nla_data(attr), stats); | 852 | copy_rtnl_link_stats(nla_data(attr), stats); |
852 | 853 | ||
853 | attr = nla_reserve(skb, IFLA_STATS64, | 854 | attr = nla_reserve(skb, IFLA_STATS64, |