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.c91
1 files changed, 48 insertions, 43 deletions
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 0c7e276c230e..ea071fad67a0 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,
@@ -1725,14 +1725,15 @@ static void fib6_flush_trees(struct net *net)
1725 * Garbage collection 1725 * Garbage collection
1726 */ 1726 */
1727 1727
1728static struct fib6_gc_args 1728struct fib6_gc_args
1729{ 1729{
1730 int timeout; 1730 int timeout;
1731 int more; 1731 int more;
1732} gc_args; 1732};
1733 1733
1734static int fib6_age(struct rt6_info *rt, void *arg) 1734static int fib6_age(struct rt6_info *rt, void *arg)
1735{ 1735{
1736 struct fib6_gc_args *gc_args = arg;
1736 unsigned long now = jiffies; 1737 unsigned long now = jiffies;
1737 1738
1738 /* 1739 /*
@@ -1748,10 +1749,10 @@ static int fib6_age(struct rt6_info *rt, void *arg)
1748 RT6_TRACE("expiring %p\n", rt); 1749 RT6_TRACE("expiring %p\n", rt);
1749 return -1; 1750 return -1;
1750 } 1751 }
1751 gc_args.more++; 1752 gc_args->more++;
1752 } else if (rt->rt6i_flags & RTF_CACHE) { 1753 } else if (rt->rt6i_flags & RTF_CACHE) {
1753 if (atomic_read(&rt->dst.__refcnt) == 0 && 1754 if (atomic_read(&rt->dst.__refcnt) == 0 &&
1754 time_after_eq(now, rt->dst.lastuse + gc_args.timeout)) { 1755 time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
1755 RT6_TRACE("aging clone %p\n", rt); 1756 RT6_TRACE("aging clone %p\n", rt);
1756 return -1; 1757 return -1;
1757 } else if (rt->rt6i_flags & RTF_GATEWAY) { 1758 } else if (rt->rt6i_flags & RTF_GATEWAY) {
@@ -1769,21 +1770,20 @@ static int fib6_age(struct rt6_info *rt, void *arg)
1769 return -1; 1770 return -1;
1770 } 1771 }
1771 } 1772 }
1772 gc_args.more++; 1773 gc_args->more++;
1773 } 1774 }
1774 1775
1775 return 0; 1776 return 0;
1776} 1777}
1777 1778
1778static DEFINE_SPINLOCK(fib6_gc_lock);
1779
1780void fib6_run_gc(unsigned long expires, struct net *net, bool force) 1779void fib6_run_gc(unsigned long expires, struct net *net, bool force)
1781{ 1780{
1781 struct fib6_gc_args gc_args;
1782 unsigned long now; 1782 unsigned long now;
1783 1783
1784 if (force) { 1784 if (force) {
1785 spin_lock_bh(&fib6_gc_lock); 1785 spin_lock_bh(&net->ipv6.fib6_gc_lock);
1786 } else if (!spin_trylock_bh(&fib6_gc_lock)) { 1786 } else if (!spin_trylock_bh(&net->ipv6.fib6_gc_lock)) {
1787 mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ); 1787 mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
1788 return; 1788 return;
1789 } 1789 }
@@ -1792,7 +1792,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force)
1792 1792
1793 gc_args.more = icmp6_dst_gc(); 1793 gc_args.more = icmp6_dst_gc();
1794 1794
1795 fib6_clean_all(net, fib6_age, NULL); 1795 fib6_clean_all(net, fib6_age, &gc_args);
1796 now = jiffies; 1796 now = jiffies;
1797 net->ipv6.ip6_rt_last_gc = now; 1797 net->ipv6.ip6_rt_last_gc = now;
1798 1798
@@ -1802,7 +1802,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force)
1802 + net->ipv6.sysctl.ip6_rt_gc_interval)); 1802 + net->ipv6.sysctl.ip6_rt_gc_interval));
1803 else 1803 else
1804 del_timer(&net->ipv6.ip6_fib_timer); 1804 del_timer(&net->ipv6.ip6_fib_timer);
1805 spin_unlock_bh(&fib6_gc_lock); 1805 spin_unlock_bh(&net->ipv6.fib6_gc_lock);
1806} 1806}
1807 1807
1808static void fib6_gc_timer_cb(unsigned long arg) 1808static void fib6_gc_timer_cb(unsigned long arg)
@@ -1814,6 +1814,9 @@ static int __net_init fib6_net_init(struct net *net)
1814{ 1814{
1815 size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ; 1815 size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ;
1816 1816
1817 spin_lock_init(&net->ipv6.fib6_gc_lock);
1818 rwlock_init(&net->ipv6.fib6_walker_lock);
1819 INIT_LIST_HEAD(&net->ipv6.fib6_walkers);
1817 setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net); 1820 setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net);
1818 1821
1819 net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL); 1822 net->ipv6.rt6_stats = kzalloc(sizeof(*net->ipv6.rt6_stats), GFP_KERNEL);
@@ -1974,7 +1977,8 @@ static int ipv6_route_yield(struct fib6_walker *w)
1974 return 0; 1977 return 0;
1975} 1978}
1976 1979
1977static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter) 1980static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter,
1981 struct net *net)
1978{ 1982{
1979 memset(&iter->w, 0, sizeof(iter->w)); 1983 memset(&iter->w, 0, sizeof(iter->w));
1980 iter->w.func = ipv6_route_yield; 1984 iter->w.func = ipv6_route_yield;
@@ -1984,7 +1988,7 @@ static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter)
1984 iter->w.args = iter; 1988 iter->w.args = iter;
1985 iter->sernum = iter->w.root->fn_sernum; 1989 iter->sernum = iter->w.root->fn_sernum;
1986 INIT_LIST_HEAD(&iter->w.lh); 1990 INIT_LIST_HEAD(&iter->w.lh);
1987 fib6_walker_link(&iter->w); 1991 fib6_walker_link(net, &iter->w);
1988} 1992}
1989 1993
1990static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl, 1994static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
@@ -2045,16 +2049,16 @@ iter_table:
2045 ++*pos; 2049 ++*pos;
2046 return iter->w.leaf; 2050 return iter->w.leaf;
2047 } else if (r < 0) { 2051 } else if (r < 0) {
2048 fib6_walker_unlink(&iter->w); 2052 fib6_walker_unlink(net, &iter->w);
2049 return NULL; 2053 return NULL;
2050 } 2054 }
2051 fib6_walker_unlink(&iter->w); 2055 fib6_walker_unlink(net, &iter->w);
2052 2056
2053 iter->tbl = ipv6_route_seq_next_table(iter->tbl, net); 2057 iter->tbl = ipv6_route_seq_next_table(iter->tbl, net);
2054 if (!iter->tbl) 2058 if (!iter->tbl)
2055 return NULL; 2059 return NULL;
2056 2060
2057 ipv6_route_seq_setup_walk(iter); 2061 ipv6_route_seq_setup_walk(iter, net);
2058 goto iter_table; 2062 goto iter_table;
2059} 2063}
2060 2064
@@ -2069,7 +2073,7 @@ static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos)
2069 iter->skip = *pos; 2073 iter->skip = *pos;
2070 2074
2071 if (iter->tbl) { 2075 if (iter->tbl) {
2072 ipv6_route_seq_setup_walk(iter); 2076 ipv6_route_seq_setup_walk(iter, net);
2073 return ipv6_route_seq_next(seq, NULL, pos); 2077 return ipv6_route_seq_next(seq, NULL, pos);
2074 } else { 2078 } else {
2075 return NULL; 2079 return NULL;
@@ -2085,10 +2089,11 @@ static bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
2085static void ipv6_route_seq_stop(struct seq_file *seq, void *v) 2089static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
2086 __releases(RCU_BH) 2090 __releases(RCU_BH)
2087{ 2091{
2092 struct net *net = seq_file_net(seq);
2088 struct ipv6_route_iter *iter = seq->private; 2093 struct ipv6_route_iter *iter = seq->private;
2089 2094
2090 if (ipv6_route_iter_active(iter)) 2095 if (ipv6_route_iter_active(iter))
2091 fib6_walker_unlink(&iter->w); 2096 fib6_walker_unlink(net, &iter->w);
2092 2097
2093 rcu_read_unlock_bh(); 2098 rcu_read_unlock_bh();
2094} 2099}