aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichal Kubeček <mkubecek@suse.cz>2016-03-08 08:44:35 -0500
committerDavid S. Miller <davem@davemloft.net>2016-03-08 15:16:51 -0500
commit9a03cd8f38efb83c13fbe62aff50eea4efff93da (patch)
tree196c6d1b32fe34fa44f7bc9e164b6af2d0ff25ec
parent3570df914f9cae15df7f6ba472a51eaf798fdb46 (diff)
ipv6: per netns fib6 walkers
The IPv6 FIB data structures are separated per network namespace but there is still only one global walkers list and one global walker list lock. This means changes in one namespace unnecessarily interfere with walkers in other namespaces. Replace the global list with per-netns lists (and give each its own lock). Signed-off-by: Michal Kubecek <mkubecek@suse.cz> Reviewed-by: Cong Wang <xiyou.wangcong@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/net/netns/ipv6.h2
-rw-r--r--net/ipv6/ip6_fib.c68
2 files changed, 38 insertions, 32 deletions
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h
index c0368db6df54..f0109b973648 100644
--- a/include/net/netns/ipv6.h
+++ b/include/net/netns/ipv6.h
@@ -58,7 +58,9 @@ struct netns_ipv6 {
58 struct timer_list ip6_fib_timer; 58 struct timer_list ip6_fib_timer;
59 struct hlist_head *fib_table_hash; 59 struct hlist_head *fib_table_hash;
60 struct fib6_table *fib6_main_tbl; 60 struct fib6_table *fib6_main_tbl;
61 struct list_head fib6_walkers;
61 struct dst_ops ip6_dst_ops; 62 struct dst_ops ip6_dst_ops;
63 rwlock_t fib6_walker_lock;
62 unsigned int ip6_rt_gc_expire; 64 unsigned int ip6_rt_gc_expire;
63 unsigned long ip6_rt_last_gc; 65 unsigned long ip6_rt_last_gc;
64#ifdef CONFIG_IPV6_MULTIPLE_TABLES 66#ifdef CONFIG_IPV6_MULTIPLE_TABLES
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index d7c715accac9..883f2836beab 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -55,8 +55,6 @@ struct fib6_cleaner {
55 void *arg; 55 void *arg;
56}; 56};
57 57
58static DEFINE_RWLOCK(fib6_walker_lock);
59
60#ifdef CONFIG_IPV6_SUBTREES 58#ifdef CONFIG_IPV6_SUBTREES
61#define FWS_INIT FWS_S 59#define FWS_INIT FWS_S
62#else 60#else
@@ -66,7 +64,7 @@ static DEFINE_RWLOCK(fib6_walker_lock);
66static void fib6_prune_clones(struct net *net, struct fib6_node *fn); 64static void fib6_prune_clones(struct net *net, struct fib6_node *fn);
67static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn); 65static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn);
68static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn); 66static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn);
69static int fib6_walk(struct fib6_walker *w); 67static int fib6_walk(struct net *net, struct fib6_walker *w);
70static int fib6_walk_continue(struct fib6_walker *w); 68static int fib6_walk_continue(struct fib6_walker *w);
71 69
72/* 70/*
@@ -78,21 +76,21 @@ static int fib6_walk_continue(struct fib6_walker *w);
78 76
79static void fib6_gc_timer_cb(unsigned long arg); 77static void fib6_gc_timer_cb(unsigned long arg);
80 78
81static LIST_HEAD(fib6_walkers); 79#define FOR_WALKERS(net, w) \
82#define FOR_WALKERS(w) list_for_each_entry(w, &fib6_walkers, lh) 80 list_for_each_entry(w, &(net)->ipv6.fib6_walkers, lh)
83 81
84static void fib6_walker_link(struct fib6_walker *w) 82static void fib6_walker_link(struct net *net, struct fib6_walker *w)
85{ 83{
86 write_lock_bh(&fib6_walker_lock); 84 write_lock_bh(&net->ipv6.fib6_walker_lock);
87 list_add(&w->lh, &fib6_walkers); 85 list_add(&w->lh, &net->ipv6.fib6_walkers);
88 write_unlock_bh(&fib6_walker_lock); 86 write_unlock_bh(&net->ipv6.fib6_walker_lock);
89} 87}
90 88
91static void fib6_walker_unlink(struct fib6_walker *w) 89static void fib6_walker_unlink(struct net *net, struct fib6_walker *w)
92{ 90{
93 write_lock_bh(&fib6_walker_lock); 91 write_lock_bh(&net->ipv6.fib6_walker_lock);
94 list_del(&w->lh); 92 list_del(&w->lh);
95 write_unlock_bh(&fib6_walker_lock); 93 write_unlock_bh(&net->ipv6.fib6_walker_lock);
96} 94}
97 95
98static int fib6_new_sernum(struct net *net) 96static int fib6_new_sernum(struct net *net)
@@ -325,12 +323,13 @@ static int fib6_dump_node(struct fib6_walker *w)
325 323
326static void fib6_dump_end(struct netlink_callback *cb) 324static void fib6_dump_end(struct netlink_callback *cb)
327{ 325{
326 struct net *net = sock_net(cb->skb->sk);
328 struct fib6_walker *w = (void *)cb->args[2]; 327 struct fib6_walker *w = (void *)cb->args[2];
329 328
330 if (w) { 329 if (w) {
331 if (cb->args[4]) { 330 if (cb->args[4]) {
332 cb->args[4] = 0; 331 cb->args[4] = 0;
333 fib6_walker_unlink(w); 332 fib6_walker_unlink(net, w);
334 } 333 }
335 cb->args[2] = 0; 334 cb->args[2] = 0;
336 kfree(w); 335 kfree(w);
@@ -348,6 +347,7 @@ static int fib6_dump_done(struct netlink_callback *cb)
348static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb, 347static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
349 struct netlink_callback *cb) 348 struct netlink_callback *cb)
350{ 349{
350 struct net *net = sock_net(skb->sk);
351 struct fib6_walker *w; 351 struct fib6_walker *w;
352 int res; 352 int res;
353 353
@@ -359,7 +359,7 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
359 w->skip = 0; 359 w->skip = 0;
360 360
361 read_lock_bh(&table->tb6_lock); 361 read_lock_bh(&table->tb6_lock);
362 res = fib6_walk(w); 362 res = fib6_walk(net, w);
363 read_unlock_bh(&table->tb6_lock); 363 read_unlock_bh(&table->tb6_lock);
364 if (res > 0) { 364 if (res > 0) {
365 cb->args[4] = 1; 365 cb->args[4] = 1;
@@ -379,7 +379,7 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
379 res = fib6_walk_continue(w); 379 res = fib6_walk_continue(w);
380 read_unlock_bh(&table->tb6_lock); 380 read_unlock_bh(&table->tb6_lock);
381 if (res <= 0) { 381 if (res <= 0) {
382 fib6_walker_unlink(w); 382 fib6_walker_unlink(net, w);
383 cb->args[4] = 0; 383 cb->args[4] = 0;
384 } 384 }
385 } 385 }
@@ -1340,8 +1340,8 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
1340 } 1340 }
1341#endif 1341#endif
1342 1342
1343 read_lock(&fib6_walker_lock); 1343 read_lock(&net->ipv6.fib6_walker_lock);
1344 FOR_WALKERS(w) { 1344 FOR_WALKERS(net, w) {
1345 if (!child) { 1345 if (!child) {
1346 if (w->root == fn) { 1346 if (w->root == fn) {
1347 w->root = w->node = NULL; 1347 w->root = w->node = NULL;
@@ -1368,7 +1368,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
1368 } 1368 }
1369 } 1369 }
1370 } 1370 }
1371 read_unlock(&fib6_walker_lock); 1371 read_unlock(&net->ipv6.fib6_walker_lock);
1372 1372
1373 node_free(fn); 1373 node_free(fn);
1374 if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn)) 1374 if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn))
@@ -1411,8 +1411,8 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
1411 } 1411 }
1412 1412
1413 /* Adjust walkers */ 1413 /* Adjust walkers */
1414 read_lock(&fib6_walker_lock); 1414 read_lock(&net->ipv6.fib6_walker_lock);
1415 FOR_WALKERS(w) { 1415 FOR_WALKERS(net, w) {
1416 if (w->state == FWS_C && w->leaf == rt) { 1416 if (w->state == FWS_C && w->leaf == rt) {
1417 RT6_TRACE("walker %p adjusted by delroute\n", w); 1417 RT6_TRACE("walker %p adjusted by delroute\n", w);
1418 w->leaf = rt->dst.rt6_next; 1418 w->leaf = rt->dst.rt6_next;
@@ -1420,7 +1420,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
1420 w->state = FWS_U; 1420 w->state = FWS_U;
1421 } 1421 }
1422 } 1422 }
1423 read_unlock(&fib6_walker_lock); 1423 read_unlock(&net->ipv6.fib6_walker_lock);
1424 1424
1425 rt->dst.rt6_next = NULL; 1425 rt->dst.rt6_next = NULL;
1426 1426
@@ -1588,17 +1588,17 @@ skip:
1588 } 1588 }
1589} 1589}
1590 1590
1591static int fib6_walk(struct fib6_walker *w) 1591static int fib6_walk(struct net *net, struct fib6_walker *w)
1592{ 1592{
1593 int res; 1593 int res;
1594 1594
1595 w->state = FWS_INIT; 1595 w->state = FWS_INIT;
1596 w->node = w->root; 1596 w->node = w->root;
1597 1597
1598 fib6_walker_link(w); 1598 fib6_walker_link(net, w);
1599 res = fib6_walk_continue(w); 1599 res = fib6_walk_continue(w);
1600 if (res <= 0) 1600 if (res <= 0)
1601 fib6_walker_unlink(w); 1601 fib6_walker_unlink(net, w);
1602 return res; 1602 return res;
1603} 1603}
1604 1604
@@ -1668,7 +1668,7 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root,
1668 c.arg = arg; 1668 c.arg = arg;
1669 c.net = net; 1669 c.net = net;
1670 1670
1671 fib6_walk(&c.w); 1671 fib6_walk(net, &c.w);
1672} 1672}
1673 1673
1674static void __fib6_clean_all(struct net *net, 1674static void __fib6_clean_all(struct net *net,
@@ -1816,6 +1816,8 @@ static int __net_init fib6_net_init(struct net *net)
1816{ 1816{
1817 size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ; 1817 size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ;
1818 1818
1819 rwlock_init(&net->ipv6.fib6_walker_lock);
1820 INIT_LIST_HEAD(&net->ipv6.fib6_walkers);
1819 setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net); 1821 setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net);
1820 1822
1821 net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL); 1823 net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL);
@@ -1976,7 +1978,8 @@ static int ipv6_route_yield(struct fib6_walker *w)
1976 return 0; 1978 return 0;
1977} 1979}
1978 1980
1979static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter) 1981static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter,
1982 struct net *net)
1980{ 1983{
1981 memset(&iter->w, 0, sizeof(iter->w)); 1984 memset(&iter->w, 0, sizeof(iter->w));
1982 iter->w.func = ipv6_route_yield; 1985 iter->w.func = ipv6_route_yield;
@@ -1986,7 +1989,7 @@ static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter)
1986 iter->w.args = iter; 1989 iter->w.args = iter;
1987 iter->sernum = iter->w.root->fn_sernum; 1990 iter->sernum = iter->w.root->fn_sernum;
1988 INIT_LIST_HEAD(&iter->w.lh); 1991 INIT_LIST_HEAD(&iter->w.lh);
1989 fib6_walker_link(&iter->w); 1992 fib6_walker_link(net, &iter->w);
1990} 1993}
1991 1994
1992static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl, 1995static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
@@ -2047,16 +2050,16 @@ iter_table:
2047 ++*pos; 2050 ++*pos;
2048 return iter->w.leaf; 2051 return iter->w.leaf;
2049 } else if (r < 0) { 2052 } else if (r < 0) {
2050 fib6_walker_unlink(&iter->w); 2053 fib6_walker_unlink(net, &iter->w);
2051 return NULL; 2054 return NULL;
2052 } 2055 }
2053 fib6_walker_unlink(&iter->w); 2056 fib6_walker_unlink(net, &iter->w);
2054 2057
2055 iter->tbl = ipv6_route_seq_next_table(iter->tbl, net); 2058 iter->tbl = ipv6_route_seq_next_table(iter->tbl, net);
2056 if (!iter->tbl) 2059 if (!iter->tbl)
2057 return NULL; 2060 return NULL;
2058 2061
2059 ipv6_route_seq_setup_walk(iter); 2062 ipv6_route_seq_setup_walk(iter, net);
2060 goto iter_table; 2063 goto iter_table;
2061} 2064}
2062 2065
@@ -2071,7 +2074,7 @@ static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos)
2071 iter->skip = *pos; 2074 iter->skip = *pos;
2072 2075
2073 if (iter->tbl) { 2076 if (iter->tbl) {
2074 ipv6_route_seq_setup_walk(iter); 2077 ipv6_route_seq_setup_walk(iter, net);
2075 return ipv6_route_seq_next(seq, NULL, pos); 2078 return ipv6_route_seq_next(seq, NULL, pos);
2076 } else { 2079 } else {
2077 return NULL; 2080 return NULL;
@@ -2087,10 +2090,11 @@ static bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
2087static void ipv6_route_seq_stop(struct seq_file *seq, void *v) 2090static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
2088 __releases(RCU_BH) 2091 __releases(RCU_BH)
2089{ 2092{
2093 struct net *net = seq_file_net(seq);
2090 struct ipv6_route_iter *iter = seq->private; 2094 struct ipv6_route_iter *iter = seq->private;
2091 2095
2092 if (ipv6_route_iter_active(iter)) 2096 if (ipv6_route_iter_active(iter))
2093 fib6_walker_unlink(&iter->w); 2097 fib6_walker_unlink(net, &iter->w);
2094 2098
2095 rcu_read_unlock_bh(); 2099 rcu_read_unlock_bh();
2096} 2100}