Skip to content

Commit

Permalink
ipv6: hook up exception table to store dst cache
Browse files Browse the repository at this point in the history
This commit makes use of the exception hash table implementation to
store dst caches created by pmtu discovery and ip redirect into the hash
table under the rt_info and no longer inserts these routes into fib6
tree.
This makes the fib6 tree only contain static configured routes and could
now be protected by rcu instead of a rw lock.
With this change, in the route lookup related functions, after finding
the rt6_info with the longest prefix, we also need to search for the
exception table before doing backtracking.
In the route delete function, if the route being deleted is not a dst
cache, deletion of this route also need to flush the whole hash table
under it. If it is a dst cache, then only delete the cached dst in the
hash table.

Note: for fib6_walk_continue() function, w->root now is always pointing
to a root node considering that fib6_prune_clones() is removed from the
code. So we add a WARN_ON() msg to make sure w->root always points to a
root node and also removed the update of w->root in fib6_repair_tree().
This is a prerequisite for later patch because we don't need to make
w->root as rcu protected when replacing rwlock with RCU.
Also, we remove all prune related variables as it is no longer used.

Signed-off-by: Wei Wang <weiwan@google.com>
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
tracywwnj authored and davem330 committed Oct 7, 2017
1 parent 38fbeee commit 2b760fc
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 133 deletions.
1 change: 0 additions & 1 deletion include/net/ip6_fib.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,6 @@ struct fib6_walker {
struct fib6_node *root, *node;
struct rt6_info *leaf;
enum fib6_walk_state state;
bool prune;
unsigned int skip;
unsigned int count;
int (*func)(struct fib6_walker *);
Expand Down
1 change: 0 additions & 1 deletion net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2326,7 +2326,6 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
if (!fn)
goto out;

noflags |= RTF_CACHE;
for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
if (rt->dst.dev->ifindex != dev->ifindex)
continue;
Expand Down
95 changes: 17 additions & 78 deletions net/ipv6/ip6_fib.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ struct fib6_cleaner {
#define FWS_INIT FWS_L
#endif

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 net *net, struct fib6_walker *w);
Expand Down Expand Up @@ -1101,6 +1100,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,

if (WARN_ON_ONCE(!atomic_read(&rt->dst.__refcnt)))
return -EINVAL;
if (WARN_ON_ONCE(rt->rt6i_flags & RTF_CACHE))
return -EINVAL;

if (info->nlh) {
if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
Expand Down Expand Up @@ -1192,11 +1193,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
#endif

err = fib6_add_rt2node(fn, rt, info, mxc);
if (!err) {
if (!err)
fib6_start_gc(info->nl_net, rt);
if (!(rt->rt6i_flags & RTF_CACHE))
fib6_prune_clones(info->nl_net, pn);
}

out:
if (err) {
Expand Down Expand Up @@ -1511,19 +1509,12 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
read_lock(&net->ipv6.fib6_walker_lock);
FOR_WALKERS(net, w) {
if (!child) {
if (w->root == fn) {
w->root = w->node = NULL;
RT6_TRACE("W %p adjusted by delroot 1\n", w);
} else if (w->node == fn) {
if (w->node == fn) {
RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate);
w->node = pn;
w->state = nstate;
}
} else {
if (w->root == fn) {
w->root = child;
RT6_TRACE("W %p adjusted by delroot 2\n", w);
}
if (w->node == fn) {
w->node = child;
if (children&2) {
Expand Down Expand Up @@ -1557,12 +1548,17 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,

RT6_TRACE("fib6_del_route\n");

WARN_ON_ONCE(rt->rt6i_flags & RTF_CACHE);

/* Unlink it */
*rtp = rt->dst.rt6_next;
rt->rt6i_node = NULL;
net->ipv6.rt6_stats->fib_rt_entries--;
net->ipv6.rt6_stats->fib_discarded_routes++;

/* Flush all cached dst in exception table */
rt6_flush_exceptions(rt);

/* Reset round-robin state, if necessary */
if (fn->rr_ptr == rt)
fn->rr_ptr = NULL;
Expand Down Expand Up @@ -1625,18 +1621,9 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)

WARN_ON(!(fn->fn_flags & RTN_RTINFO));

if (!(rt->rt6i_flags & RTF_CACHE)) {
struct fib6_node *pn = fn;
#ifdef CONFIG_IPV6_SUBTREES
/* clones of this route might be in another subtree */
if (rt->rt6i_src.plen) {
while (!(pn->fn_flags & RTN_ROOT))
pn = pn->parent;
pn = pn->parent;
}
#endif
fib6_prune_clones(info->nl_net, pn);
}
/* remove cached dst from exception table */
if (rt->rt6i_flags & RTF_CACHE)
return rt6_remove_exception_rt(rt);

/*
* Walk the leaf entries looking for ourself
Expand Down Expand Up @@ -1679,16 +1666,14 @@ static int fib6_walk_continue(struct fib6_walker *w)
{
struct fib6_node *fn, *pn;

/* w->root should always be table->tb6_root */
WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT));

for (;;) {
fn = w->node;
if (!fn)
return 0;

if (w->prune && fn != w->root &&
fn->fn_flags & RTN_RTINFO && w->state < FWS_C) {
w->state = FWS_C;
w->leaf = fn->leaf;
}
switch (w->state) {
#ifdef CONFIG_IPV6_SUBTREES
case FWS_S:
Expand Down Expand Up @@ -1820,20 +1805,16 @@ static int fib6_clean_node(struct fib6_walker *w)
* func is called on each route.
* It may return -1 -> delete this route.
* 0 -> continue walking
*
* prune==1 -> only immediate children of node (certainly,
* ignoring pure split nodes) will be scanned.
*/

static void fib6_clean_tree(struct net *net, struct fib6_node *root,
int (*func)(struct rt6_info *, void *arg),
bool prune, int sernum, void *arg)
int sernum, void *arg)
{
struct fib6_cleaner c;

c.w.root = root;
c.w.func = fib6_clean_node;
c.w.prune = prune;
c.w.count = 0;
c.w.skip = 0;
c.func = func;
Expand All @@ -1858,7 +1839,7 @@ static void __fib6_clean_all(struct net *net,
hlist_for_each_entry_rcu(table, head, tb6_hlist) {
write_lock_bh(&table->tb6_lock);
fib6_clean_tree(net, &table->tb6_root,
func, false, sernum, arg);
func, sernum, arg);
write_unlock_bh(&table->tb6_lock);
}
}
Expand All @@ -1871,22 +1852,6 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *),
__fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg);
}

static int fib6_prune_clone(struct rt6_info *rt, void *arg)
{
if (rt->rt6i_flags & RTF_CACHE) {
RT6_TRACE("pruning clone %p\n", rt);
return -1;
}

return 0;
}

static void fib6_prune_clones(struct net *net, struct fib6_node *fn)
{
fib6_clean_tree(net, fn, fib6_prune_clone, true,
FIB6_NO_SERNUM_CHANGE, NULL);
}

static void fib6_flush_trees(struct net *net)
{
int new_sernum = fib6_new_sernum(net);
Expand Down Expand Up @@ -1914,32 +1879,6 @@ static int fib6_age(struct rt6_info *rt, void *arg)
return -1;
}
gc_args->more++;
/* The following part will soon be removed when the exception
* table is hooked up to store all cached routes.
*/
} else if (rt->rt6i_flags & RTF_CACHE) {
if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout))
rt->dst.obsolete = DST_OBSOLETE_KILL;
if (atomic_read(&rt->dst.__refcnt) == 1 &&
rt->dst.obsolete == DST_OBSOLETE_KILL) {
RT6_TRACE("aging clone %p\n", rt);
return -1;
} else if (rt->rt6i_flags & RTF_GATEWAY) {
struct neighbour *neigh;
__u8 neigh_flags = 0;

neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway);
if (neigh) {
neigh_flags = neigh->flags;
neigh_release(neigh);
}
if (!(neigh_flags & NTF_ROUTER)) {
RT6_TRACE("purging route %p via non-router but gateway\n",
rt);
return -1;
}
}
gc_args->more++;
}

/* Also age clones in the exception table.
Expand Down

0 comments on commit 2b760fc

Please sign in to comment.