diff options
author | Amerigo Wang <amwang@redhat.com> | 2013-06-29 09:30:49 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-28 19:29:49 -0400 |
commit | 36bddbad5049d3f3916abc9f594526d47ad3ab84 (patch) | |
tree | d5aaf3bc7cc3fb8985fbaff65bc5b844b2173655 | |
parent | da04e7df0494a9a6ccfe2aada8a262e38c284914 (diff) |
ipv6,mcast: always hold idev->lock before mca_lock
[ Upstream commit 8965779d2c0e6ab246c82a405236b1fb2adae6b2, with
some bits from commit b7b1bfce0bb68bd8f6e62a28295922785cc63781
("ipv6: split duplicate address detection and router solicitation timer")
to get the __ipv6_get_lladdr() used by this patch. ]
dingtianhong reported the following deadlock detected by lockdep:
======================================================
[ INFO: possible circular locking dependency detected ]
3.4.24.05-0.1-default #1 Not tainted
-------------------------------------------------------
ksoftirqd/0/3 is trying to acquire lock:
(&ndev->lock){+.+...}, at: [<ffffffff8147f804>] ipv6_get_lladdr+0x74/0x120
but task is already holding lock:
(&mc->mca_lock){+.+...}, at: [<ffffffff8149d130>] mld_send_report+0x40/0x150
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #1 (&mc->mca_lock){+.+...}:
[<ffffffff810a8027>] validate_chain+0x637/0x730
[<ffffffff810a8417>] __lock_acquire+0x2f7/0x500
[<ffffffff810a8734>] lock_acquire+0x114/0x150
[<ffffffff814f691a>] rt_spin_lock+0x4a/0x60
[<ffffffff8149e4bb>] igmp6_group_added+0x3b/0x120
[<ffffffff8149e5d8>] ipv6_mc_up+0x38/0x60
[<ffffffff81480a4d>] ipv6_find_idev+0x3d/0x80
[<ffffffff81483175>] addrconf_notify+0x3d5/0x4b0
[<ffffffff814fae3f>] notifier_call_chain+0x3f/0x80
[<ffffffff81073471>] raw_notifier_call_chain+0x11/0x20
[<ffffffff813d8722>] call_netdevice_notifiers+0x32/0x60
[<ffffffff813d92d4>] __dev_notify_flags+0x34/0x80
[<ffffffff813d9360>] dev_change_flags+0x40/0x70
[<ffffffff813ea627>] do_setlink+0x237/0x8a0
[<ffffffff813ebb6c>] rtnl_newlink+0x3ec/0x600
[<ffffffff813eb4d0>] rtnetlink_rcv_msg+0x160/0x310
[<ffffffff814040b9>] netlink_rcv_skb+0x89/0xb0
[<ffffffff813eb357>] rtnetlink_rcv+0x27/0x40
[<ffffffff81403e20>] netlink_unicast+0x140/0x180
[<ffffffff81404a9e>] netlink_sendmsg+0x33e/0x380
[<ffffffff813c4252>] sock_sendmsg+0x112/0x130
[<ffffffff813c537e>] __sys_sendmsg+0x44e/0x460
[<ffffffff813c5544>] sys_sendmsg+0x44/0x70
[<ffffffff814feab9>] system_call_fastpath+0x16/0x1b
-> #0 (&ndev->lock){+.+...}:
[<ffffffff810a798e>] check_prev_add+0x3de/0x440
[<ffffffff810a8027>] validate_chain+0x637/0x730
[<ffffffff810a8417>] __lock_acquire+0x2f7/0x500
[<ffffffff810a8734>] lock_acquire+0x114/0x150
[<ffffffff814f6c82>] rt_read_lock+0x42/0x60
[<ffffffff8147f804>] ipv6_get_lladdr+0x74/0x120
[<ffffffff8149b036>] mld_newpack+0xb6/0x160
[<ffffffff8149b18b>] add_grhead+0xab/0xc0
[<ffffffff8149d03b>] add_grec+0x3ab/0x460
[<ffffffff8149d14a>] mld_send_report+0x5a/0x150
[<ffffffff8149f99e>] igmp6_timer_handler+0x4e/0xb0
[<ffffffff8105705a>] call_timer_fn+0xca/0x1d0
[<ffffffff81057b9f>] run_timer_softirq+0x1df/0x2e0
[<ffffffff8104e8c7>] handle_pending_softirqs+0xf7/0x1f0
[<ffffffff8104ea3b>] __do_softirq_common+0x7b/0xf0
[<ffffffff8104f07f>] __thread_do_softirq+0x1af/0x210
[<ffffffff8104f1c1>] run_ksoftirqd+0xe1/0x1f0
[<ffffffff8106c7de>] kthread+0xae/0xc0
[<ffffffff814fff74>] kernel_thread_helper+0x4/0x10
actually we can just hold idev->lock before taking pmc->mca_lock,
and avoid taking idev->lock again when iterating idev->addr_list,
since the upper callers of mld_newpack() already take
read_lock_bh(&idev->lock).
Reported-by: dingtianhong <dingtianhong@huawei.com>
Cc: dingtianhong <dingtianhong@huawei.com>
Cc: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Tested-by: Ding Tianhong <dingtianhong@huawei.com>
Tested-by: Chen Weilong <chenweilong@huawei.com>
Signed-off-by: Cong Wang <amwang@redhat.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | include/net/addrconf.h | 3 | ||||
-rw-r--r-- | net/ipv6/addrconf.c | 28 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 18 |
3 files changed, 31 insertions, 18 deletions
diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 21f702704f24..01b1a1ad77d2 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h | |||
@@ -86,6 +86,9 @@ extern int ipv6_dev_get_saddr(struct net *net, | |||
86 | const struct in6_addr *daddr, | 86 | const struct in6_addr *daddr, |
87 | unsigned int srcprefs, | 87 | unsigned int srcprefs, |
88 | struct in6_addr *saddr); | 88 | struct in6_addr *saddr); |
89 | extern int __ipv6_get_lladdr(struct inet6_dev *idev, | ||
90 | struct in6_addr *addr, | ||
91 | unsigned char banned_flags); | ||
89 | extern int ipv6_get_lladdr(struct net_device *dev, | 92 | extern int ipv6_get_lladdr(struct net_device *dev, |
90 | struct in6_addr *addr, | 93 | struct in6_addr *addr, |
91 | unsigned char banned_flags); | 94 | unsigned char banned_flags); |
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 4ab4c38958c6..fb8c94c4ab86 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -1448,6 +1448,23 @@ try_nextdev: | |||
1448 | } | 1448 | } |
1449 | EXPORT_SYMBOL(ipv6_dev_get_saddr); | 1449 | EXPORT_SYMBOL(ipv6_dev_get_saddr); |
1450 | 1450 | ||
1451 | int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, | ||
1452 | unsigned char banned_flags) | ||
1453 | { | ||
1454 | struct inet6_ifaddr *ifp; | ||
1455 | int err = -EADDRNOTAVAIL; | ||
1456 | |||
1457 | list_for_each_entry(ifp, &idev->addr_list, if_list) { | ||
1458 | if (ifp->scope == IFA_LINK && | ||
1459 | !(ifp->flags & banned_flags)) { | ||
1460 | *addr = ifp->addr; | ||
1461 | err = 0; | ||
1462 | break; | ||
1463 | } | ||
1464 | } | ||
1465 | return err; | ||
1466 | } | ||
1467 | |||
1451 | int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, | 1468 | int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, |
1452 | unsigned char banned_flags) | 1469 | unsigned char banned_flags) |
1453 | { | 1470 | { |
@@ -1457,17 +1474,8 @@ int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, | |||
1457 | rcu_read_lock(); | 1474 | rcu_read_lock(); |
1458 | idev = __in6_dev_get(dev); | 1475 | idev = __in6_dev_get(dev); |
1459 | if (idev) { | 1476 | if (idev) { |
1460 | struct inet6_ifaddr *ifp; | ||
1461 | |||
1462 | read_lock_bh(&idev->lock); | 1477 | read_lock_bh(&idev->lock); |
1463 | list_for_each_entry(ifp, &idev->addr_list, if_list) { | 1478 | err = __ipv6_get_lladdr(idev, addr, banned_flags); |
1464 | if (ifp->scope == IFA_LINK && | ||
1465 | !(ifp->flags & banned_flags)) { | ||
1466 | *addr = ifp->addr; | ||
1467 | err = 0; | ||
1468 | break; | ||
1469 | } | ||
1470 | } | ||
1471 | read_unlock_bh(&idev->lock); | 1479 | read_unlock_bh(&idev->lock); |
1472 | } | 1480 | } |
1473 | rcu_read_unlock(); | 1481 | rcu_read_unlock(); |
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index bfa6cc36ef2a..c3998c2bbc5a 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c | |||
@@ -1343,8 +1343,9 @@ static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb, | |||
1343 | hdr->daddr = *daddr; | 1343 | hdr->daddr = *daddr; |
1344 | } | 1344 | } |
1345 | 1345 | ||
1346 | static struct sk_buff *mld_newpack(struct net_device *dev, int size) | 1346 | static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size) |
1347 | { | 1347 | { |
1348 | struct net_device *dev = idev->dev; | ||
1348 | struct net *net = dev_net(dev); | 1349 | struct net *net = dev_net(dev); |
1349 | struct sock *sk = net->ipv6.igmp_sk; | 1350 | struct sock *sk = net->ipv6.igmp_sk; |
1350 | struct sk_buff *skb; | 1351 | struct sk_buff *skb; |
@@ -1369,7 +1370,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size) | |||
1369 | 1370 | ||
1370 | skb_reserve(skb, hlen); | 1371 | skb_reserve(skb, hlen); |
1371 | 1372 | ||
1372 | if (ipv6_get_lladdr(dev, &addr_buf, IFA_F_TENTATIVE)) { | 1373 | if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) { |
1373 | /* <draft-ietf-magma-mld-source-05.txt>: | 1374 | /* <draft-ietf-magma-mld-source-05.txt>: |
1374 | * use unspecified address as the source address | 1375 | * use unspecified address as the source address |
1375 | * when a valid link-local address is not available. | 1376 | * when a valid link-local address is not available. |
@@ -1465,7 +1466,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, | |||
1465 | struct mld2_grec *pgr; | 1466 | struct mld2_grec *pgr; |
1466 | 1467 | ||
1467 | if (!skb) | 1468 | if (!skb) |
1468 | skb = mld_newpack(dev, dev->mtu); | 1469 | skb = mld_newpack(pmc->idev, dev->mtu); |
1469 | if (!skb) | 1470 | if (!skb) |
1470 | return NULL; | 1471 | return NULL; |
1471 | pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec)); | 1472 | pgr = (struct mld2_grec *)skb_put(skb, sizeof(struct mld2_grec)); |
@@ -1485,7 +1486,8 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc, | |||
1485 | static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, | 1486 | static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, |
1486 | int type, int gdeleted, int sdeleted) | 1487 | int type, int gdeleted, int sdeleted) |
1487 | { | 1488 | { |
1488 | struct net_device *dev = pmc->idev->dev; | 1489 | struct inet6_dev *idev = pmc->idev; |
1490 | struct net_device *dev = idev->dev; | ||
1489 | struct mld2_report *pmr; | 1491 | struct mld2_report *pmr; |
1490 | struct mld2_grec *pgr = NULL; | 1492 | struct mld2_grec *pgr = NULL; |
1491 | struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list; | 1493 | struct ip6_sf_list *psf, *psf_next, *psf_prev, **psf_list; |
@@ -1514,7 +1516,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, | |||
1514 | AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { | 1516 | AVAILABLE(skb) < grec_size(pmc, type, gdeleted, sdeleted)) { |
1515 | if (skb) | 1517 | if (skb) |
1516 | mld_sendpack(skb); | 1518 | mld_sendpack(skb); |
1517 | skb = mld_newpack(dev, dev->mtu); | 1519 | skb = mld_newpack(idev, dev->mtu); |
1518 | } | 1520 | } |
1519 | } | 1521 | } |
1520 | first = 1; | 1522 | first = 1; |
@@ -1541,7 +1543,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc, | |||
1541 | pgr->grec_nsrcs = htons(scount); | 1543 | pgr->grec_nsrcs = htons(scount); |
1542 | if (skb) | 1544 | if (skb) |
1543 | mld_sendpack(skb); | 1545 | mld_sendpack(skb); |
1544 | skb = mld_newpack(dev, dev->mtu); | 1546 | skb = mld_newpack(idev, dev->mtu); |
1545 | first = 1; | 1547 | first = 1; |
1546 | scount = 0; | 1548 | scount = 0; |
1547 | } | 1549 | } |
@@ -1596,8 +1598,8 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) | |||
1596 | struct sk_buff *skb = NULL; | 1598 | struct sk_buff *skb = NULL; |
1597 | int type; | 1599 | int type; |
1598 | 1600 | ||
1601 | read_lock_bh(&idev->lock); | ||
1599 | if (!pmc) { | 1602 | if (!pmc) { |
1600 | read_lock_bh(&idev->lock); | ||
1601 | for (pmc=idev->mc_list; pmc; pmc=pmc->next) { | 1603 | for (pmc=idev->mc_list; pmc; pmc=pmc->next) { |
1602 | if (pmc->mca_flags & MAF_NOREPORT) | 1604 | if (pmc->mca_flags & MAF_NOREPORT) |
1603 | continue; | 1605 | continue; |
@@ -1609,7 +1611,6 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) | |||
1609 | skb = add_grec(skb, pmc, type, 0, 0); | 1611 | skb = add_grec(skb, pmc, type, 0, 0); |
1610 | spin_unlock_bh(&pmc->mca_lock); | 1612 | spin_unlock_bh(&pmc->mca_lock); |
1611 | } | 1613 | } |
1612 | read_unlock_bh(&idev->lock); | ||
1613 | } else { | 1614 | } else { |
1614 | spin_lock_bh(&pmc->mca_lock); | 1615 | spin_lock_bh(&pmc->mca_lock); |
1615 | if (pmc->mca_sfcount[MCAST_EXCLUDE]) | 1616 | if (pmc->mca_sfcount[MCAST_EXCLUDE]) |
@@ -1619,6 +1620,7 @@ static void mld_send_report(struct inet6_dev *idev, struct ifmcaddr6 *pmc) | |||
1619 | skb = add_grec(skb, pmc, type, 0, 0); | 1620 | skb = add_grec(skb, pmc, type, 0, 0); |
1620 | spin_unlock_bh(&pmc->mca_lock); | 1621 | spin_unlock_bh(&pmc->mca_lock); |
1621 | } | 1622 | } |
1623 | read_unlock_bh(&idev->lock); | ||
1622 | if (skb) | 1624 | if (skb) |
1623 | mld_sendpack(skb); | 1625 | mld_sendpack(skb); |
1624 | } | 1626 | } |