From be281e554e2a4cf2478df7a8b8926c89454bccfa Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 19 May 2011 01:14:23 +0000 Subject: ipv6: reduce per device ICMP mib sizes ipv6 has per device ICMP SNMP counters, taking too much space because they use percpu storage. needed size per device is : (512+4)*sizeof(long)*number_of_possible_cpus*2 On a 32bit kernel, 16 possible cpus, this wastes more than 64kbytes of memory per ipv6 enabled network device, taken in vmalloc pool. Since ICMP messages are rare, just use shared counters (atomic_long_t) Per network space ICMP counters are still using percpu memory, we might also convert them to shared counters in a future patch. Signed-off-by: Eric Dumazet CC: Denys Fedoryshchenko Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 24 ++++++++++++------------ net/ipv6/proc.c | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 26 deletions(-) (limited to 'net/ipv6') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f2f9b2e3cfe9..3cfbbf3387a0 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -289,19 +289,19 @@ static int snmp6_alloc_dev(struct inet6_dev *idev) sizeof(struct ipstats_mib), __alignof__(struct ipstats_mib)) < 0) goto err_ip; - if (snmp_mib_init((void __percpu **)idev->stats.icmpv6, - sizeof(struct icmpv6_mib), - __alignof__(struct icmpv6_mib)) < 0) + idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6dev) goto err_icmp; - if (snmp_mib_init((void __percpu **)idev->stats.icmpv6msg, - sizeof(struct icmpv6msg_mib), - __alignof__(struct icmpv6msg_mib)) < 0) + idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device), + GFP_KERNEL); + if (!idev->stats.icmpv6msgdev) goto err_icmpmsg; return 0; err_icmpmsg: - snmp_mib_free((void __percpu **)idev->stats.icmpv6); + kfree(idev->stats.icmpv6dev); err_icmp: snmp_mib_free((void __percpu **)idev->stats.ipv6); err_ip: @@ -310,8 +310,8 @@ err_ip: static void snmp6_free_dev(struct inet6_dev *idev) { - snmp_mib_free((void __percpu **)idev->stats.icmpv6msg); - snmp_mib_free((void __percpu **)idev->stats.icmpv6); + kfree(idev->stats.icmpv6msgdev); + kfree(idev->stats.icmpv6dev); snmp_mib_free((void __percpu **)idev->stats.ipv6); } @@ -3838,7 +3838,7 @@ static inline size_t inet6_if_nlmsg_size(void) + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */ } -static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, +static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib, int items, int bytes) { int i; @@ -3848,7 +3848,7 @@ static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, /* Use put_unaligned() because stats may not be aligned for u64. */ put_unaligned(items, &stats[0]); for (i = 1; i < items; i++) - put_unaligned(snmp_fold_field(mib, i), &stats[i]); + put_unaligned(atomic_long_read(&mib[i]), &stats[i]); memset(&stats[items], 0, pad); } @@ -3877,7 +3877,7 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype, IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp)); break; case IFLA_INET6_ICMP6STATS: - __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes); + __snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, ICMP6_MIB_MAX, bytes); break; } } diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c index 24b3558b8e67..18ff5df7ec02 100644 --- a/net/ipv6/proc.c +++ b/net/ipv6/proc.c @@ -141,7 +141,11 @@ static const struct snmp_mib snmp6_udplite6_list[] = { SNMP_MIB_SENTINEL }; -static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib) +/* can be called either with percpu mib (pcpumib != NULL), + * or shared one (smib != NULL) + */ +static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **pcpumib, + atomic_long_t *smib) { char name[32]; int i; @@ -158,14 +162,14 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib) snprintf(name, sizeof(name), "Icmp6%s%s", i & 0x100 ? "Out" : "In", p); seq_printf(seq, "%-32s\t%lu\n", name, - snmp_fold_field(mib, i)); + pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i)); } /* print by number (nonzero only) - ICMPMsgStat format */ for (i = 0; i < ICMP6MSG_MIB_MAX; i++) { unsigned long val; - val = snmp_fold_field(mib, i); + val = pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i); if (!val) continue; snprintf(name, sizeof(name), "Icmp6%sType%u", @@ -174,14 +178,22 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib) } } -static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **mib, +/* can be called either with percpu mib (pcpumib != NULL), + * or shared one (smib != NULL) + */ +static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib, + atomic_long_t *smib, const struct snmp_mib *itemlist) { int i; + unsigned long val; - for (i = 0; itemlist[i].name; i++) - seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, - snmp_fold_field(mib, itemlist[i].entry)); + for (i = 0; itemlist[i].name; i++) { + val = pcpumib ? + snmp_fold_field(pcpumib, itemlist[i].entry) : + atomic_long_read(smib + itemlist[i].entry); + seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, val); + } } static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib, @@ -201,13 +213,13 @@ static int snmp6_seq_show(struct seq_file *seq, void *v) snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics, snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp)); snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics, - snmp6_icmp6_list); + NULL, snmp6_icmp6_list); snmp6_seq_show_icmpv6msg(seq, - (void __percpu **)net->mib.icmpv6msg_statistics); + (void __percpu **)net->mib.icmpv6msg_statistics, NULL); snmp6_seq_show_item(seq, (void __percpu **)net->mib.udp_stats_in6, - snmp6_udp6_list); + NULL, snmp6_udp6_list); snmp6_seq_show_item(seq, (void __percpu **)net->mib.udplite_stats_in6, - snmp6_udplite6_list); + NULL, snmp6_udplite6_list); return 0; } @@ -229,11 +241,11 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v) struct inet6_dev *idev = (struct inet6_dev *)seq->private; seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); - snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, + snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, NULL, snmp6_ipstats_list); - snmp6_seq_show_item(seq, (void __percpu **)idev->stats.icmpv6, + snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs, snmp6_icmp6_list); - snmp6_seq_show_icmpv6msg(seq, (void __percpu **)idev->stats.icmpv6msg); + snmp6_seq_show_icmpv6msg(seq, NULL, idev->stats.icmpv6msgdev->mibs); return 0; } -- cgit v1.2.3-58-ga151