diff options
Diffstat (limited to 'net/ipv6/ip6_fib.c')
-rw-r--r-- | net/ipv6/ip6_fib.c | 91 |
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 | ||
58 | static 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); | |||
66 | static void fib6_prune_clones(struct net *net, struct fib6_node *fn); | 64 | static void fib6_prune_clones(struct net *net, struct fib6_node *fn); |
67 | static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn); | 65 | static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn); |
68 | static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn); | 66 | static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn); |
69 | static int fib6_walk(struct fib6_walker *w); | 67 | static int fib6_walk(struct net *net, struct fib6_walker *w); |
70 | static int fib6_walk_continue(struct fib6_walker *w); | 68 | static 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 | ||
79 | static void fib6_gc_timer_cb(unsigned long arg); | 77 | static void fib6_gc_timer_cb(unsigned long arg); |
80 | 78 | ||
81 | static 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 | ||
84 | static void fib6_walker_link(struct fib6_walker *w) | 82 | static 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 | ||
91 | static void fib6_walker_unlink(struct fib6_walker *w) | 89 | static 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 | ||
98 | static int fib6_new_sernum(struct net *net) | 96 | static int fib6_new_sernum(struct net *net) |
@@ -325,12 +323,13 @@ static int fib6_dump_node(struct fib6_walker *w) | |||
325 | 323 | ||
326 | static void fib6_dump_end(struct netlink_callback *cb) | 324 | static 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) | |||
348 | static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb, | 347 | static 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 | ||
1591 | static int fib6_walk(struct fib6_walker *w) | 1591 | static 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 | ||
1674 | static void __fib6_clean_all(struct net *net, | 1674 | static 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 | ||
1728 | static struct fib6_gc_args | 1728 | struct fib6_gc_args |
1729 | { | 1729 | { |
1730 | int timeout; | 1730 | int timeout; |
1731 | int more; | 1731 | int more; |
1732 | } gc_args; | 1732 | }; |
1733 | 1733 | ||
1734 | static int fib6_age(struct rt6_info *rt, void *arg) | 1734 | static 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 | ||
1778 | static DEFINE_SPINLOCK(fib6_gc_lock); | ||
1779 | |||
1780 | void fib6_run_gc(unsigned long expires, struct net *net, bool force) | 1779 | void 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 | ||
1808 | static void fib6_gc_timer_cb(unsigned long arg) | 1808 | static 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 | ||
1977 | static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter) | 1980 | static 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 | ||
1990 | static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl, | 1994 | static 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) | |||
2085 | static void ipv6_route_seq_stop(struct seq_file *seq, void *v) | 2089 | static 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 | } |