summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWANG Cong <xiyou.wangcong@gmail.com>2017-06-20 11:42:27 -0700
committerBen Hutchings <ben@decadent.org.uk>2017-09-15 18:30:14 +0100
commit295302357e7ebc359e07cf8404e8dad4a51dd674 (patch)
treefb331f174a75668b04c6134b6f19c6f6fc1bf424
parent10b096d5125d3909b0cc593a96d018fb2581c46b (diff)
downloadlinux-295302357e7ebc359e07cf8404e8dad4a51dd674.tar.gz
linux-295302357e7ebc359e07cf8404e8dad4a51dd674.tar.bz2
linux-295302357e7ebc359e07cf8404e8dad4a51dd674.zip
ipv6: only call ip6_route_dev_notify() once for NETDEV_UNREGISTER
commit 76da0704507bbc51875013f6557877ab308cfd0a upstream. In commit 242d3a49a2a1 ("ipv6: reorder ip6_route_dev_notifier after ipv6_dev_notf") I assumed NETDEV_REGISTER and NETDEV_UNREGISTER are paired, unfortunately, as reported by jeffy, netdev_wait_allrefs() could rebroadcast NETDEV_UNREGISTER event until all refs are gone. We have to add an additional check to avoid this corner case. For netdev_wait_allrefs() dev->reg_state is NETREG_UNREGISTERED, for dev_change_net_namespace(), dev->reg_state is NETREG_REGISTERED. So check for dev->reg_state != NETREG_UNREGISTERED. Fixes: 242d3a49a2a1 ("ipv6: reorder ip6_route_dev_notifier after ipv6_dev_notf") Reported-by: jeffy <jeffy.chen@rock-chips.com> Cc: David Ahern <dsahern@gmail.com> Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com> Acked-by: David Ahern <dsahern@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net> Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r--net/ipv6/route.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 286beb459faa..af2bc34cb81f 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2835,7 +2835,11 @@ static int ip6_route_dev_notify(struct notifier_block *this,
net->ipv6.ip6_blk_hole_entry->dst.dev = dev;
net->ipv6.ip6_blk_hole_entry->rt6i_idev = in6_dev_get(dev);
#endif
- } else if (event == NETDEV_UNREGISTER) {
+ } else if (event == NETDEV_UNREGISTER &&
+ dev->reg_state != NETREG_UNREGISTERED) {
+ /* NETDEV_UNREGISTER could be fired for multiple times by
+ * netdev_wait_allrefs(). Make sure we only call this once.
+ */
in6_dev_put(net->ipv6.ip6_null_entry->rt6i_idev);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
in6_dev_put(net->ipv6.ip6_prohibit_entry->rt6i_idev);