aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-05-18 21:14:23 -0400
committerDavid S. Miller <davem@davemloft.net>2011-05-19 16:21:22 -0400
commitbe281e554e2a4cf2478df7a8b8926c89454bccfa (patch)
tree7e80e5eb0aa76efcdb39a06413ff9e0ae73de35a
parent75e308c894c4a5e47c005b8e821ae5f539ad2ef3 (diff)
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 <eric.dumazet@gmail.com> CC: Denys Fedoryshchenko <denys@visp.net.lb> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/if_inet6.h4
-rw-r--r--include/net/ipv6.h19
-rw-r--r--include/net/snmp.h14
-rw-r--r--net/ipv6/addrconf.c24
-rw-r--r--net/ipv6/proc.c40
5 files changed, 68 insertions, 33 deletions
diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h
index 0c603fe65377..11cf373970a9 100644
--- a/include/net/if_inet6.h
+++ b/include/net/if_inet6.h
@@ -154,8 +154,8 @@ struct ifacaddr6 {
154struct ipv6_devstat { 154struct ipv6_devstat {
155 struct proc_dir_entry *proc_dir_entry; 155 struct proc_dir_entry *proc_dir_entry;
156 DEFINE_SNMP_STAT(struct ipstats_mib, ipv6); 156 DEFINE_SNMP_STAT(struct ipstats_mib, ipv6);
157 DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6); 157 DEFINE_SNMP_STAT_ATOMIC(struct icmpv6_mib_device, icmpv6dev);
158 DEFINE_SNMP_STAT(struct icmpv6msg_mib, icmpv6msg); 158 DEFINE_SNMP_STAT_ATOMIC(struct icmpv6msg_mib_device, icmpv6msgdev);
159}; 159};
160 160
161struct inet6_dev { 161struct inet6_dev {
diff --git a/include/net/ipv6.h b/include/net/ipv6.h
index e1c60b43e73b..c033ed00df7d 100644
--- a/include/net/ipv6.h
+++ b/include/net/ipv6.h
@@ -123,6 +123,15 @@ extern struct ctl_path net_ipv6_ctl_path[];
123 SNMP_INC_STATS##modifier((net)->mib.statname##_statistics, (field));\ 123 SNMP_INC_STATS##modifier((net)->mib.statname##_statistics, (field));\
124}) 124})
125 125
126/* per device counters are atomic_long_t */
127#define _DEVINCATOMIC(net, statname, modifier, idev, field) \
128({ \
129 struct inet6_dev *_idev = (idev); \
130 if (likely(_idev != NULL)) \
131 SNMP_INC_STATS_ATOMIC_LONG((_idev)->stats.statname##dev, (field)); \
132 SNMP_INC_STATS##modifier((net)->mib.statname##_statistics, (field));\
133})
134
126#define _DEVADD(net, statname, modifier, idev, field, val) \ 135#define _DEVADD(net, statname, modifier, idev, field, val) \
127({ \ 136({ \
128 struct inet6_dev *_idev = (idev); \ 137 struct inet6_dev *_idev = (idev); \
@@ -154,16 +163,16 @@ extern struct ctl_path net_ipv6_ctl_path[];
154#define IP6_UPD_PO_STATS_BH(net, idev,field,val) \ 163#define IP6_UPD_PO_STATS_BH(net, idev,field,val) \
155 _DEVUPD(net, ipv6, 64_BH, idev, field, val) 164 _DEVUPD(net, ipv6, 64_BH, idev, field, val)
156#define ICMP6_INC_STATS(net, idev, field) \ 165#define ICMP6_INC_STATS(net, idev, field) \
157 _DEVINC(net, icmpv6, , idev, field) 166 _DEVINCATOMIC(net, icmpv6, , idev, field)
158#define ICMP6_INC_STATS_BH(net, idev, field) \ 167#define ICMP6_INC_STATS_BH(net, idev, field) \
159 _DEVINC(net, icmpv6, _BH, idev, field) 168 _DEVINCATOMIC(net, icmpv6, _BH, idev, field)
160 169
161#define ICMP6MSGOUT_INC_STATS(net, idev, field) \ 170#define ICMP6MSGOUT_INC_STATS(net, idev, field) \
162 _DEVINC(net, icmpv6msg, , idev, field +256) 171 _DEVINCATOMIC(net, icmpv6msg, , idev, field +256)
163#define ICMP6MSGOUT_INC_STATS_BH(net, idev, field) \ 172#define ICMP6MSGOUT_INC_STATS_BH(net, idev, field) \
164 _DEVINC(net, icmpv6msg, _BH, idev, field +256) 173 _DEVINCATOMIC(net, icmpv6msg, _BH, idev, field +256)
165#define ICMP6MSGIN_INC_STATS_BH(net, idev, field) \ 174#define ICMP6MSGIN_INC_STATS_BH(net, idev, field) \
166 _DEVINC(net, icmpv6msg, _BH, idev, field) 175 _DEVINCATOMIC(net, icmpv6msg, _BH, idev, field)
167 176
168struct ip6_ra_chain { 177struct ip6_ra_chain {
169 struct ip6_ra_chain *next; 178 struct ip6_ra_chain *next;
diff --git a/include/net/snmp.h b/include/net/snmp.h
index 27461d6dd46f..479083a78b0c 100644
--- a/include/net/snmp.h
+++ b/include/net/snmp.h
@@ -72,14 +72,24 @@ struct icmpmsg_mib {
72 72
73/* ICMP6 (IPv6-ICMP) */ 73/* ICMP6 (IPv6-ICMP) */
74#define ICMP6_MIB_MAX __ICMP6_MIB_MAX 74#define ICMP6_MIB_MAX __ICMP6_MIB_MAX
75/* per network ns counters */
75struct icmpv6_mib { 76struct icmpv6_mib {
76 unsigned long mibs[ICMP6_MIB_MAX]; 77 unsigned long mibs[ICMP6_MIB_MAX];
77}; 78};
79/* per device counters, (shared on all cpus) */
80struct icmpv6_mib_device {
81 atomic_long_t mibs[ICMP6_MIB_MAX];
82};
78 83
79#define ICMP6MSG_MIB_MAX __ICMP6MSG_MIB_MAX 84#define ICMP6MSG_MIB_MAX __ICMP6MSG_MIB_MAX
85/* per network ns counters */
80struct icmpv6msg_mib { 86struct icmpv6msg_mib {
81 unsigned long mibs[ICMP6MSG_MIB_MAX]; 87 unsigned long mibs[ICMP6MSG_MIB_MAX];
82}; 88};
89/* per device counters, (shared on all cpus) */
90struct icmpv6msg_mib_device {
91 atomic_long_t mibs[ICMP6MSG_MIB_MAX];
92};
83 93
84 94
85/* TCP */ 95/* TCP */
@@ -114,6 +124,8 @@ struct linux_xfrm_mib {
114 */ 124 */
115#define DEFINE_SNMP_STAT(type, name) \ 125#define DEFINE_SNMP_STAT(type, name) \
116 __typeof__(type) __percpu *name[2] 126 __typeof__(type) __percpu *name[2]
127#define DEFINE_SNMP_STAT_ATOMIC(type, name) \
128 __typeof__(type) *name
117#define DECLARE_SNMP_STAT(type, name) \ 129#define DECLARE_SNMP_STAT(type, name) \
118 extern __typeof__(type) __percpu *name[2] 130 extern __typeof__(type) __percpu *name[2]
119 131
@@ -124,6 +136,8 @@ struct linux_xfrm_mib {
124 __this_cpu_inc(mib[0]->mibs[field]) 136 __this_cpu_inc(mib[0]->mibs[field])
125#define SNMP_INC_STATS_USER(mib, field) \ 137#define SNMP_INC_STATS_USER(mib, field) \
126 this_cpu_inc(mib[1]->mibs[field]) 138 this_cpu_inc(mib[1]->mibs[field])
139#define SNMP_INC_STATS_ATOMIC_LONG(mib, field) \
140 atomic_long_inc(&mib->mibs[field])
127#define SNMP_INC_STATS(mib, field) \ 141#define SNMP_INC_STATS(mib, field) \
128 this_cpu_inc(mib[!in_softirq()]->mibs[field]) 142 this_cpu_inc(mib[!in_softirq()]->mibs[field])
129#define SNMP_DEC_STATS(mib, field) \ 143#define SNMP_DEC_STATS(mib, field) \
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)
289 sizeof(struct ipstats_mib), 289 sizeof(struct ipstats_mib),
290 __alignof__(struct ipstats_mib)) < 0) 290 __alignof__(struct ipstats_mib)) < 0)
291 goto err_ip; 291 goto err_ip;
292 if (snmp_mib_init((void __percpu **)idev->stats.icmpv6, 292 idev->stats.icmpv6dev = kzalloc(sizeof(struct icmpv6_mib_device),
293 sizeof(struct icmpv6_mib), 293 GFP_KERNEL);
294 __alignof__(struct icmpv6_mib)) < 0) 294 if (!idev->stats.icmpv6dev)
295 goto err_icmp; 295 goto err_icmp;
296 if (snmp_mib_init((void __percpu **)idev->stats.icmpv6msg, 296 idev->stats.icmpv6msgdev = kzalloc(sizeof(struct icmpv6msg_mib_device),
297 sizeof(struct icmpv6msg_mib), 297 GFP_KERNEL);
298 __alignof__(struct icmpv6msg_mib)) < 0) 298 if (!idev->stats.icmpv6msgdev)
299 goto err_icmpmsg; 299 goto err_icmpmsg;
300 300
301 return 0; 301 return 0;
302 302
303err_icmpmsg: 303err_icmpmsg:
304 snmp_mib_free((void __percpu **)idev->stats.icmpv6); 304 kfree(idev->stats.icmpv6dev);
305err_icmp: 305err_icmp:
306 snmp_mib_free((void __percpu **)idev->stats.ipv6); 306 snmp_mib_free((void __percpu **)idev->stats.ipv6);
307err_ip: 307err_ip:
@@ -310,8 +310,8 @@ err_ip:
310 310
311static void snmp6_free_dev(struct inet6_dev *idev) 311static void snmp6_free_dev(struct inet6_dev *idev)
312{ 312{
313 snmp_mib_free((void __percpu **)idev->stats.icmpv6msg); 313 kfree(idev->stats.icmpv6msgdev);
314 snmp_mib_free((void __percpu **)idev->stats.icmpv6); 314 kfree(idev->stats.icmpv6dev);
315 snmp_mib_free((void __percpu **)idev->stats.ipv6); 315 snmp_mib_free((void __percpu **)idev->stats.ipv6);
316} 316}
317 317
@@ -3838,7 +3838,7 @@ static inline size_t inet6_if_nlmsg_size(void)
3838 + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */ 3838 + nla_total_size(inet6_ifla6_size()); /* IFLA_PROTINFO */
3839} 3839}
3840 3840
3841static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib, 3841static inline void __snmp6_fill_statsdev(u64 *stats, atomic_long_t *mib,
3842 int items, int bytes) 3842 int items, int bytes)
3843{ 3843{
3844 int i; 3844 int i;
@@ -3848,7 +3848,7 @@ static inline void __snmp6_fill_stats(u64 *stats, void __percpu **mib,
3848 /* Use put_unaligned() because stats may not be aligned for u64. */ 3848 /* Use put_unaligned() because stats may not be aligned for u64. */
3849 put_unaligned(items, &stats[0]); 3849 put_unaligned(items, &stats[0]);
3850 for (i = 1; i < items; i++) 3850 for (i = 1; i < items; i++)
3851 put_unaligned(snmp_fold_field(mib, i), &stats[i]); 3851 put_unaligned(atomic_long_read(&mib[i]), &stats[i]);
3852 3852
3853 memset(&stats[items], 0, pad); 3853 memset(&stats[items], 0, pad);
3854} 3854}
@@ -3877,7 +3877,7 @@ static void snmp6_fill_stats(u64 *stats, struct inet6_dev *idev, int attrtype,
3877 IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp)); 3877 IPSTATS_MIB_MAX, bytes, offsetof(struct ipstats_mib, syncp));
3878 break; 3878 break;
3879 case IFLA_INET6_ICMP6STATS: 3879 case IFLA_INET6_ICMP6STATS:
3880 __snmp6_fill_stats(stats, (void __percpu **)idev->stats.icmpv6, ICMP6_MIB_MAX, bytes); 3880 __snmp6_fill_statsdev(stats, idev->stats.icmpv6dev->mibs, ICMP6_MIB_MAX, bytes);
3881 break; 3881 break;
3882 } 3882 }
3883} 3883}
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[] = {
141 SNMP_MIB_SENTINEL 141 SNMP_MIB_SENTINEL
142}; 142};
143 143
144static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib) 144/* can be called either with percpu mib (pcpumib != NULL),
145 * or shared one (smib != NULL)
146 */
147static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **pcpumib,
148 atomic_long_t *smib)
145{ 149{
146 char name[32]; 150 char name[32];
147 int i; 151 int i;
@@ -158,14 +162,14 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib)
158 snprintf(name, sizeof(name), "Icmp6%s%s", 162 snprintf(name, sizeof(name), "Icmp6%s%s",
159 i & 0x100 ? "Out" : "In", p); 163 i & 0x100 ? "Out" : "In", p);
160 seq_printf(seq, "%-32s\t%lu\n", name, 164 seq_printf(seq, "%-32s\t%lu\n", name,
161 snmp_fold_field(mib, i)); 165 pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i));
162 } 166 }
163 167
164 /* print by number (nonzero only) - ICMPMsgStat format */ 168 /* print by number (nonzero only) - ICMPMsgStat format */
165 for (i = 0; i < ICMP6MSG_MIB_MAX; i++) { 169 for (i = 0; i < ICMP6MSG_MIB_MAX; i++) {
166 unsigned long val; 170 unsigned long val;
167 171
168 val = snmp_fold_field(mib, i); 172 val = pcpumib ? snmp_fold_field(pcpumib, i) : atomic_long_read(smib + i);
169 if (!val) 173 if (!val)
170 continue; 174 continue;
171 snprintf(name, sizeof(name), "Icmp6%sType%u", 175 snprintf(name, sizeof(name), "Icmp6%sType%u",
@@ -174,14 +178,22 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void __percpu **mib)
174 } 178 }
175} 179}
176 180
177static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **mib, 181/* can be called either with percpu mib (pcpumib != NULL),
182 * or shared one (smib != NULL)
183 */
184static void snmp6_seq_show_item(struct seq_file *seq, void __percpu **pcpumib,
185 atomic_long_t *smib,
178 const struct snmp_mib *itemlist) 186 const struct snmp_mib *itemlist)
179{ 187{
180 int i; 188 int i;
189 unsigned long val;
181 190
182 for (i = 0; itemlist[i].name; i++) 191 for (i = 0; itemlist[i].name; i++) {
183 seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, 192 val = pcpumib ?
184 snmp_fold_field(mib, itemlist[i].entry)); 193 snmp_fold_field(pcpumib, itemlist[i].entry) :
194 atomic_long_read(smib + itemlist[i].entry);
195 seq_printf(seq, "%-32s\t%lu\n", itemlist[i].name, val);
196 }
185} 197}
186 198
187static void snmp6_seq_show_item64(struct seq_file *seq, void __percpu **mib, 199static 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)
201 snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics, 213 snmp6_seq_show_item64(seq, (void __percpu **)net->mib.ipv6_statistics,
202 snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp)); 214 snmp6_ipstats_list, offsetof(struct ipstats_mib, syncp));
203 snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics, 215 snmp6_seq_show_item(seq, (void __percpu **)net->mib.icmpv6_statistics,
204 snmp6_icmp6_list); 216 NULL, snmp6_icmp6_list);
205 snmp6_seq_show_icmpv6msg(seq, 217 snmp6_seq_show_icmpv6msg(seq,
206 (void __percpu **)net->mib.icmpv6msg_statistics); 218 (void __percpu **)net->mib.icmpv6msg_statistics, NULL);
207 snmp6_seq_show_item(seq, (void __percpu **)net->mib.udp_stats_in6, 219 snmp6_seq_show_item(seq, (void __percpu **)net->mib.udp_stats_in6,
208 snmp6_udp6_list); 220 NULL, snmp6_udp6_list);
209 snmp6_seq_show_item(seq, (void __percpu **)net->mib.udplite_stats_in6, 221 snmp6_seq_show_item(seq, (void __percpu **)net->mib.udplite_stats_in6,
210 snmp6_udplite6_list); 222 NULL, snmp6_udplite6_list);
211 return 0; 223 return 0;
212} 224}
213 225
@@ -229,11 +241,11 @@ static int snmp6_dev_seq_show(struct seq_file *seq, void *v)
229 struct inet6_dev *idev = (struct inet6_dev *)seq->private; 241 struct inet6_dev *idev = (struct inet6_dev *)seq->private;
230 242
231 seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex); 243 seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
232 snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, 244 snmp6_seq_show_item(seq, (void __percpu **)idev->stats.ipv6, NULL,
233 snmp6_ipstats_list); 245 snmp6_ipstats_list);
234 snmp6_seq_show_item(seq, (void __percpu **)idev->stats.icmpv6, 246 snmp6_seq_show_item(seq, NULL, idev->stats.icmpv6dev->mibs,
235 snmp6_icmp6_list); 247 snmp6_icmp6_list);
236 snmp6_seq_show_icmpv6msg(seq, (void __percpu **)idev->stats.icmpv6msg); 248 snmp6_seq_show_icmpv6msg(seq, NULL, idev->stats.icmpv6msgdev->mibs);
237 return 0; 249 return 0;
238} 250}
239 251