Skip to content

Commit 3dec89b

Browse files
ThinkerYzu1davem330
authored andcommitted
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>
1 parent d147085 commit 3dec89b

3 files changed

Lines changed: 103 additions & 22 deletions

File tree

include/net/ip6_fib.h

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ struct fib6_info {
179179

180180
refcount_t fib6_ref;
181181
unsigned long expires;
182+
183+
struct hlist_node gc_link;
184+
182185
struct dst_metrics *fib6_metrics;
183186
#define fib6_pmtu fib6_metrics->metrics[RTAX_MTU-1]
184187

@@ -247,26 +250,18 @@ static inline bool fib6_requires_src(const struct fib6_info *rt)
247250
return rt->fib6_src.plen > 0;
248251
}
249252

250-
static inline void fib6_clean_expires(struct fib6_info *f6i)
251-
{
252-
f6i->fib6_flags &= ~RTF_EXPIRES;
253-
f6i->expires = 0;
254-
}
255-
256-
static inline void fib6_set_expires(struct fib6_info *f6i,
257-
unsigned long expires)
258-
{
259-
f6i->expires = expires;
260-
f6i->fib6_flags |= RTF_EXPIRES;
261-
}
262-
263253
static inline bool fib6_check_expired(const struct fib6_info *f6i)
264254
{
265255
if (f6i->fib6_flags & RTF_EXPIRES)
266256
return time_after(jiffies, f6i->expires);
267257
return false;
268258
}
269259

260+
static inline bool fib6_has_expires(const struct fib6_info *f6i)
261+
{
262+
return f6i->fib6_flags & RTF_EXPIRES;
263+
}
264+
270265
/* Function to safely get fn->fn_sernum for passed in rt
271266
* and store result in passed in cookie.
272267
* Return true if we can get cookie safely
@@ -388,6 +383,7 @@ struct fib6_table {
388383
struct inet_peer_base tb6_peers;
389384
unsigned int flags;
390385
unsigned int fib_seq;
386+
struct hlist_head tb6_gc_hlist; /* GC candidates */
391387
#define RT6_TABLE_HAS_DFLT_ROUTER BIT(0)
392388
};
393389

@@ -504,6 +500,48 @@ void fib6_gc_cleanup(void);
504500

505501
int fib6_init(void);
506502

503+
/* fib6_info must be locked by the caller, and fib6_info->fib6_table can be
504+
* NULL.
505+
*/
506+
static inline void fib6_set_expires_locked(struct fib6_info *f6i,
507+
unsigned long expires)
508+
{
509+
struct fib6_table *tb6;
510+
511+
tb6 = f6i->fib6_table;
512+
f6i->expires = expires;
513+
if (tb6 && !fib6_has_expires(f6i))
514+
hlist_add_head(&f6i->gc_link, &tb6->tb6_gc_hlist);
515+
f6i->fib6_flags |= RTF_EXPIRES;
516+
}
517+
518+
/* fib6_info must be locked by the caller, and fib6_info->fib6_table can be
519+
* NULL. If fib6_table is NULL, the fib6_info will no be inserted into the
520+
* list of GC candidates until it is inserted into a table.
521+
*/
522+
static inline void fib6_set_expires(struct fib6_info *f6i,
523+
unsigned long expires)
524+
{
525+
spin_lock_bh(&f6i->fib6_table->tb6_lock);
526+
fib6_set_expires_locked(f6i, expires);
527+
spin_unlock_bh(&f6i->fib6_table->tb6_lock);
528+
}
529+
530+
static inline void fib6_clean_expires_locked(struct fib6_info *f6i)
531+
{
532+
if (fib6_has_expires(f6i))
533+
hlist_del_init(&f6i->gc_link);
534+
f6i->fib6_flags &= ~RTF_EXPIRES;
535+
f6i->expires = 0;
536+
}
537+
538+
static inline void fib6_clean_expires(struct fib6_info *f6i)
539+
{
540+
spin_lock_bh(&f6i->fib6_table->tb6_lock);
541+
fib6_clean_expires_locked(f6i);
542+
spin_unlock_bh(&f6i->fib6_table->tb6_lock);
543+
}
544+
507545
struct ipv6_route_iter {
508546
struct seq_net_private p;
509547
struct fib6_walker w;

net/ipv6/ip6_fib.c

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ struct fib6_info *fib6_info_alloc(gfp_t gfp_flags, bool with_fib6_nh)
160160
INIT_LIST_HEAD(&f6i->fib6_siblings);
161161
refcount_set(&f6i->fib6_ref, 1);
162162

163+
INIT_HLIST_NODE(&f6i->gc_link);
164+
163165
return f6i;
164166
}
165167

@@ -246,6 +248,7 @@ static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
246248
net->ipv6.fib6_null_entry);
247249
table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
248250
inet_peer_base_init(&table->tb6_peers);
251+
INIT_HLIST_HEAD(&table->tb6_gc_hlist);
249252
}
250253

