mirror of
https://github.com/torvalds/linux.git
synced 2024-09-20 15:03:04 +00:00
net/ipv6: Remove expired routes with a separated list of routes.
FIB6 GC walks trees of fib6_tables to remove expired routes. Walking a tree can be expensive if the number of routes in a table is big, even if most of them are permanent. Checking routes in a separated list of routes having expiration will avoid this potential issue. Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com> Reviewed-by: David Ahern <dsahern@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
d147085183
commit
3dec89b14d
|
@ -179,6 +179,9 @@ struct fib6_info {
|
||||||
|
|
||||||
refcount_t fib6_ref;
|
refcount_t fib6_ref;
|
||||||
unsigned long expires;
|
unsigned long expires;
|
||||||
|
|
||||||
|
struct hlist_node gc_link;
|
||||||
|
|
||||||
struct dst_metrics *fib6_metrics;
|
struct dst_metrics *fib6_metrics;
|
||||||
#define fib6_pmtu fib6_metrics->metrics[RTAX_MTU-1]
|
#define fib6_pmtu fib6_metrics->metrics[RTAX_MTU-1]
|
||||||
|
|
||||||
|
@ -247,19 +250,6 @@ static inline bool fib6_requires_src(const struct fib6_info *rt)
|
||||||
return rt->fib6_src.plen > 0;
|
return rt->fib6_src.plen > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void fib6_clean_expires(struct fib6_info *f6i)
|
|
||||||
{
|
|
||||||
f6i->fib6_flags &= ~RTF_EXPIRES;
|
|
||||||
f6i->expires = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void fib6_set_expires(struct fib6_info *f6i,
|
|
||||||
unsigned long expires)
|
|
||||||
{
|
|
||||||
f6i->expires = expires;
|
|
||||||
f6i->fib6_flags |= RTF_EXPIRES;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool fib6_check_expired(const struct fib6_info *f6i)
|
static inline bool fib6_check_expired(const struct fib6_info *f6i)
|
||||||
{
|
{
|
||||||
if (f6i->fib6_flags & RTF_EXPIRES)
|
if (f6i->fib6_flags & RTF_EXPIRES)
|
||||||
|
@ -267,6 +257,11 @@ static inline bool fib6_check_expired(const struct fib6_info *f6i)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool fib6_has_expires(const struct fib6_info *f6i)
|
||||||
|
{
|
||||||
|
return f6i->fib6_flags & RTF_EXPIRES;
|
||||||
|
}
|
||||||
|
|
||||||
/* Function to safely get fn->fn_sernum for passed in rt
|
/* Function to safely get fn->fn_sernum for passed in rt
|
||||||
* and store result in passed in cookie.
|
* and store result in passed in cookie.
|
||||||
* Return true if we can get cookie safely
|
* Return true if we can get cookie safely
|
||||||
|
@ -388,6 +383,7 @@ struct fib6_table {
|
||||||
struct inet_peer_base tb6_peers;
|
struct inet_peer_base tb6_peers;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
unsigned int fib_seq;
|
unsigned int fib_seq;
|
||||||
|
struct hlist_head tb6_gc_hlist; /* GC candidates */
|
||||||
#define RT6_TABLE_HAS_DFLT_ROUTER BIT(0)
|
#define RT6_TABLE_HAS_DFLT_ROUTER BIT(0)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -504,6 +500,48 @@ void fib6_gc_cleanup(void);
|
||||||
|
|
||||||
int fib6_init(void);
|
int fib6_init(void);
|
||||||
|
|
||||||
|
/* fib6_info must be locked by the caller, and fib6_info->fib6_table can be
|
||||||
|
* NULL.
|
||||||
|
*/
|
||||||
|
static inline void fib6_set_expires_locked(struct fib6_info *f6i,
|
||||||
|
unsigned long expires)
|
||||||
|
{
|
||||||
|
struct fib6_table *tb6;
|
||||||
|
|
||||||
|
tb6 = f6i->fib6_table;
|
||||||
|
f6i->expires = expires;
|
||||||
|
if (tb6 && !fib6_has_expires(f6i))
|
||||||
|
hlist_add_head(&f6i->gc_link, &tb6->tb6_gc_hlist);
|
||||||
|
f6i->fib6_flags |= RTF_EXPIRES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fib6_info must be locked by the caller, and fib6_info->fib6_table can be
|
||||||
|
* NULL. If fib6_table is NULL, the fib6_info will no be inserted into the
|
||||||
|
* list of GC candidates until it is inserted into a table.
|
||||||
|
*/
|
||||||
|
static inline void fib6_set_expires(struct fib6_info *f6i,
|
||||||
|
unsigned long expires)
|
||||||
|
{
|
||||||
|
spin_lock_bh(&f6i->fib6_table->tb6_lock);
|
||||||
|
fib6_set_expires_locked(f6i, expires);
|
||||||
|
spin_unlock_bh(&f6i->fib6_table->tb6_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void fib6_clean_expires_locked(struct fib6_info *f6i)
|
||||||
|
{
|
||||||
|
if (fib6_has_expires(f6i))
|
||||||
|
hlist_del_init(&f6i->gc_link);
|
||||||
|
f6i->fib6_flags &= ~RTF_EXPIRES;
|
||||||
|
f6i->expires = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void fib6_clean_expires(struct fib6_info *f6i)
|
||||||
|
{
|
||||||
|
spin_lock_bh(&f6i->fib6_table->tb6_lock);
|
||||||
|
fib6_clean_expires_locked(f6i);
|
||||||
|
spin_unlock_bh(&f6i->fib6_table->tb6_lock);
|
||||||
|
}
|
||||||
|
|
||||||
struct ipv6_route_iter {
|
struct ipv6_route_iter {
|
||||||
struct seq_net_private p;
|
struct seq_net_private p;
|
||||||
struct fib6_walker w;
|
struct fib6_walker w;
|
||||||
|
|
|
@ -160,6 +160,8 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh)
|
||||||
INIT_LIST_HEAD(&f6i->fib6_siblings);
|
INIT_LIST_HEAD(&f6i->fib6_siblings);
|
||||||
refcount_set(&f6i->fib6_ref, 1);
|
refcount_set(&f6i->fib6_ref, 1);
|
||||||
|
|
||||||
|
INIT_HLIST_NODE(&f6i->gc_link);
|
||||||
|
|
||||||
return f6i;
|
return f6i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,6 +248,7 @@ static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
|
||||||
net->ipv6.fib6_null_entry);
|
net->ipv6.fib6_null_entry);
|
||||||
table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
|
table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
|
||||||
inet_peer_base_init(&table->tb6_peers);
|
inet_peer_base_init(&table->tb6_peers);
|
||||||
|
INIT_HLIST_HEAD(&table->tb6_gc_hlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
return table;
|
return table;
|
||||||
|
@ -1057,6 +1060,8 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
|
||||||
lockdep_is_held(&table->tb6_lock));
|
lockdep_is_held(&table->tb6_lock));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fib6_clean_expires_locked(rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1118,9 +1123,10 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
|
||||||
if (!(iter->fib6_flags & RTF_EXPIRES))
|
if (!(iter->fib6_flags & RTF_EXPIRES))
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
if (!(rt->fib6_flags & RTF_EXPIRES))
|
if (!(rt->fib6_flags & RTF_EXPIRES))
|
||||||
fib6_clean_expires(iter);
|
fib6_clean_expires_locked(iter);
|
||||||
else
|
else
|
||||||
fib6_set_expires(iter, rt->expires);
|
fib6_set_expires_locked(iter,
|
||||||
|
rt->expires);
|
||||||
|
|
||||||
if (rt->fib6_pmtu)
|
if (rt->fib6_pmtu)
|
||||||
fib6_metric_set(iter, RTAX_MTU,
|
fib6_metric_set(iter, RTAX_MTU,
|
||||||
|
@ -1479,6 +1485,10 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
|
||||||
if (rt->nh)
|
if (rt->nh)
|
||||||
list_add(&rt->nh_list, &rt->nh->f6i_list);
|
list_add(&rt->nh_list, &rt->nh->f6i_list);
|
||||||
__fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net));
|
__fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net));
|
||||||
|
|
||||||
|
if (fib6_has_expires(rt))
|
||||||
|
hlist_add_head(&rt->gc_link, &table->tb6_gc_hlist);
|
||||||
|
|
||||||
fib6_start_gc(info->nl_net, rt);
|
fib6_start_gc(info->nl_net, rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2285,9 +2295,8 @@ static void fib6_flush_trees(struct net *net)
|
||||||
* Garbage collection
|
* Garbage collection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int fib6_age(struct fib6_info *rt, void *arg)
|
static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
|
||||||
{
|
{
|
||||||
struct fib6_gc_args *gc_args = arg;
|
|
||||||
unsigned long now = jiffies;
|
unsigned long now = jiffies;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2295,7 +2304,7 @@ static int fib6_age(struct fib6_info *rt, void *arg)
|
||||||
* Routes are expired even if they are in use.
|
* Routes are expired even if they are in use.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (rt->fib6_flags & RTF_EXPIRES && rt->expires) {
|
if (fib6_has_expires(rt) && rt->expires) {
|
||||||
if (time_after(now, rt->expires)) {
|
if (time_after(now, rt->expires)) {
|
||||||
RT6_TRACE("expiring %p\n", rt);
|
RT6_TRACE("expiring %p\n", rt);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -2312,6 +2321,40 @@ static int fib6_age(struct fib6_info *rt, void *arg)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fib6_gc_table(struct net *net,
|
||||||
|
struct fib6_table *tb6,
|
||||||
|
struct fib6_gc_args *gc_args)
|
||||||
|
{
|
||||||
|
struct fib6_info *rt;
|
||||||
|
struct hlist_node *n;
|
||||||
|
struct nl_info info = {
|
||||||
|
.nl_net = net,
|
||||||
|
.skip_notify = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
hlist_for_each_entry_safe(rt, n, &tb6->tb6_gc_hlist, gc_link)
|
||||||
|
if (fib6_age(rt, gc_args) == -1)
|
||||||
|
fib6_del(rt, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fib6_gc_all(struct net *net, struct fib6_gc_args *gc_args)
|
||||||
|
{
|
||||||
|
struct fib6_table *table;
|
||||||
|
struct hlist_head *head;
|
||||||
|
unsigned int h;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
|
||||||
|
head = &net->ipv6.fib_table_hash[h];
|
||||||
|
hlist_for_each_entry_rcu(table, head, tb6_hlist) {
|
||||||
|
spin_lock_bh(&table->tb6_lock);
|
||||||
|
fib6_gc_table(net, table, gc_args);
|
||||||
|
spin_unlock_bh(&table->tb6_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void fib6_run_gc(unsigned long expires, struct net *net, bool force)
|
void fib6_run_gc(unsigned long expires, struct net *net, bool force)
|
||||||
{
|
{
|
||||||
struct fib6_gc_args gc_args;
|
struct fib6_gc_args gc_args;
|
||||||
|
@ -2327,7 +2370,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force)
|
||||||
net->ipv6.sysctl.ip6_rt_gc_interval;
|
net->ipv6.sysctl.ip6_rt_gc_interval;
|
||||||
gc_args.more = 0;
|
gc_args.more = 0;
|
||||||
|
|
||||||
fib6_clean_all(net, fib6_age, &gc_args);
|
fib6_gc_all(net, &gc_args);
|
||||||
now = jiffies;
|
now = jiffies;
|
||||||
net->ipv6.ip6_rt_last_gc = now;
|
net->ipv6.ip6_rt_last_gc = now;
|
||||||
|
|
||||||
|
|
|
@ -3761,10 +3761,10 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
|
||||||
rt->dst_nocount = true;
|
rt->dst_nocount = true;
|
||||||
|
|
||||||
if (cfg->fc_flags & RTF_EXPIRES)
|
if (cfg->fc_flags & RTF_EXPIRES)
|
||||||
fib6_set_expires(rt, jiffies +
|
fib6_set_expires_locked(rt, jiffies +
|
||||||
clock_t_to_jiffies(cfg->fc_expires));
|
clock_t_to_jiffies(cfg->fc_expires));
|
||||||
else
|
else
|
||||||
fib6_clean_expires(rt);
|
fib6_clean_expires_locked(rt);
|
||||||
|
|
||||||
if (cfg->fc_protocol == RTPROT_UNSPEC)
|
if (cfg->fc_protocol == RTPROT_UNSPEC)
|
||||||
cfg->fc_protocol = RTPROT_BOOT;
|
cfg->fc_protocol = RTPROT_BOOT;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user