aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_fib.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r--net/ipv6/ip6_fib.c53
1 files changed, 35 insertions, 18 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 0e93ca56eb69..6b82e02158c6 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -26,6 +26,7 @@
26#include <linux/in6.h> 26#include <linux/in6.h>
27#include <linux/init.h> 27#include <linux/init.h>
28#include <linux/list.h> 28#include <linux/list.h>
29#include <linux/slab.h>
29 30
30#ifdef CONFIG_PROC_FS 31#ifdef CONFIG_PROC_FS
31#include <linux/proc_fs.h> 32#include <linux/proc_fs.h>
@@ -93,29 +94,20 @@ static __u32 rt_sernum;
93 94
94static void fib6_gc_timer_cb(unsigned long arg); 95static void fib6_gc_timer_cb(unsigned long arg);
95 96
96static struct fib6_walker_t fib6_walker_list = { 97static LIST_HEAD(fib6_walkers);
97 .prev = &fib6_walker_list, 98#define FOR_WALKERS(w) list_for_each_entry(w, &fib6_walkers, lh)
98 .next = &fib6_walker_list,
99};
100
101#define FOR_WALKERS(w) for ((w)=fib6_walker_list.next; (w) != &fib6_walker_list; (w)=(w)->next)
102 99
103static inline void fib6_walker_link(struct fib6_walker_t *w) 100static inline void fib6_walker_link(struct fib6_walker_t *w)
104{ 101{
105 write_lock_bh(&fib6_walker_lock); 102 write_lock_bh(&fib6_walker_lock);
106 w->next = fib6_walker_list.next; 103 list_add(&w->lh, &fib6_walkers);
107 w->prev = &fib6_walker_list;
108 w->next->prev = w;
109 w->prev->next = w;
110 write_unlock_bh(&fib6_walker_lock); 104 write_unlock_bh(&fib6_walker_lock);
111} 105}
112 106
113static inline void fib6_walker_unlink(struct fib6_walker_t *w) 107static inline void fib6_walker_unlink(struct fib6_walker_t *w)
114{ 108{
115 write_lock_bh(&fib6_walker_lock); 109 write_lock_bh(&fib6_walker_lock);
116 w->next->prev = w->prev; 110 list_del(&w->lh);
117 w->prev->next = w->next;
118 w->prev = w->next = w;
119 write_unlock_bh(&fib6_walker_lock); 111 write_unlock_bh(&fib6_walker_lock);
120} 112}
121static __inline__ u32 fib6_new_sernum(void) 113static __inline__ u32 fib6_new_sernum(void)
@@ -239,7 +231,7 @@ struct fib6_table *fib6_get_table(struct net *net, u32 id)
239 return NULL; 231 return NULL;
240} 232}
241 233
242static void fib6_tables_init(struct net *net) 234static void __net_init fib6_tables_init(struct net *net)
243{ 235{
244 fib6_link_table(net, net->ipv6.fib6_main_tbl); 236 fib6_link_table(net, net->ipv6.fib6_main_tbl);
245 fib6_link_table(net, net->ipv6.fib6_local_tbl); 237 fib6_link_table(net, net->ipv6.fib6_local_tbl);
@@ -262,7 +254,7 @@ struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi *fl,
262 return (struct dst_entry *) lookup(net, net->ipv6.fib6_main_tbl, fl, flags); 254 return (struct dst_entry *) lookup(net, net->ipv6.fib6_main_tbl, fl, flags);
263} 255}
264 256
265static void fib6_tables_init(struct net *net) 257static void __net_init fib6_tables_init(struct net *net)
266{ 258{
267 fib6_link_table(net, net->ipv6.fib6_main_tbl); 259 fib6_link_table(net, net->ipv6.fib6_main_tbl);
268} 260}
@@ -319,12 +311,26 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
319 w->root = &table->tb6_root; 311 w->root = &table->tb6_root;
320 312
321 if (cb->args[4] == 0) { 313 if (cb->args[4] == 0) {
314 w->count = 0;
315 w->skip = 0;
316
322 read_lock_bh(&table->tb6_lock); 317 read_lock_bh(&table->tb6_lock);
323 res = fib6_walk(w); 318 res = fib6_walk(w);
324 read_unlock_bh(&table->tb6_lock); 319 read_unlock_bh(&table->tb6_lock);
325 if (res > 0) 320 if (res > 0) {
326 cb->args[4] = 1; 321 cb->args[4] = 1;
322 cb->args[5] = w->root->fn_sernum;
323 }
327 } else { 324 } else {
325 if (cb->args[5] != w->root->fn_sernum) {
326 /* Begin at the root if the tree changed */
327 cb->args[5] = w->root->fn_sernum;
328 w->state = FWS_INIT;
329 w->node = w->root;
330 w->skip = w->count;
331 } else
332 w->skip = 0;
333
328 read_lock_bh(&table->tb6_lock); 334 read_lock_bh(&table->tb6_lock);
329 res = fib6_walk_continue(w); 335 res = fib6_walk_continue(w);
330 read_unlock_bh(&table->tb6_lock); 336 read_unlock_bh(&table->tb6_lock);
@@ -1250,9 +1256,18 @@ static int fib6_walk_continue(struct fib6_walker_t *w)
1250 w->leaf = fn->leaf; 1256 w->leaf = fn->leaf;
1251 case FWS_C: 1257 case FWS_C:
1252 if (w->leaf && fn->fn_flags&RTN_RTINFO) { 1258 if (w->leaf && fn->fn_flags&RTN_RTINFO) {
1253 int err = w->func(w); 1259 int err;
1260
1261 if (w->count < w->skip) {
1262 w->count++;
1263 continue;
1264 }
1265
1266 err = w->func(w);
1254 if (err) 1267 if (err)
1255 return err; 1268 return err;
1269
1270 w->count++;
1256 continue; 1271 continue;
1257 } 1272 }
1258 w->state = FWS_U; 1273 w->state = FWS_U;
@@ -1346,6 +1361,8 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root,
1346 c.w.root = root; 1361 c.w.root = root;
1347 c.w.func = fib6_clean_node; 1362 c.w.func = fib6_clean_node;
1348 c.w.prune = prune; 1363 c.w.prune = prune;
1364 c.w.count = 0;
1365 c.w.skip = 0;
1349 c.func = func; 1366 c.func = func;
1350 c.arg = arg; 1367 c.arg = arg;
1351 c.net = net; 1368 c.net = net;
@@ -1469,7 +1486,7 @@ static void fib6_gc_timer_cb(unsigned long arg)
1469 fib6_run_gc(0, (struct net *)arg); 1486 fib6_run_gc(0, (struct net *)arg);
1470} 1487}
1471 1488
1472static int fib6_net_init(struct net *net) 1489static int __net_init fib6_net_init(struct net *net)
1473{ 1490{
1474 setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net); 1491 setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net);
1475 1492