]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv6/ip6_fib.c
ipv6: prevent fib6_run_gc() contention
[karo-tx-linux.git] / net / ipv6 / ip6_fib.c
index 192dd1a0e18810f01923b43ef5f4a75c5b5d8d35..d872553ca933078061459dbc9fcc61d0764cf689 100644 (file)
@@ -632,6 +632,12 @@ insert_above:
        return ln;
 }
 
+static inline bool rt6_qualify_for_ecmp(struct rt6_info *rt)
+{
+       return (rt->rt6i_flags & (RTF_GATEWAY|RTF_ADDRCONF|RTF_DYNAMIC)) ==
+              RTF_GATEWAY;
+}
+
 /*
  *     Insert routing information in a node.
  */
@@ -646,6 +652,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
        int add = (!info->nlh ||
                   (info->nlh->nlmsg_flags & NLM_F_CREATE));
        int found = 0;
+       bool rt_can_ecmp = rt6_qualify_for_ecmp(rt);
 
        ins = &fn->leaf;
 
@@ -691,9 +698,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
                         * To avoid long list, we only had siblings if the
                         * route have a gateway.
                         */
-                       if (rt->rt6i_flags & RTF_GATEWAY &&
-                           !(rt->rt6i_flags & RTF_EXPIRES) &&
-                           !(iter->rt6i_flags & RTF_EXPIRES))
+                       if (rt_can_ecmp &&
+                           rt6_qualify_for_ecmp(iter))
                                rt->rt6i_nsiblings++;
                }
 
@@ -715,7 +721,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
                /* Find the first route that have the same metric */
                sibling = fn->leaf;
                while (sibling) {
-                       if (sibling->rt6i_metric == rt->rt6i_metric) {
+                       if (sibling->rt6i_metric == rt->rt6i_metric &&
+                           rt6_qualify_for_ecmp(sibling)) {
                                list_add_tail(&rt->rt6i_siblings,
                                              &sibling->rt6i_siblings);
                                break;
@@ -1625,19 +1632,16 @@ static int fib6_age(struct rt6_info *rt, void *arg)
 
 static DEFINE_SPINLOCK(fib6_gc_lock);
 
-void fib6_run_gc(unsigned long expires, struct net *net)
+void fib6_run_gc(unsigned long expires, struct net *net, bool force)
 {
-       if (expires != ~0UL) {
+       if (force) {
                spin_lock_bh(&fib6_gc_lock);
-               gc_args.timeout = expires ? (int)expires :
-                       net->ipv6.sysctl.ip6_rt_gc_interval;
-       } else {
-               if (!spin_trylock_bh(&fib6_gc_lock)) {
-                       mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
-                       return;
-               }
-               gc_args.timeout = net->ipv6.sysctl.ip6_rt_gc_interval;
+       } else if (!spin_trylock_bh(&fib6_gc_lock)) {
+               mod_timer(&net->ipv6.ip6_fib_timer, jiffies + HZ);
+               return;
        }
+       gc_args.timeout = expires ? (int)expires :
+                         net->ipv6.sysctl.ip6_rt_gc_interval;
 
        gc_args.more = icmp6_dst_gc();
 
@@ -1654,7 +1658,7 @@ void fib6_run_gc(unsigned long expires, struct net *net)
 
 static void fib6_gc_timer_cb(unsigned long arg)
 {
-       fib6_run_gc(0, (struct net *)arg);
+       fib6_run_gc(0, (struct net *)arg, true);
 }
 
 static int __net_init fib6_net_init(struct net *net)