From: Li RongQing <roy.qing...@gmail.com> 1. nothing of idev is changed, so read lock is enough 2. ifp is changed, so used ifp->lock or cmpxchg to protect it
Signed-off-by: Li RongQing <roy.qing...@gmail.com> --- net/ipv6/addrconf.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 27aed1a..f6e7605b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3184,14 +3184,21 @@ static void addrconf_gre_config(struct net_device *dev) static void l3mdev_check_host_rt(struct inet6_dev *idev, struct inet6_ifaddr *ifp) { + struct rt6_info *rt = NULL; + + spin_lock(&ifp->lock); if (ifp->rt) { u32 tb_id = l3mdev_fib_table(idev->dev) ? : RT6_TABLE_LOCAL; if (tb_id != ifp->rt->rt6i_table->tb6_id) { - ip6_del_rt(ifp->rt); + rt = ifp->rt; ifp->rt = NULL; } } + spin_unlock(&ifp->lock); + + if (rt) + ip6_del_rt(rt); } #else static void l3mdev_check_host_rt(struct inet6_dev *idev, @@ -3203,6 +3210,8 @@ static void l3mdev_check_host_rt(struct inet6_dev *idev, static int fixup_permanent_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) { + struct rt6_info *prev; + l3mdev_check_host_rt(idev, ifp); if (!ifp->rt) { @@ -3212,7 +3221,12 @@ static int fixup_permanent_addr(struct inet6_dev *idev, if (unlikely(IS_ERR(rt))) return PTR_ERR(rt); - ifp->rt = rt; + prev = cmpxchg(&ifp->rt, NULL, rt); + + /*if cmpxchg failed*/ + if (prev) { + ip6_rt_put(rt); + } } if (!(ifp->flags & IFA_F_NOPREFIXROUTE)) { @@ -3234,21 +3248,21 @@ static void addrconf_permanent_addr(struct net_device *dev) if (!idev) return; - write_lock_bh(&idev->lock); + read_lock_bh(&idev->lock); list_for_each_entry_safe(ifp, tmp, &idev->addr_list, if_list) { if ((ifp->flags & IFA_F_PERMANENT) && fixup_permanent_addr(idev, ifp) < 0) { - write_unlock_bh(&idev->lock); + read_unlock_bh(&idev->lock); ipv6_del_addr(ifp); - write_lock_bh(&idev->lock); + read_lock_bh(&idev->lock); net_info_ratelimited("%s: Failed to add prefix route for address %pI6c; dropping\n", idev->dev->name, &ifp->addr); } } - write_unlock_bh(&idev->lock); + read_unlock_bh(&idev->lock); } static int addrconf_notify(struct notifier_block *this, unsigned long event, -- 2.1.4