|
@@ -55,8 +55,6 @@ struct fib6_cleaner {
|
|
|
void *arg;
|
|
|
};
|
|
|
|
|
|
-static DEFINE_RWLOCK(fib6_walker_lock);
|
|
|
-
|
|
|
#ifdef CONFIG_IPV6_SUBTREES
|
|
|
#define FWS_INIT FWS_S
|
|
|
#else
|
|
@@ -66,7 +64,7 @@ static DEFINE_RWLOCK(fib6_walker_lock);
|
|
|
static void fib6_prune_clones(struct net *net, struct fib6_node *fn);
|
|
|
static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn);
|
|
|
static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn);
|
|
|
-static int fib6_walk(struct fib6_walker *w);
|
|
|
+static int fib6_walk(struct net *net, struct fib6_walker *w);
|
|
|
static int fib6_walk_continue(struct fib6_walker *w);
|
|
|
|
|
|
/*
|
|
@@ -78,21 +76,21 @@ static int fib6_walk_continue(struct fib6_walker *w);
|
|
|
|
|
|
static void fib6_gc_timer_cb(unsigned long arg);
|
|
|
|
|
|
-static LIST_HEAD(fib6_walkers);
|
|
|
-#define FOR_WALKERS(w) list_for_each_entry(w, &fib6_walkers, lh)
|
|
|
+#define FOR_WALKERS(net, w) \
|
|
|
+ list_for_each_entry(w, &(net)->ipv6.fib6_walkers, lh)
|
|
|
|
|
|
-static void fib6_walker_link(struct fib6_walker *w)
|
|
|
+static void fib6_walker_link(struct net *net, struct fib6_walker *w)
|
|
|
{
|
|
|
- write_lock_bh(&fib6_walker_lock);
|
|
|
- list_add(&w->lh, &fib6_walkers);
|
|
|
- write_unlock_bh(&fib6_walker_lock);
|
|
|
+ write_lock_bh(&net->ipv6.fib6_walker_lock);
|
|
|
+ list_add(&w->lh, &net->ipv6.fib6_walkers);
|
|
|
+ write_unlock_bh(&net->ipv6.fib6_walker_lock);
|
|
|
}
|
|
|
|
|
|
-static void fib6_walker_unlink(struct fib6_walker *w)
|
|
|
+static void fib6_walker_unlink(struct net *net, struct fib6_walker *w)
|
|
|
{
|
|
|
- write_lock_bh(&fib6_walker_lock);
|
|
|
+ write_lock_bh(&net->ipv6.fib6_walker_lock);
|
|
|
list_del(&w->lh);
|
|
|
- write_unlock_bh(&fib6_walker_lock);
|
|
|
+ write_unlock_bh(&net->ipv6.fib6_walker_lock);
|
|
|
}
|
|
|
|
|
|
static int fib6_new_sernum(struct net *net)
|
|
@@ -325,12 +323,13 @@ static int fib6_dump_node(struct fib6_walker *w)
|
|
|
|
|
|
static void fib6_dump_end(struct netlink_callback *cb)
|
|
|
{
|
|
|
+ struct net *net = sock_net(cb->skb->sk);
|
|
|
struct fib6_walker *w = (void *)cb->args[2];
|
|
|
|
|
|
if (w) {
|
|
|
if (cb->args[4]) {
|
|
|
cb->args[4] = 0;
|
|
|
- fib6_walker_unlink(w);
|
|
|
+ fib6_walker_unlink(net, w);
|
|
|
}
|
|
|
cb->args[2] = 0;
|
|
|
kfree(w);
|
|
@@ -348,6 +347,7 @@ static int fib6_dump_done(struct netlink_callback *cb)
|
|
|
static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
|
|
|
struct netlink_callback *cb)
|
|
|
{
|
|
|
+ struct net *net = sock_net(skb->sk);
|
|
|
struct fib6_walker *w;
|
|
|
int res;
|
|
|
|
|
@@ -359,7 +359,7 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
|
|
|
w->skip = 0;
|
|
|
|
|
|
read_lock_bh(&table->tb6_lock);
|
|
|
- res = fib6_walk(w);
|
|
|
+ res = fib6_walk(net, w);
|
|
|
read_unlock_bh(&table->tb6_lock);
|
|
|
if (res > 0) {
|
|
|
cb->args[4] = 1;
|
|
@@ -379,7 +379,7 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb,
|
|
|
res = fib6_walk_continue(w);
|
|
|
read_unlock_bh(&table->tb6_lock);
|
|
|
if (res <= 0) {
|
|
|
- fib6_walker_unlink(w);
|
|
|
+ fib6_walker_unlink(net, w);
|
|
|
cb->args[4] = 0;
|
|
|
}
|
|
|
}
|
|
@@ -1340,8 +1340,8 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
- read_lock(&fib6_walker_lock);
|
|
|
- FOR_WALKERS(w) {
|
|
|
+ read_lock(&net->ipv6.fib6_walker_lock);
|
|
|
+ FOR_WALKERS(net, w) {
|
|
|
if (!child) {
|
|
|
if (w->root == fn) {
|
|
|
w->root = w->node = NULL;
|
|
@@ -1368,7 +1368,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- read_unlock(&fib6_walker_lock);
|
|
|
+ read_unlock(&net->ipv6.fib6_walker_lock);
|
|
|
|
|
|
node_free(fn);
|
|
|
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,
|
|
|
}
|
|
|
|
|
|
/* Adjust walkers */
|
|
|
- read_lock(&fib6_walker_lock);
|
|
|
- FOR_WALKERS(w) {
|
|
|
+ read_lock(&net->ipv6.fib6_walker_lock);
|
|
|
+ FOR_WALKERS(net, w) {
|
|
|
if (w->state == FWS_C && w->leaf == rt) {
|
|
|
RT6_TRACE("walker %p adjusted by delroute\n", w);
|
|
|
w->leaf = rt->dst.rt6_next;
|
|
@@ -1420,7 +1420,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
|
|
|
w->state = FWS_U;
|
|
|
}
|
|
|
}
|
|
|
- read_unlock(&fib6_walker_lock);
|
|
|
+ read_unlock(&net->ipv6.fib6_walker_lock);
|
|
|
|
|
|
rt->dst.rt6_next = NULL;
|
|
|
|
|
@@ -1588,17 +1588,17 @@ skip:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int fib6_walk(struct fib6_walker *w)
|
|
|
+static int fib6_walk(struct net *net, struct fib6_walker *w)
|
|
|
{
|
|
|
int res;
|
|
|
|
|
|
w->state = FWS_INIT;
|
|
|
w->node = w->root;
|
|
|
|
|
|
- fib6_walker_link(w);
|
|
|
+ fib6_walker_link(net, w);
|
|
|
res = fib6_walk_continue(w);
|
|
|
if (res <= 0)
|
|
|
- fib6_walker_unlink(w);
|
|
|
+ fib6_walker_unlink(net, w);
|
|
|
return res;
|
|
|
}
|
|
|
|
|
@@ -1668,7 +1668,7 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root,
|
|
|
c.arg = arg;
|
|
|
c.net = net;
|
|
|
|
|
|
- fib6_walk(&c.w);
|
|
|
+ fib6_walk(net, &c.w);
|
|
|
}
|
|
|
|
|
|
static void __fib6_clean_all(struct net *net,
|
|
@@ -1725,14 +1725,15 @@ static void fib6_flush_trees(struct net *net)
|
|
|
* Garbage collection
|
|
|
*/
|
|
|
|
|
|
-static struct fib6_gc_args
|
|
|
+struct fib6_gc_args
|
|
|
{
|
|
|
int timeout;
|
|
|
int more;
|
|
|
-} gc_args;
|
|
|
+};
|
|
|
|
|
|
static int fib6_age(struct rt6_info *rt, void *arg)
|
|
|
{
|
|
|
+ struct fib6_gc_args *gc_args = arg;
|
|
|
unsigned long now = jiffies;
|
|
|
|
|
|
/*
|
|
@@ -1748,10 +1749,10 @@ static int fib6_age(struct rt6_info *rt, void *arg)
|
|
|
RT6_TRACE("expiring %p\n", rt);
|
|
|
return -1;
|
|
|
}
|
|
|
- gc_args.more++;
|
|
|
+ gc_args->more++;
|
|
|
} else if (rt->rt6i_flags & RTF_CACHE) {
|
|
|
if (atomic_read(&rt->dst.__refcnt) == 0 &&
|
|
|
- time_after_eq(now, rt->dst.lastuse + gc_args.timeout)) {
|
|
|
+ time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
|
|
|
RT6_TRACE("aging clone %p\n", rt);
|
|
|
return -1;
|
|
|
} else if (rt->rt6i_flags & RTF_GATEWAY) {
|
|
@@ -1769,21 +1770,20 @@ static int fib6_age(struct rt6_info *rt, void *arg)
|
|
|
return -1;
|
|
|
}
|
|
|
}
|
|
|
- gc_args.more++;
|
|
|
+ gc_args->more++;
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static DEFINE_SPINLOCK(fib6_gc_lock);
|
|
|
-
|
|
|
void fib6_run_gc(unsigned long expires, struct net *net, bool force)
|
|
|
{
|
|
|
+ struct fib6_gc_args gc_args;
|
|
|
unsigned long now;
|
|
|
|
|
|
if (force) {
|
|
|
- spin_lock_bh(&fib6_gc_lock);
|
|
|
- } else if (!spin_trylock_bh(&fib6_gc_lock)) {
|
|
|
+ spin_lock_bh(&net->ipv6.fib6_gc_lock);
|
|
|
+ } else if (!spin_trylock_bh(&net->ipv6.fib6_gc_lock)) {
|
|
|
mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
|
|
|
return;
|
|
|
}
|
|
@@ -1792,7 +1792,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force)
|
|
|
|
|
|
gc_args.more = icmp6_dst_gc();
|
|
|
|
|
|
- fib6_clean_all(net, fib6_age, NULL);
|
|
|
+ fib6_clean_all(net, fib6_age, &gc_args);
|
|
|
now = jiffies;
|
|
|
net->ipv6.ip6_rt_last_gc = now;
|
|
|
|
|
@@ -1802,7 +1802,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force)
|
|
|
+ net->ipv6.sysctl.ip6_rt_gc_interval));
|
|
|
else
|
|
|
del_timer(&net->ipv6.ip6_fib_timer);
|
|
|
- spin_unlock_bh(&fib6_gc_lock);
|
|
|
+ spin_unlock_bh(&net->ipv6.fib6_gc_lock);
|
|
|
}
|
|
|
|
|
|
static void fib6_gc_timer_cb(unsigned long arg)
|
|
@@ -1814,6 +1814,9 @@ static int __net_init fib6_net_init(struct net *net)
|
|
|
{
|
|
|
size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ;
|
|
|
|
|
|
+ spin_lock_init(&net->ipv6.fib6_gc_lock);
|
|
|
+ rwlock_init(&net->ipv6.fib6_walker_lock);
|
|
|
+ INIT_LIST_HEAD(&net->ipv6.fib6_walkers);
|
|
|
setup_timer(&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long)net);
|
|
|
|
|
|
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)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter)
|
|
|
+static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter,
|
|
|
+ struct net *net)
|
|
|
{
|
|
|
memset(&iter->w, 0, sizeof(iter->w));
|
|
|
iter->w.func = ipv6_route_yield;
|
|
@@ -1984,7 +1988,7 @@ static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter)
|
|
|
iter->w.args = iter;
|
|
|
iter->sernum = iter->w.root->fn_sernum;
|
|
|
INIT_LIST_HEAD(&iter->w.lh);
|
|
|
- fib6_walker_link(&iter->w);
|
|
|
+ fib6_walker_link(net, &iter->w);
|
|
|
}
|
|
|
|
|
|
static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
|
|
@@ -2045,16 +2049,16 @@ iter_table:
|
|
|
++*pos;
|
|
|
return iter->w.leaf;
|
|
|
} else if (r < 0) {
|
|
|
- fib6_walker_unlink(&iter->w);
|
|
|
+ fib6_walker_unlink(net, &iter->w);
|
|
|
return NULL;
|
|
|
}
|
|
|
- fib6_walker_unlink(&iter->w);
|
|
|
+ fib6_walker_unlink(net, &iter->w);
|
|
|
|
|
|
iter->tbl = ipv6_route_seq_next_table(iter->tbl, net);
|
|
|
if (!iter->tbl)
|
|
|
return NULL;
|
|
|
|
|
|
- ipv6_route_seq_setup_walk(iter);
|
|
|
+ ipv6_route_seq_setup_walk(iter, net);
|
|
|
goto iter_table;
|
|
|
}
|
|
|
|
|
@@ -2069,7 +2073,7 @@ static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos)
|
|
|
iter->skip = *pos;
|
|
|
|
|
|
if (iter->tbl) {
|
|
|
- ipv6_route_seq_setup_walk(iter);
|
|
|
+ ipv6_route_seq_setup_walk(iter, net);
|
|
|
return ipv6_route_seq_next(seq, NULL, pos);
|
|
|
} else {
|
|
|
return NULL;
|
|
@@ -2085,10 +2089,11 @@ static bool ipv6_route_iter_active(struct ipv6_route_iter *iter)
|
|
|
static void ipv6_route_seq_stop(struct seq_file *seq, void *v)
|
|
|
__releases(RCU_BH)
|
|
|
{
|
|
|
+ struct net *net = seq_file_net(seq);
|
|
|
struct ipv6_route_iter *iter = seq->private;
|
|
|
|
|
|
if (ipv6_route_iter_active(iter))
|
|
|
- fib6_walker_unlink(&iter->w);
|
|
|
+ fib6_walker_unlink(net, &iter->w);
|
|
|
|
|
|
rcu_read_unlock_bh();
|
|
|
}
|