diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-11-29 16:31:23 -0500 |
---|---|---|
committer | Roland Dreier <roland@purestorage.com> | 2011-11-29 16:37:11 -0500 |
commit | 580da35a31f91a594f3090b7a2c39b85cb051a12 (patch) | |
tree | 8cb8f106fe3c5df725dbde3efd2bfd19e0f38a2e | |
parent | 1ea6b8f48918282bdca0b32a34095504ee65bab5 (diff) |
IB: Fix RCU lockdep splats
Commit f2c31e32b37 ("net: fix NULL dereferences in check_peer_redir()")
forgot to take care of infiniband uses of dst neighbours.
Many thanks to Marc Aurele who provided a nice bug report and feedback.
Reported-by: Marc Aurele La France <tsi@ualberta.ca>
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: <stable@kernel.org>
Signed-off-by: Roland Dreier <roland@purestorage.com>
-rw-r--r-- | drivers/infiniband/core/addr.c | 9 | ||||
-rw-r--r-- | drivers/infiniband/hw/cxgb3/iwch_cm.c | 4 | ||||
-rw-r--r-- | drivers/infiniband/hw/cxgb4/cm.c | 6 | ||||
-rw-r--r-- | drivers/infiniband/hw/nes/nes_cm.c | 6 | ||||
-rw-r--r-- | drivers/infiniband/ulp/ipoib/ipoib_main.c | 18 | ||||
-rw-r--r-- | drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 6 |
6 files changed, 35 insertions, 14 deletions
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 691276bafd78..e9cf51b1343b 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c | |||
@@ -216,7 +216,9 @@ static int addr4_resolve(struct sockaddr_in *src_in, | |||
216 | 216 | ||
217 | neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev); | 217 | neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev); |
218 | if (!neigh || !(neigh->nud_state & NUD_VALID)) { | 218 | if (!neigh || !(neigh->nud_state & NUD_VALID)) { |
219 | rcu_read_lock(); | ||
219 | neigh_event_send(dst_get_neighbour(&rt->dst), NULL); | 220 | neigh_event_send(dst_get_neighbour(&rt->dst), NULL); |
221 | rcu_read_unlock(); | ||
220 | ret = -ENODATA; | 222 | ret = -ENODATA; |
221 | if (neigh) | 223 | if (neigh) |
222 | goto release; | 224 | goto release; |
@@ -274,15 +276,16 @@ static int addr6_resolve(struct sockaddr_in6 *src_in, | |||
274 | goto put; | 276 | goto put; |
275 | } | 277 | } |
276 | 278 | ||
279 | rcu_read_lock(); | ||
277 | neigh = dst_get_neighbour(dst); | 280 | neigh = dst_get_neighbour(dst); |
278 | if (!neigh || !(neigh->nud_state & NUD_VALID)) { | 281 | if (!neigh || !(neigh->nud_state & NUD_VALID)) { |
279 | if (neigh) | 282 | if (neigh) |
280 | neigh_event_send(neigh, NULL); | 283 | neigh_event_send(neigh, NULL); |
281 | ret = -ENODATA; | 284 | ret = -ENODATA; |
282 | goto put; | 285 | } else { |
286 | ret = rdma_copy_addr(addr, dst->dev, neigh->ha); | ||
283 | } | 287 | } |
284 | 288 | rcu_read_unlock(); | |
285 | ret = rdma_copy_addr(addr, dst->dev, neigh->ha); | ||
286 | put: | 289 | put: |
287 | dst_release(dst); | 290 | dst_release(dst); |
288 | return ret; | 291 | return ret; |
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index de6d0774e609..c88b12beef25 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c | |||
@@ -1375,8 +1375,10 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) | |||
1375 | goto reject; | 1375 | goto reject; |
1376 | } | 1376 | } |
1377 | dst = &rt->dst; | 1377 | dst = &rt->dst; |
1378 | rcu_read_lock(); | ||
1378 | neigh = dst_get_neighbour(dst); | 1379 | neigh = dst_get_neighbour(dst); |
1379 | l2t = t3_l2t_get(tdev, neigh, neigh->dev); | 1380 | l2t = t3_l2t_get(tdev, neigh, neigh->dev); |
1381 | rcu_read_unlock(); | ||
1380 | if (!l2t) { | 1382 | if (!l2t) { |
1381 | printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", | 1383 | printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", |
1382 | __func__); | 1384 | __func__); |
@@ -1946,10 +1948,12 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) | |||
1946 | } | 1948 | } |
1947 | ep->dst = &rt->dst; | 1949 | ep->dst = &rt->dst; |
1948 | 1950 | ||
1951 | rcu_read_lock(); | ||
1949 | neigh = dst_get_neighbour(ep->dst); | 1952 | neigh = dst_get_neighbour(ep->dst); |
1950 | 1953 | ||
1951 | /* get a l2t entry */ | 1954 | /* get a l2t entry */ |
1952 | ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev); | 1955 | ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev); |
1956 | rcu_read_unlock(); | ||
1953 | if (!ep->l2t) { | 1957 | if (!ep->l2t) { |
1954 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); | 1958 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); |
1955 | err = -ENOMEM; | 1959 | err = -ENOMEM; |
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index b36cdac9c558..75b57bee6622 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c | |||
@@ -1594,6 +1594,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) | |||
1594 | goto reject; | 1594 | goto reject; |
1595 | } | 1595 | } |
1596 | dst = &rt->dst; | 1596 | dst = &rt->dst; |
1597 | rcu_read_lock(); | ||
1597 | neigh = dst_get_neighbour(dst); | 1598 | neigh = dst_get_neighbour(dst); |
1598 | if (neigh->dev->flags & IFF_LOOPBACK) { | 1599 | if (neigh->dev->flags & IFF_LOOPBACK) { |
1599 | pdev = ip_dev_find(&init_net, peer_ip); | 1600 | pdev = ip_dev_find(&init_net, peer_ip); |
@@ -1620,6 +1621,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) | |||
1620 | rss_qid = dev->rdev.lldi.rxq_ids[ | 1621 | rss_qid = dev->rdev.lldi.rxq_ids[ |
1621 | cxgb4_port_idx(neigh->dev) * step]; | 1622 | cxgb4_port_idx(neigh->dev) * step]; |
1622 | } | 1623 | } |
1624 | rcu_read_unlock(); | ||
1623 | if (!l2t) { | 1625 | if (!l2t) { |
1624 | printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", | 1626 | printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n", |
1625 | __func__); | 1627 | __func__); |
@@ -1820,6 +1822,7 @@ static int c4iw_reconnect(struct c4iw_ep *ep) | |||
1820 | } | 1822 | } |
1821 | ep->dst = &rt->dst; | 1823 | ep->dst = &rt->dst; |
1822 | 1824 | ||
1825 | rcu_read_lock(); | ||
1823 | neigh = dst_get_neighbour(ep->dst); | 1826 | neigh = dst_get_neighbour(ep->dst); |
1824 | 1827 | ||
1825 | /* get a l2t entry */ | 1828 | /* get a l2t entry */ |
@@ -1856,6 +1859,7 @@ static int c4iw_reconnect(struct c4iw_ep *ep) | |||
1856 | ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[ | 1859 | ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[ |
1857 | cxgb4_port_idx(neigh->dev) * step]; | 1860 | cxgb4_port_idx(neigh->dev) * step]; |
1858 | } | 1861 | } |
1862 | rcu_read_unlock(); | ||
1859 | if (!ep->l2t) { | 1863 | if (!ep->l2t) { |
1860 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); | 1864 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); |
1861 | err = -ENOMEM; | 1865 | err = -ENOMEM; |
@@ -2301,6 +2305,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) | |||
2301 | } | 2305 | } |
2302 | ep->dst = &rt->dst; | 2306 | ep->dst = &rt->dst; |
2303 | 2307 | ||
2308 | rcu_read_lock(); | ||
2304 | neigh = dst_get_neighbour(ep->dst); | 2309 | neigh = dst_get_neighbour(ep->dst); |
2305 | 2310 | ||
2306 | /* get a l2t entry */ | 2311 | /* get a l2t entry */ |
@@ -2339,6 +2344,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param) | |||
2339 | ep->retry_with_mpa_v1 = 0; | 2344 | ep->retry_with_mpa_v1 = 0; |
2340 | ep->tried_with_mpa_v1 = 0; | 2345 | ep->tried_with_mpa_v1 = 0; |
2341 | } | 2346 | } |
2347 | rcu_read_unlock(); | ||
2342 | if (!ep->l2t) { | 2348 | if (!ep->l2t) { |
2343 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); | 2349 | printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__); |
2344 | err = -ENOMEM; | 2350 | err = -ENOMEM; |
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index dfce9ea98a39..0a52d72371ee 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c | |||
@@ -1377,9 +1377,11 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi | |||
1377 | neigh_release(neigh); | 1377 | neigh_release(neigh); |
1378 | } | 1378 | } |
1379 | 1379 | ||
1380 | if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) | 1380 | if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) { |
1381 | rcu_read_lock(); | ||
1381 | neigh_event_send(dst_get_neighbour(&rt->dst), NULL); | 1382 | neigh_event_send(dst_get_neighbour(&rt->dst), NULL); |
1382 | 1383 | rcu_read_unlock(); | |
1384 | } | ||
1383 | ip_rt_put(rt); | 1385 | ip_rt_put(rt); |
1384 | return rc; | 1386 | return rc; |
1385 | } | 1387 | } |
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 7567b6000230..ef38848d1b0e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c | |||
@@ -555,6 +555,7 @@ static int path_rec_start(struct net_device *dev, | |||
555 | return 0; | 555 | return 0; |
556 | } | 556 | } |
557 | 557 | ||
558 | /* called with rcu_read_lock */ | ||
558 | static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) | 559 | static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) |
559 | { | 560 | { |
560 | struct ipoib_dev_priv *priv = netdev_priv(dev); | 561 | struct ipoib_dev_priv *priv = netdev_priv(dev); |
@@ -636,6 +637,7 @@ err_drop: | |||
636 | spin_unlock_irqrestore(&priv->lock, flags); | 637 | spin_unlock_irqrestore(&priv->lock, flags); |
637 | } | 638 | } |
638 | 639 | ||
640 | /* called with rcu_read_lock */ | ||
639 | static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev) | 641 | static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev) |
640 | { | 642 | { |
641 | struct ipoib_dev_priv *priv = netdev_priv(skb->dev); | 643 | struct ipoib_dev_priv *priv = netdev_priv(skb->dev); |
@@ -720,13 +722,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
720 | struct neighbour *n = NULL; | 722 | struct neighbour *n = NULL; |
721 | unsigned long flags; | 723 | unsigned long flags; |
722 | 724 | ||
725 | rcu_read_lock(); | ||
723 | if (likely(skb_dst(skb))) | 726 | if (likely(skb_dst(skb))) |
724 | n = dst_get_neighbour(skb_dst(skb)); | 727 | n = dst_get_neighbour(skb_dst(skb)); |
725 | 728 | ||
726 | if (likely(n)) { | 729 | if (likely(n)) { |
727 | if (unlikely(!*to_ipoib_neigh(n))) { | 730 | if (unlikely(!*to_ipoib_neigh(n))) { |
728 | ipoib_path_lookup(skb, dev); | 731 | ipoib_path_lookup(skb, dev); |
729 | return NETDEV_TX_OK; | 732 | goto unlock; |
730 | } | 733 | } |
731 | 734 | ||
732 | neigh = *to_ipoib_neigh(n); | 735 | neigh = *to_ipoib_neigh(n); |
@@ -749,17 +752,17 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
749 | ipoib_neigh_free(dev, neigh); | 752 | ipoib_neigh_free(dev, neigh); |
750 | spin_unlock_irqrestore(&priv->lock, flags); | 753 | spin_unlock_irqrestore(&priv->lock, flags); |
751 | ipoib_path_lookup(skb, dev); | 754 | ipoib_path_lookup(skb, dev); |
752 | return NETDEV_TX_OK; | 755 | goto unlock; |
753 | } | 756 | } |
754 | 757 | ||
755 | if (ipoib_cm_get(neigh)) { | 758 | if (ipoib_cm_get(neigh)) { |
756 | if (ipoib_cm_up(neigh)) { | 759 | if (ipoib_cm_up(neigh)) { |
757 | ipoib_cm_send(dev, skb, ipoib_cm_get(neigh)); | 760 | ipoib_cm_send(dev, skb, ipoib_cm_get(neigh)); |
758 | return NETDEV_TX_OK; | 761 | goto unlock; |
759 | } | 762 | } |
760 | } else if (neigh->ah) { | 763 | } else if (neigh->ah) { |
761 | ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha)); | 764 | ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha)); |
762 | return NETDEV_TX_OK; | 765 | goto unlock; |
763 | } | 766 | } |
764 | 767 | ||
765 | if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { | 768 | if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) { |
@@ -793,13 +796,14 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
793 | phdr->hwaddr + 4); | 796 | phdr->hwaddr + 4); |
794 | dev_kfree_skb_any(skb); | 797 | dev_kfree_skb_any(skb); |
795 | ++dev->stats.tx_dropped; | 798 | ++dev->stats.tx_dropped; |
796 | return NETDEV_TX_OK; | 799 | goto unlock; |
797 | } | 800 | } |
798 | 801 | ||
799 | unicast_arp_send(skb, dev, phdr); | 802 | unicast_arp_send(skb, dev, phdr); |
800 | } | 803 | } |
801 | } | 804 | } |
802 | 805 | unlock: | |
806 | rcu_read_unlock(); | ||
803 | return NETDEV_TX_OK; | 807 | return NETDEV_TX_OK; |
804 | } | 808 | } |
805 | 809 | ||
@@ -837,7 +841,7 @@ static int ipoib_hard_header(struct sk_buff *skb, | |||
837 | dst = skb_dst(skb); | 841 | dst = skb_dst(skb); |
838 | n = NULL; | 842 | n = NULL; |
839 | if (dst) | 843 | if (dst) |
840 | n = dst_get_neighbour(dst); | 844 | n = dst_get_neighbour_raw(dst); |
841 | if ((!dst || !n) && daddr) { | 845 | if ((!dst || !n) && daddr) { |
842 | struct ipoib_pseudoheader *phdr = | 846 | struct ipoib_pseudoheader *phdr = |
843 | (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); | 847 | (struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr); |
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 1b7a97686356..cad1894594a8 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c | |||
@@ -266,7 +266,7 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, | |||
266 | 266 | ||
267 | skb->dev = dev; | 267 | skb->dev = dev; |
268 | if (dst) | 268 | if (dst) |
269 | n = dst_get_neighbour(dst); | 269 | n = dst_get_neighbour_raw(dst); |
270 | if (!dst || !n) { | 270 | if (!dst || !n) { |
271 | /* put pseudoheader back on for next time */ | 271 | /* put pseudoheader back on for next time */ |
272 | skb_push(skb, sizeof (struct ipoib_pseudoheader)); | 272 | skb_push(skb, sizeof (struct ipoib_pseudoheader)); |
@@ -722,6 +722,8 @@ out: | |||
722 | if (mcast && mcast->ah) { | 722 | if (mcast && mcast->ah) { |
723 | struct dst_entry *dst = skb_dst(skb); | 723 | struct dst_entry *dst = skb_dst(skb); |
724 | struct neighbour *n = NULL; | 724 | struct neighbour *n = NULL; |
725 | |||
726 | rcu_read_lock(); | ||
725 | if (dst) | 727 | if (dst) |
726 | n = dst_get_neighbour(dst); | 728 | n = dst_get_neighbour(dst); |
727 | if (n && !*to_ipoib_neigh(n)) { | 729 | if (n && !*to_ipoib_neigh(n)) { |
@@ -734,7 +736,7 @@ out: | |||
734 | list_add_tail(&neigh->list, &mcast->neigh_list); | 736 | list_add_tail(&neigh->list, &mcast->neigh_list); |
735 | } | 737 | } |
736 | } | 738 | } |
737 | 739 | rcu_read_unlock(); | |
738 | spin_unlock_irqrestore(&priv->lock, flags); | 740 | spin_unlock_irqrestore(&priv->lock, flags); |
739 | ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN); | 741 | ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN); |
740 | return; | 742 | return; |