]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
ipv6: do not call ndisc_send_rs() with write lock
authorCong Wang <amwang@redhat.com>
Sat, 31 Aug 2013 05:44:32 +0000 (13:44 +0800)
committerDavid S. Miller <davem@davemloft.net>
Sun, 1 Sep 2013 02:30:00 +0000 (22:30 -0400)
Because vxlan module will call ip6_dst_lookup() in TX path,
which will hold write lock. So we have to release this write lock
before calling ndisc_send_rs(), otherwise could deadlock.

Reviewed-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: Cong Wang <amwang@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/addrconf.c

index 619451336bf1fb6a232decd3f15ec44ef29501e0..baaaead69ee1d0107d09a6797a5246f42ea3b757 100644 (file)
@@ -3090,6 +3090,7 @@ static int addrconf_ifdown(struct net_device *dev, int how)
 static void addrconf_rs_timer(unsigned long data)
 {
        struct inet6_dev *idev = (struct inet6_dev *)data;
+       struct net_device *dev = idev->dev;
        struct in6_addr lladdr;
 
        write_lock(&idev->lock);
@@ -3104,12 +3105,14 @@ static void addrconf_rs_timer(unsigned long data)
                goto out;
 
        if (idev->rs_probes++ < idev->cnf.rtr_solicits) {
-               if (!__ipv6_get_lladdr(idev, &lladdr, IFA_F_TENTATIVE))
-                       ndisc_send_rs(idev->dev, &lladdr,
+               write_unlock(&idev->lock);
+               if (!ipv6_get_lladdr(dev, &lladdr, IFA_F_TENTATIVE))
+                       ndisc_send_rs(dev, &lladdr,
                                      &in6addr_linklocal_allrouters);
                else
-                       goto out;
+                       goto put;
 
+               write_lock(&idev->lock);
                /* The wait after the last probe can be shorter */
                addrconf_mod_rs_timer(idev, (idev->rs_probes ==
                                             idev->cnf.rtr_solicits) ?
@@ -3125,6 +3128,7 @@ static void addrconf_rs_timer(unsigned long data)
 
 out:
        write_unlock(&idev->lock);
+put:
        in6_dev_put(idev);
 }