251254
return table;
@@ -1057,6 +1060,8 @@ static void fib6_purge_rt(struct fib6_info *rt, struct fib6_node *fn,
10571060
lockdep_is_held(&table->tb6_lock));
10581061
}
10591062
}
1063+
1064+
fib6_clean_expires_locked(rt);
10601065
}
10611066

10621067
/*
@@ -1118,9 +1123,10 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct fib6_info *rt,
11181123
if (!(iter->fib6_flags & RTF_EXPIRES))
11191124
return -EEXIST;
11201125
if (!(rt->fib6_flags & RTF_EXPIRES))
1121-
fib6_clean_expires(iter);
1126+
fib6_clean_expires_locked(iter);
11221127
else
1123-
fib6_set_expires(iter, rt->expires);
1128+
fib6_set_expires_locked(iter,
1129+
rt->expires);
11241130

11251131
if (rt->fib6_pmtu)
11261132
fib6_metric_set(iter, RTAX_MTU,
@@ -1479,6 +1485,10 @@ int fib6_add(struct fib6_node *root, struct fib6_info *rt,
14791485
if (rt->nh)
14801486
list_add(&rt->nh_list, &rt->nh->f6i_list);
14811487
__fib6_update_sernum_upto_root(rt, fib6_new_sernum(info->nl_net));
1488+
1489+
if (fib6_has_expires(rt))
1490+
hlist_add_head(&rt->gc_link, &table->tb6_gc_hlist);
1491+
14821492
fib6_start_gc(info->nl_net, rt);
14831493
}
14841494

@@ -2285,17 +2295,16 @@ static void fib6_flush_trees(struct net *net)
22852295
* Garbage collection
22862296
*/
22872297

2288-
static int fib6_age(struct fib6_info *rt, void *arg)
2298+
static int fib6_age(struct fib6_info *rt, struct fib6_gc_args *gc_args)
22892299
{
2290-
struct fib6_gc_args *gc_args = arg;
22912300
unsigned long now = jiffies;
22922301

22932302
/*
22942303
* check addrconf expiration here.
22952304
* Routes are expired even if they are in use.
22962305
*/
22972306

2298-
if (rt->fib6_flags & RTF_EXPIRES && rt->expires) {
2307+
if (fib6_has_expires(rt) && rt->expires) {
22992308
if (time_after(now, rt->expires)) {
23002309
RT6_TRACE("expiring %p\n", rt);
23012310
return -1;
@@ -2312,6 +2321,40 @@ static int fib6_age(struct fib6_info *rt, void *arg)
23122321
return 0;
23132322
}
23142323

2324+
static void fib6_gc_table(struct net *net,
2325+
struct fib6_table *tb6,
2326+
struct fib6_gc_args *gc_args)
2327+
{
2328+
struct fib6_info *rt;
2329+
struct hlist_node *n;
2330+
struct nl_info info = {
2331+
.nl_net = net,
2332+
.skip_notify = false,
2333+
};
2334+
2335+
hlist_for_each_entry_safe(rt, n, &tb6->tb6_gc_hlist, gc_link)
2336+
if (fib6_age(rt, gc_args) == -1)
2337+
fib6_del(rt, &info);
2338+
}
2339+
2340+
static void fib6_gc_all(struct net *net, struct fib6_gc_args *gc_args)
2341+
{
2342+
struct fib6_table *table;
2343+
struct hlist_head *head;
2344+
unsigned int h;
2345+
2346+
rcu_read_lock();
2347+
for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
2348+
head = &net->ipv6.fib_table_hash[h];
2349+
hlist_for_each_entry_rcu(table, head, tb6_hlist) {
2350+
spin_lock_bh(&table->tb6_lock);
2351+
fib6_gc_table(net, table, gc_args);
2352+
spin_unlock_bh(&table->tb6_lock);
2353+
}
2354+
}
2355+
rcu_read_unlock();
2356+
}
2357+
23152358
void fib6_run_gc(unsigned long expires, struct net *net, bool force)
23162359
{
23172360
struct fib6_gc_args gc_args;
@@ -2327,7 +2370,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force)
23272370
net->ipv6.sysctl.ip6_rt_gc_interval;
23282371
gc_args.more = 0;
23292372

2330-
fib6_clean_all(net, fib6_age, &gc_args);
2373+
fib6_gc_all(net, &gc_args);
23312374
now = jiffies;
23322375
net->ipv6.ip6_rt_last_gc = now;
23332376

net/ipv6/route.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3761,10 +3761,10 @@ static struct fib6_info *ip6_route_info_create(struct fib6_config *cfg,
37613761
rt->dst_nocount = true;
37623762

37633763
if (cfg->fc_flags & RTF_EXPIRES)
3764-
fib6_set_expires(rt, jiffies +
3765-
clock_t_to_jiffies(cfg->fc_expires));
3764+
fib6_set_expires_locked(rt, jiffies +
3765+
clock_t_to_jiffies(cfg->fc_expires));
37663766
else
3767-
fib6_clean_expires(rt);
3767+
fib6_clean_expires_locked(rt);
37683768

37693769
if (cfg->fc_protocol == RTPROT_UNSPEC)
37703770
cfg->fc_protocol = RTPROT_BOOT;

0 commit comments

Comments
 (0)