ipv6: per netns FIB6 walkers and garbage collector
Subsystems (3)
v2 cover ipv6: per netns FIB6 walkers and garbage collector
Commit 2ac3ac8f86f2 ("ipv6: prevent fib6_run_gc() contention") reduced the risk of contention on FIB6 garbage collector lock on systems with many CPUs. However, one of our customers can still observe heavy contention on fib6_gc_lock which can even trigger the soft lockup detector.
This is caused by garbage collector running in forced mode from a timer. While there is one timer per network namespace, the instances of fib6_run_gc() running from them are protected by one global spinlock so that only one garbage collector can run at any moment and other namespaces have to wait. As most relevant data structures are separated per netns, there is little reason for garbage collectors blocking each other.
Similar problem exists for walkers: changes in one tree do not need to adjust (and block) walkers traversing FIB trees in other namespaces.
This series separates both the walkers infrastructure and garbage collector so that they work independently in network namespaces.
v2: get rid of ifdef in ipv6_route_seq_setup_walk(), pass net from callers instead
Michal Kubecek (3):
ipv6: replace global gc_args with local variable
ipv6: per netns fib6 walkers
ipv6: per netns FIB garbage collection
include/net/netns/ipv6.h | 3 ++ net/ipv6/ip6_fib.c | 91 +++++++++++++++++++++++++----------------------- 2 files changed, 51 insertions(+), 43 deletions(-)
Signature · 1 line
2.7.2
PATCH v2 1/3 ipv6: replace global gc_args with local variable
Global variable gc_args is only used in fib6_run_gc() and functions called from it. As fib6_run_gc() makes sure there is at most one instance of fib6_clean_all() running at any moment, we can replace gc_args with a local variable which will be needed once multiple instances (per netns) of garbage collector are allowed.
--- net/ipv6/ip6_fib.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-)
net/ipv6/ip6_fib.c +8 -6
@@ -1725,14 +1725,15 @@ static void fib6_flush_trees(struct net *net)* Garbage collection */ -static struct fib6_gc_args +struct fib6_gc_args {int timeout;int more; -} gc_args; +};static int fib6_age (struct rt6_info *rt,void *arg) { +struct fib6_gc_args *gc_args = arg;unsigned long now = jiffies;/*
@@ -1748,10 +1749,10 @@ static int fib6_age(struct rt6_info *rt, void *arg)RT6_TRACE ("expiring %p\n" , rt);return -1 ; } - gc_args.more++; + gc_args->more++; }else if (rt->rt6i_flags &RTF_CACHE ) {if (atomic_read (&rt->dst.__refcnt) ==0 && -time_after_eq (now, rt->dst.lastuse + gc_args.timeout)) { +time_after_eq (now, rt->dst.lastuse + gc_args->timeout)) {RT6_TRACE ("aging clone %p\n" , rt);return -1 ; }else if (rt->rt6i_flags &RTF_GATEWAY ) {
@@ -1769,7 +1770,7 @@ static int fib6_age(struct rt6_info *rt, void *arg)return -1 ; } } - gc_args.more++; + gc_args->more++; }return 0 ;
@@ -1779,6 +1780,7 @@ static DEFINE_SPINLOCK(fib6_gc_lock);void fib6_run_gc (unsigned long expires,struct net *net,bool force) { +struct fib6_gc_args gc_args;unsigned long now;if (force) {
@@ -1792,7 +1794,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force) gc_args.more =icmp6_dst_gc (); -fib6_clean_all (net, fib6_age,NULL ); +fib6_clean_all (net, fib6_age, &gc_args); now = jiffies; net->ipv6.ip6_rt_last_gc = now;
Signature · 1 line
2.7.2
Re: [PATCH net-next v2 1/3] ipv6: replace global gc_args with local variable
Quoted 7 lines · Michal Kubecek
Global variable
gc_argsis only used infib6_run_gc()and functions called from it. Asfib6_run_gc()makes sure there is at most one instance offib6_clean_all()running at any moment, we can replacegc_argswith a local variable which will be needed once multiple instances (per netns) of garbage collector are allowed.Signed-off-by Michal Kubecek <[email protected]>
Thanks.
PATCH v2 2/3 ipv6: per netns fib6 walkers
In reply to Michal KubecekThe IPv6 FIB data structures are separated per network namespace but there is still only one global walkers list and one global walker list lock. This means changes in one namespace unnecessarily interfere with walkers in other namespaces.
Replace the global list with per-netns lists (and give each its own lock).
---
v2: get rid of ifdef in ipv6_route_seq_setup_walk(), pass net from callers instead
--- include/net/netns/ipv6.h | 2 ++ net/ipv6/ip6_fib.c | 68 +++++++++++++++++++++++++----------------------- 2 files changed, 38 insertions(+), 32 deletions(-)
include/net/netns/ipv6.h +2 -0
@@ -58,7 +58,9 @@ struct netns_ipv6 {struct timer_list ip6_fib_timer;struct hlist_head *fib_table_hash;struct fib6_table *fib6_main_tbl; +struct list_head fib6_walkers;struct dst_ops ip6_dst_ops; +rwlock_t fib6_walker_lock;unsigned int ip6_rt_gc_expire;unsigned long ip6_rt_last_gc;#ifdef CONFIG_IPV6_MULTIPLE_TABLES
net/ipv6/ip6_fib.c +36 -32
211 lines not on this page · raw message
Signature · 1 line
2.7.2
Re: [PATCH net-next v2 2/3] ipv6: per netns fib6 walkers
Quoted 5 lines · Michal Kubecek
The IPv6 FIB data structures are separated per network namespace but there is still only one global walkers list and one global walker list lock. This means changes in one namespace unnecessarily interfere with walkers in other namespaces.
Quoted 7 lines
Replace the global list with per-netns lists (and give each its own lock).
Signed-off-by Michal Kubecek <[email protected]>---
v2: get rid of ifdef inipv6_route_seq_setup_walk(), pass net from callers instead
PATCH v2 3/3 ipv6: per netns FIB garbage collection
In reply to Michal KubecekOne of our customers observed issues with FIB6 garbage collectors running in different network namespaces blocking each other, resulting in soft lockups (fib6_run_gc() initiated from timer runs always in forced mode).
Now that FIB6 walkers are separated per namespace, there is no more need for instances of fib6_run_gc() in different namespaces blocking each other. There is still a call to icmp6_dst_gc() which operates on shared data but this function is protected by its own shared lock.
--- include/net/netns/ipv6.h | 1 + net/ipv6/ip6_fib.c | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-)
include/net/netns/ipv6.h +1 -0
@@ -61,6 +61,7 @@ struct netns_ipv6 {struct list_head fib6_walkers;struct dst_ops ip6_dst_ops;rwlock_t fib6_walker_lock; +spinlock_t fib6_gc_lock;unsigned int ip6_rt_gc_expire;unsigned long ip6_rt_last_gc;#ifdef CONFIG_IPV6_MULTIPLE_TABLES
net/ipv6/ip6_fib.c +4 -5
@@ -1776,16 +1776,14 @@ static int fib6_age(struct rt6_info *rt, void *arg)return 0 ; } -static DEFINE_SPINLOCK (fib6_gc_lock); -void fib6_run_gc (unsigned long expires,struct net *net,bool force) {struct fib6_gc_args gc_args;unsigned long now;if (force) { -spin_lock_bh (&fib6_gc_lock); - }else if (!spin_trylock_bh (&fib6_gc_lock)) { +spin_lock_bh (&net->ipv6.fib6_gc_lock); + }else if (!spin_trylock_bh (&net->ipv6.fib6_gc_lock)) {mod_timer (&net->ipv6.ip6_fib_timer, jiffies +HZ );return ; }
@@ -1804,7 +1802,7 @@ void fib6_run_gc(unsigned long expires, struct net *net, bool force) + net->ipv6.sysctl.ip6_rt_gc_interval)); else del_timer(&net->ipv6.ip6_fib_timer); -spin_unlock_bh (&fib6_gc_lock); +spin_unlock_bh (&net->ipv6.fib6_gc_lock); }static void fib6_gc_timer_cb (unsigned long arg)
@@ -1816,6 +1814,7 @@ static int __net_init fib6_net_init(struct net *net) {size_t size =sizeof (struct hlist_head ) *FIB6_TABLE_HASHSZ ; +spin_lock_init (&net->ipv6.fib6_gc_lock);rwlock_init (&net->ipv6.fib6_walker_lock);INIT_LIST_HEAD (&net->ipv6.fib6_walkers);setup_timer (&net->ipv6.ip6_fib_timer, fib6_gc_timer_cb, (unsigned long )net);
Signature · 1 line
2.7.2
Re: [PATCH net-next v2 3/3] ipv6: per netns FIB garbage collection
Quoted 11 lines · Michal Kubecek
One of our customers observed issues with FIB6 garbage collectors running in different network namespaces blocking each other, resulting in soft lockups (
fib6_run_gc()initiated from timer runs always in forced mode).Now that FIB6 walkers are separated per namespace, there is no more need for instances of
fib6_run_gc()in different namespaces blocking each other. There is still a call toicmp6_dst_gc()which operates on shared data but this function is protected by its own shared lock.Signed-off-by Michal Kubecek <[email protected]>
David Miller
maintainerFrom: Michal Kubecek <[email protected]>
Date: Tue, 8 Mar 2016 14:44:15 +0100 (CET)
Quoted 14 lines
Commit
2ac3ac8f86f2("ipv6: preventfib6_run_gc()contention") reduced the risk of contention on FIB6 garbage collector lock on systems with many CPUs. However, one of our customers can still observe heavy contention onfib6_gc_lockwhich can even trigger the soft lockup detector.This is caused by garbage collector running in forced mode from a timer. While there is one timer per network namespace, the instances of
fib6_run_gc()running from them are protected by one global spinlock so that only one garbage collector can run at any moment and other namespaces have to wait. As most relevant data structures are separated per netns, there is little reason for garbage collectors blocking each other.
Quoted 8 lines
Similar problem exists for walkers: changes in one tree do not need to adjust (and block) walkers traversing FIB trees in other namespaces.
This series separates both the walkers infrastructure and garbage collector so that they work independently in network namespaces.
v2: get rid of ifdef in
ipv6_route_seq_setup_walk(), pass net from callers instead
Series applied, thanks.