ipv6: On interface down/unregister, purge icmp routes too.
authorDavid S. Miller <davem@davemloft.net>
Thu, 11 Sep 2008 00:27:15 +0000 (17:27 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 11 Sep 2008 06:39:28 +0000 (23:39 -0700)
Johannes Berg reported that occaisionally, bringing an interface
down or unregistering it would hang for up to 30 seconds.  Using
debugging output he provided it became clear that ICMP6 routes
were the culprit.

The problem is that ICMP6 routes live in their own world totally
separate from normal ipv6 routes.  So there are all kinds of special
cases throughout the ipv6 code to handle this.

While we should really try to unify all of this stuff somehow,
for the time being let's fix this by purging the ICMP6 routes
that match the device in question during rt6_ifdown().

Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/route.c

index 9af6115f0f500822c94ff6cb751ab24cd34de911..776871ee22886f41631504fd6a9b740d909df9d7 100644 (file)
@@ -1003,6 +1003,25 @@ int icmp6_dst_gc(void)
        return more;
 }
 
+static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
+                           void *arg)
+{
+       struct dst_entry *dst, **pprev;
+
+       spin_lock_bh(&icmp6_dst_lock);
+       pprev = &icmp6_dst_gc_list;
+       while ((dst = *pprev) != NULL) {
+               struct rt6_info *rt = (struct rt6_info *) dst;
+               if (func(rt, arg)) {
+                       *pprev = dst->next;
+                       dst_free(dst);
+               } else {
+                       pprev = &dst->next;
+               }
+       }
+       spin_unlock_bh(&icmp6_dst_lock);
+}
+
 static int ip6_dst_gc(struct dst_ops *ops)
 {
        unsigned long now = jiffies;
@@ -1930,6 +1949,7 @@ void rt6_ifdown(struct net *net, struct net_device *dev)
        };
 
        fib6_clean_all(net, fib6_ifdown, 0, &adn);
+       icmp6_clean_all(fib6_ifdown, &adn);
 }
 
 struct rt6_mtu_change_arg