diff options
| author | WANG Cong <xiyou.wangcong@gmail.com> | 2017-06-20 11:42:27 -0700 |
|---|---|---|
| committer | Ben Hutchings <ben@decadent.org.uk> | 2017-09-15 18:30:14 +0100 |
| commit | 295302357e7ebc359e07cf8404e8dad4a51dd674 (patch) | |
| tree | fb331f174a75668b04c6134b6f19c6f6fc1bf424 | |
| parent | 10b096d5125d3909b0cc593a96d018fb2581c46b (diff) | |
| download | linux-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.c | 6 |
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); |
