Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2ac3ac8

Browse files
mkubecekdavem330
authored andcommitted
ipv6: prevent fib6_run_gc() contention
On a high-traffic router with many processors and many IPv6 dst entries, soft lockup in fib6_run_gc() can occur when number of entries reaches gc_thresh. This happens because fib6_run_gc() uses fib6_gc_lock to allow only one thread to run the garbage collector but ip6_dst_gc() doesn't update net->ipv6.ip6_rt_last_gc until fib6_run_gc() returns. On a system with many entries, this can take some time so that in the meantime, other threads pass the tests in ip6_dst_gc() (ip6_rt_last_gc is still not updated) and wait for the lock. They then have to run the garbage collector one after another which blocks them for quite long. Resolve this by replacing special value ~0UL of expire parameter to fib6_run_gc() by explicit "force" parameter to choose between spin_lock_bh() and spin_trylock_bh() and call fib6_run_gc() with force=false if gc_thresh is reached but not max_size. Signed-off-by: Michal Kubecek <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 1f1059f commit 2ac3ac8

File tree

4 files changed

+13
-16
lines changed

4 files changed

+13
-16
lines changed

include/net/ip6_fib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ extern void inet6_rt_notify(int event, struct rt6_info *rt,
300300
struct nl_info *info);
301301

302302
extern void fib6_run_gc(unsigned long expires,
303-
struct net *net);
303+
struct net *net, bool force);
304304

305305
extern void fib6_gc_cleanup(void);
306306

net/ipv6/ip6_fib.c

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,19 +1632,16 @@ static int fib6_age(struct rt6_info *rt, void *arg)
16321632

16331633
static DEFINE_SPINLOCK(fib6_gc_lock);
16341634

1635-
void fib6_run_gc(unsigned long expires, struct net *net)
1635+
void fib6_run_gc(unsigned long expires, struct net *net, bool force)
16361636
{
1637-
if (expires != ~0UL) {
1637+
if (force) {
16381638
spin_lock_bh(&fib6_gc_lock);
1639-
gc_args.timeout = expires ? (int)expires :
1640-
net->ipv6.sysctl.ip6_rt_gc_interval;
1641-
} else {
1642-
if (!spin_trylock_bh(&fib6_gc_lock)) {
1643-
mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
1644-
return;
1645-
}
1646-
gc_args.timeout = net->ipv6.sysctl.ip6_rt_gc_interval;
1639+
} else if (!spin_trylock_bh(&fib6_gc_lock)) {
1640+
mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
1641+
return;
16471642
}
1643+
gc_args.timeout = expires ? (int)expires :
1644+
net->ipv6.sysctl.ip6_rt_gc_interval;
16481645

16491646
gc_args.more = icmp6_dst_gc();
16501647

@@ -1661,7 +1658,7 @@ void fib6_run_gc(unsigned long expires, struct net *net)
16611658

16621659
static void fib6_gc_timer_cb(unsigned long arg)
16631660
{
1664-
fib6_run_gc(0, (struct net *)arg);
1661+
fib6_run_gc(0, (struct net *)arg, true);
16651662
}
16661663

16671664
static int __net_init fib6_net_init(struct net *net)

net/ipv6/ndisc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1576,7 +1576,7 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
15761576
switch (event) {
15771577
case NETDEV_CHANGEADDR:
15781578
neigh_changeaddr(&nd_tbl, dev);
1579-
fib6_run_gc(~0UL, net);
1579+
fib6_run_gc(0, net, false);
15801580
idev = in6_dev_get(dev);
15811581
if (!idev)
15821582
break;
@@ -1586,7 +1586,7 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
15861586
break;
15871587
case NETDEV_DOWN:
15881588
neigh_ifdown(&nd_tbl, dev);
1589-
fib6_run_gc(~0UL, net);
1589+
fib6_run_gc(0, net, false);
15901590
break;
15911591
case NETDEV_NOTIFY_PEERS:
15921592
ndisc_send_unsol_na(dev);

net/ipv6/route.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,7 @@ static int ip6_dst_gc(struct dst_ops *ops)
13261326
goto out;
13271327

13281328
net->ipv6.ip6_rt_gc_expire++;
1329-
fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net);
1329+
fib6_run_gc(net->ipv6.ip6_rt_gc_expire, net, entries > rt_max_size);
13301330
net->ipv6.ip6_rt_last_gc = now;
13311331
entries = dst_entries_get_slow(ops);
13321332
if (entries < ops->gc_thresh)
@@ -2827,7 +2827,7 @@ int ipv6_sysctl_rtcache_flush(struct ctl_table *ctl, int write,
28272827
net = (struct net *)ctl->extra1;
28282828
delay = net->ipv6.sysctl.flush_delay;
28292829
proc_dointvec(ctl, write, buffer, lenp, ppos);
2830-
fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net);
2830+
fib6_run_gc(delay <= 0 ? 0 : (unsigned long)delay, net, delay > 0);
28312831
return 0;
28322832
}
28332833

0 commit comments

Comments
 (0)