Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c757faa8 authored by Wei Wang's avatar Wei Wang Committed by David S. Miller
Browse files

ipv6: prepare fib6_age() for exception table



If all dst cache entries are stored in the exception table under the
main route, we have to go through them during fib6_age() when doing
garbage collecting.
Introduce a new function rt6_age_exception() which goes through all dst
entries in the exception table and remove those entries that are expired.
This function is called in fib6_age() so that all dst caches are also
garbage collected.

Signed-off-by: default avatarWei Wang <weiwan@google.com>
Signed-off-by: default avatarMartin KaFai Lau <kafai@fb.com>
Signed-off-by: default avatarEric Dumazet <edumazet@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent b16cb459
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -29,6 +29,14 @@
#define FIB6_TABLE_HASHSZ 1
#endif

#define RT6_DEBUG 2

#if RT6_DEBUG >= 3
#define RT6_TRACE(x...) pr_debug(x)
#else
#define RT6_TRACE(x...) do { ; } while (0)
#endif

struct rt6_info;

struct fib6_config {
@@ -75,6 +83,11 @@ struct fib6_node {
	struct rcu_head		rcu;
};

struct fib6_gc_args {
	int			timeout;
	int			more;
};

#ifndef CONFIG_IPV6_SUBTREES
#define FIB6_SUBTREE(fn)	NULL
#else
+2 −0
Original line number Diff line number Diff line
@@ -97,6 +97,8 @@ int ip6_del_rt(struct rt6_info *);

void rt6_flush_exceptions(struct rt6_info *rt);
int rt6_remove_exception_rt(struct rt6_info *rt);
void rt6_age_exceptions(struct rt6_info *rt, struct fib6_gc_args *gc_args,
			unsigned long now);

static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt,
				      const struct in6_addr *daddr,
+9 −17
Original line number Diff line number Diff line
@@ -38,14 +38,6 @@
#include <net/ip6_fib.h>
#include <net/ip6_route.h>

#define RT6_DEBUG 2

#if RT6_DEBUG >= 3
#define RT6_TRACE(x...) pr_debug(x)
#else
#define RT6_TRACE(x...) do { ; } while (0)
#endif

static struct kmem_cache *fib6_node_kmem __read_mostly;

struct fib6_cleaner {
@@ -1890,12 +1882,6 @@ static void fib6_flush_trees(struct net *net)
 *	Garbage collection
 */

struct fib6_gc_args
{
	int			timeout;
	int			more;
};

static int fib6_age(struct rt6_info *rt, void *arg)
{
	struct fib6_gc_args *gc_args = arg;
@@ -1904,9 +1890,6 @@ static int fib6_age(struct rt6_info *rt, void *arg)
	/*
	 *	check addrconf expiration here.
	 *	Routes are expired even if they are in use.
	 *
	 *	Also age clones. Note, that clones are aged out
	 *	only if they are not in use now.
	 */

	if (rt->rt6i_flags & RTF_EXPIRES && rt->dst.expires) {
@@ -1915,6 +1898,9 @@ 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;
@@ -1940,6 +1926,12 @@ static int fib6_age(struct rt6_info *rt, void *arg)
		gc_args->more++;
	}

	/*	Also age clones in the exception table.
	 *	Note, that clones are aged out
	 *	only if they are not in use now.
	 */
	rt6_age_exceptions(rt, gc_args, now);

	return 0;
}

+60 −0
Original line number Diff line number Diff line
@@ -1528,6 +1528,66 @@ static void rt6_exceptions_clean_tohost(struct rt6_info *rt,
	spin_unlock_bh(&rt6_exception_lock);
}

static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket,
				      struct rt6_exception *rt6_ex,
				      struct fib6_gc_args *gc_args,
				      unsigned long now)
{
	struct rt6_info *rt = rt6_ex->rt6i;

	if (atomic_read(&rt->dst.__refcnt) == 1 &&
	    time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) {
		RT6_TRACE("aging clone %p\n", rt);
		rt6_remove_exception(bucket, rt6_ex);
		return;
	} 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);
			rt6_remove_exception(bucket, rt6_ex);
			return;
		}
	}
	gc_args->more++;
}

void rt6_age_exceptions(struct rt6_info *rt,
			struct fib6_gc_args *gc_args,
			unsigned long now)
{
	struct rt6_exception_bucket *bucket;
	struct rt6_exception *rt6_ex;
	struct hlist_node *tmp;
	int i;

	if (!rcu_access_pointer(rt->rt6i_exception_bucket))
		return;

	spin_lock_bh(&rt6_exception_lock);
	bucket = rcu_dereference_protected(rt->rt6i_exception_bucket,
				    lockdep_is_held(&rt6_exception_lock));

	if (bucket) {
		for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) {
			hlist_for_each_entry_safe(rt6_ex, tmp,
						  &bucket->chain, hlist) {
				rt6_age_examine_exception(bucket, rt6_ex,
							  gc_args, now);
			}
			bucket++;
		}
	}
	spin_unlock_bh(&rt6_exception_lock);
}

struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
			       int oif, struct flowi6 *fl6, int flags)
{