From: Sabrina Dubroca Date: Wed, 10 Sep 2014 21:23:02 +0000 (+0200) Subject: ipv6: clean up anycast when an interface is destroyed X-Git-Tag: firefly_0821_release~4090^2~85 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=86a47ad60de5221c3869821d3552dcd1c89199f5;p=firefly-linux-kernel-4.4.55.git ipv6: clean up anycast when an interface is destroyed If we try to rmmod the driver for an interface while sockets with setsockopt(JOIN_ANYCAST) are alive, some refcounts aren't cleaned up and we get stuck on: unregister_netdevice: waiting for ens3 to become free. Usage count = 1 If we LEAVE_ANYCAST/close everything before rmmod'ing, there is no problem. We need to perform a cleanup similar to the one for multicast in addrconf_ifdown(how == 1). BUG: 18902601 Change-Id: I6d51aed5755eb5738fcba91950e7773a1c985d2e Signed-off-by: Sabrina Dubroca Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 96a8afe33c52..25d5a98f206c 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -196,6 +196,7 @@ extern int ipv6_dev_ac_inc(struct net_device *dev, const struct in6_addr *addr); extern int __ipv6_dev_ac_dec(struct inet6_dev *idev, const struct in6_addr *addr); extern bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev, const struct in6_addr *addr); +extern void ipv6_ac_destroy_dev(struct inet6_dev *idev); /* Device notifier */ diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 165cee964e75..adffa4747532 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3155,11 +3155,13 @@ static int addrconf_ifdown(struct net_device *dev, int how) write_unlock_bh(&idev->lock); - /* Step 5: Discard multicast list */ - if (how) + /* Step 5: Discard anycast and multicast list */ + if (how) { + ipv6_ac_destroy_dev(idev); ipv6_mc_destroy_dev(idev); - else + } else { ipv6_mc_down(idev); + } idev->tstamp = jiffies; diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c index 5a80f15a9de2..1e97d0886597 100644 --- a/net/ipv6/anycast.c +++ b/net/ipv6/anycast.c @@ -341,6 +341,27 @@ static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr) return __ipv6_dev_ac_dec(idev, addr); } +void ipv6_ac_destroy_dev(struct inet6_dev *idev) +{ + struct ifacaddr6 *aca; + + write_lock_bh(&idev->lock); + while ((aca = idev->ac_list) != NULL) { + idev->ac_list = aca->aca_next; + write_unlock_bh(&idev->lock); + + addrconf_leave_solict(idev, &aca->aca_addr); + + dst_hold(&aca->aca_rt->dst); + ip6_del_rt(aca->aca_rt); + + aca_put(aca); + + write_lock_bh(&idev->lock); + } + write_unlock_bh(&idev->lock); +} + /* * check if the interface has this anycast address * called with rcu_read_lock()