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: 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>
This commit is contained in:
Родитель
b16cb459d7
Коммит
c757faa8bf
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче