diff options
author | Ingo Molnar <[email protected]> | 2016-06-08 09:26:46 +0200 |
---|---|---|
committer | Ingo Molnar <[email protected]> | 2016-06-08 09:26:46 +0200 |
commit | 616d1c1b98ac79f30216a57a170dd7cea19b3df3 (patch) | |
tree | 6f244c2e5a7160190e73bc82b4cd7fa7bb22ee31 /net/ipv4/fib_semantics.c | |
parent | a4f144ebbdf6f7807c477bce8e136047ed27321f (diff) | |
parent | c8ae067f2635be0f8c7e5db1bb74b757d623e05b (diff) |
Merge branch 'linus' into perf/core, to refresh the branch
Signed-off-by: Ingo Molnar <[email protected]>
Diffstat (limited to 'net/ipv4/fib_semantics.c')
-rw-r--r-- | net/ipv4/fib_semantics.c | 34 |
1 files changed, 29 insertions, 5 deletions
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 2b68418c7198..d09173bf9500 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1561,21 +1561,45 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags) } #ifdef CONFIG_IP_ROUTE_MULTIPATH +static bool fib_good_nh(const struct fib_nh *nh) +{ + int state = NUD_REACHABLE; + + if (nh->nh_scope == RT_SCOPE_LINK) { + struct neighbour *n; + + rcu_read_lock_bh(); + + n = __ipv4_neigh_lookup_noref(nh->nh_dev, nh->nh_gw); + if (n) + state = n->nud_state; + + rcu_read_unlock_bh(); + } + + return !!(state & NUD_VALID); +} void fib_select_multipath(struct fib_result *res, int hash) { struct fib_info *fi = res->fi; + struct net *net = fi->fib_net; + bool first = false; for_nexthops(fi) { if (hash > atomic_read(&nh->nh_upper_bound)) continue; - res->nh_sel = nhsel; - return; + if (!net->ipv4.sysctl_fib_multipath_use_neigh || + fib_good_nh(nh)) { + res->nh_sel = nhsel; + return; + } + if (!first) { + res->nh_sel = nhsel; + first = true; + } } endfor_nexthops(fi); - - /* Race condition: route has just become dead. */ - res->nh_sel = 0; } #endif |