aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2010-06-30 16:31:19 -0400
committerDavid S. Miller <davem@davemloft.net>2010-06-30 16:31:19 -0400
commit4ce3c183fcade7f4b30a33dae90cd774c3d9e094 (patch)
tree84d5a130da50096fdbeb7fffea596dffe2cebd80 /net
parentf3eb62d2cc7da7bea4b394dd06f6bc738aa284e7 (diff)
snmp: 64bit ipstats_mib for all arches
/proc/net/snmp and /proc/net/netstat expose SNMP counters. Width of these counters is either 32 or 64 bits, depending on the size of "unsigned long" in kernel. This means user program parsing these files must already be prepared to deal with 64bit values, regardless of user program being 32 or 64 bit. This patch introduces 64bit snmp values for IPSTAT mib, where some counters can wrap pretty fast if they are 32bit wide. # netstat -s|egrep "InOctets|OutOctets" InOctets: 244068329096 OutOctets: 244069348848 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/ipv4/af_inet.c36
-rw-r--r--net/ipv4/proc.c15
-rw-r--r--net/ipv6/addrconf.c18
-rw-r--r--net/ipv6/proc.c17
4 files changed, 76 insertions, 10 deletions
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 640db9b90330..3ceb025b16f2 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1427,6 +1427,42 @@ unsigned long snmp_fold_field(void __percpu *mib[], int offt)
1427} 1427}
1428EXPORT_SYMBOL_GPL(snmp_fold_field); 1428EXPORT_SYMBOL_GPL(snmp_fold_field);
1429 1429
1430#if BITS_PER_LONG==32
1431
1432u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_offset)
1433{
1434 u64 res = 0;
1435 int cpu;
1436
1437 for_each_possible_cpu(cpu) {
1438 void *bhptr, *userptr;
1439 struct u64_stats_sync *syncp;
1440 u64 v_bh, v_user;
1441 unsigned int start;
1442
1443 /* first mib used by softirq context, we must use _bh() accessors */
1444 bhptr = per_cpu_ptr(SNMP_STAT_BHPTR(mib), cpu);
1445 syncp = (struct u64_stats_sync *)(bhptr + syncp_offset);
1446 do {
1447 start = u64_stats_fetch_begin_bh(syncp);
1448 v_bh = *(((u64 *) bhptr) + offt);
1449 } while (u64_stats_fetch_retry_bh(syncp, start));
1450
1451 /* second mib used in USER context */
1452 userptr = per_cpu_ptr(SNMP_STAT_USRPTR(mib), cpu);
1453 syncp = (struct u64_stats_sync *)(userptr + syncp_offset);
1454 do {
1455 start = u64_stats_fetch_begin(syncp);
1456 v_user = *(((u64 *) userptr) + offt);
1457 } while (u64_stats_fetch_retry(syncp, start));
1458
1459 res += v_bh + v_user;
1460 }
1461 return res;
1462}
1463EXPORT_SYMBOL_GPL(snmp_fold_field64);
1464#endif
1465
1430int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align) 1466int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align)
1431{ 1467{
1432 BUG_ON(ptr == NULL); 1468 BUG_ON(ptr == NULL);
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index e320ca6b3ef3..4ae1f203f7cb 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -343,10 +343,12 @@ static int snmp_seq_show(struct seq_file *seq, void *v)
343 IPV4_DEVCONF_ALL(net, FORWARDING) ? 1 : 2, 343 IPV4_DEVCONF_ALL(net, FORWARDING) ? 1 : 2,
344 sysctl_ip_default_ttl); 344 sysctl_ip_default_ttl);
345 345
346 BUILD_BUG_ON(offsetof(struct ipstats_mib, mibs) != 0);
346 for (i = 0; snmp4_ipstats_list[i].name != NULL; i++) 347 for (i = 0; snmp4_ipstats_list[i].name != NULL; i++)
347 seq_printf(seq, " %lu", 348 seq_printf(seq, " %llu",
348 snmp_fold_field((void __percpu **)net->mib.ip_statistics, 349 snmp_fold_field64((void __percpu **)net->mib.ip_statistics,
349 snmp4_ipstats_list[i].entry)); 350 snmp4_ipstats_list[i].entry,
351 offsetof(struct ipstats_mib, syncp)));
350 352
351 icmp_put(seq); /* RFC 2011 compatibility */ 353 icmp_put(seq); /* RFC 2011 compatibility */
352 icmpmsg_put(seq); 354 icmpmsg_put(seq);
@@ -432,9 +434,10 @@ static int netstat_seq_show(struct seq_file *seq, void *v)
432 434
433 seq_puts(seq, "\nIpExt:"); 435 seq_puts(seq, "\nIpExt:");
434 for (i = 0; snmp4_ipextstats_list[i].name != NULL; i++) 436 for (i = 0; snmp4_ipextstats_list[i].name != NULL; i++)
435 seq_printf(seq, " %lu", 437 seq_printf(seq, " %llu",
436 snmp_fold_field((void __percpu **)net->mib.ip_statistics, 438 snmp_fold_field64((void __percpu **)net->mib.ip_statistics,
437 snmp4_ipextstats_list[i].entry)); 439 snmp4_ipextstats_list[i].entry,
440 offsetof(struct ipstats_mib, syncp)));
438 441
439 seq_putc(seq, '\n'); 442 seq_putc(seq, '\n');
440 return 0; 443 return 0;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 2514adf5251e..e81155d2f251 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3862,12 +3862,28 @@ static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib,
3862 memset(&stats[items], 0, pad); 3862 memset(&stats[items], 0, pad);
3863} 3863}
3864 3864
3865static inline void __snmp6_fill_stats64(u64 *stats, void __percpu **mib,
3866 int items, int bytes, size_t syncpoff)
3867{
3868 int i;
3869 int pad = bytes - sizeof(u64) * items;
3870 BUG_ON(pad < 0);
3871
3872 /* Use put_unaligned() because stats may not be aligned for u64. */
3873 put_unaligned(items, &stats[0]);
3874 for (i = 1; i < items; i++)
3875 put_unaligned(snmp_fold_field64(mib, i, syncpoff), &stats[i]);
3876
3877 memset(&stats[items], 0, pad);
3878}
3879
3865static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, 3880static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
3866 int bytes) 3881 int bytes)
3867{ 3882{
3868 switch (attrtype) { 3883 switch (attrtype) {
3869 case IFLA_INET6_STATS: 3884 case IFLA_INET6_STATS:
3870 __snmp6_fill_stats(stats, (void __percpu **)idev->stats.ipv6, IPSTATS_MIB_MAX, bytes); 3885 __snmp6_fill_stats64(stats, (void __percpu **)idev->stats.ipv6,
3886 IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp));
3871 break; 3887 break;
3872 case IFLA_INET6_ICMP6STATS: 3888 case IFLA_INET6_ICMP6STATS:
3873 __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes); 3889 __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes);
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index 566798d69f37..d082eaeefa25 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -174,17 +174,28 @@ static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **mib,
174 const struct snmp_mib *itemlist) 174 const struct snmp_mib *itemlist)
175{ 175{
176 int i; 176 int i;
177 for (i=0; itemlist[i].name; i++) 177
178 for (i = 0; itemlist[i].name; i++)
178 seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, 179 seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name,
179 snmp_fold_field(mib, itemlist[i].entry)); 180 snmp_fold_field(mib, itemlist[i].entry));
180} 181}
181 182
183static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib,
184 const struct snmp_mib *itemlist, size_t syncpoff)
185{
186 int i;
187
188 for (i = 0; itemlist[i].name; i++)
189 seq_printf(seq, "%-32s\t%llu\n", itemlist[i].name,
190 snmp_fold_field64(mib, itemlist[i].entry, syncpoff));
191}
192
182static int snmp6_seq_show(struct seq_file *seq, void *v) 193static int snmp6_seq_show(struct seq_file *seq, void *v)
183{ 194{
184 struct net *net = (struct net *)seq->private; 195 struct net *net = (struct net *)seq->private;
185 196
186 snmp6_seq_show_item(seq, (void __percpu **)net->mib.ipv6_statistics, 197 snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics,
187 snmp6_ipstats_list); 198 snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp));
188 snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics, 199 snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics,
189 snmp6_icmp6_list); 200 snmp6_icmp6_list);
190 snmp6_seq_show_icmpv6msg(seq, 201 snmp6_seq_show_icmpv6msg(seq,