aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2008-09-10 17:27:15 -0700
committerDavid S. Miller <davem@davemloft.net>2008-09-10 23:39:28 -0700
commit1e493d1946a0b26b79001c18d7312d536156ff5a (patch)
tree2cc484a2f2ea5de5b427e6a8a7fa6f7d07ae05bc
parent08569908fffec3625e29eec7cf7577eaa512e719 (diff)
downloadkernel_samsung_smdk4412-1e493d1946a0b26b79001c18d7312d536156ff5a.zip
kernel_samsung_smdk4412-1e493d1946a0b26b79001c18d7312d536156ff5a.tar.gz
kernel_samsung_smdk4412-1e493d1946a0b26b79001c18d7312d536156ff5a.tar.bz2
ipv6: On interface down/unregister, purge icmp routes too.
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>
-rw-r--r--net/ipv6/route.c20
1 files changed, 20 insertions, 0 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 9af6115..776871e 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -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