Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 580da35a authored by Eric Dumazet's avatar Eric Dumazet Committed by Roland Dreier
Browse files

IB: Fix RCU lockdep splats



Commit f2c31e32 ("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: default avatarMarc Aurele La France <tsi@ualberta.ca>
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Cc: David Miller <davem@davemloft.net>
Cc: <stable@kernel.org>
Signed-off-by: default avatarRoland Dreier <roland@purestorage.com>
parent 1ea6b8f4
Loading
Loading
Loading
Loading
+6 −3
Original line number Diff line number Diff line
@@ -216,7 +216,9 @@ static int addr4_resolve(struct sockaddr_in *src_in,

	neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev);
	if (!neigh || !(neigh->nud_state & NUD_VALID)) {
		rcu_read_lock();
		neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
		rcu_read_unlock();
		ret = -ENODATA;
		if (neigh)
			goto release;
@@ -274,15 +276,16 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
		goto put;
	}

	rcu_read_lock();
	neigh = dst_get_neighbour(dst);
	if (!neigh || !(neigh->nud_state & NUD_VALID)) {
		if (neigh)
			neigh_event_send(neigh, NULL);
		ret = -ENODATA;
		goto put;
	}

	} else {
		ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
	}
	rcu_read_unlock();
put:
	dst_release(dst);
	return ret;
+4 −0
Original line number Diff line number Diff line
@@ -1375,8 +1375,10 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
		goto reject;
	}
	dst = &rt->dst;
	rcu_read_lock();
	neigh = dst_get_neighbour(dst);
	l2t = t3_l2t_get(tdev, neigh, neigh->dev);
	rcu_read_unlock();
	if (!l2t) {
		printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
		       __func__);
@@ -1946,10 +1948,12 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
	}
	ep->dst = &rt->dst;

	rcu_read_lock();
	neigh = dst_get_neighbour(ep->dst);

	/* get a l2t entry */
	ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev);
	rcu_read_unlock();
	if (!ep->l2t) {
		printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
		err = -ENOMEM;
+6 −0
Original line number Diff line number Diff line
@@ -1594,6 +1594,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
		goto reject;
	}
	dst = &rt->dst;
	rcu_read_lock();
	neigh = dst_get_neighbour(dst);
	if (neigh->dev->flags & IFF_LOOPBACK) {
		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)
		rss_qid = dev->rdev.lldi.rxq_ids[
			  cxgb4_port_idx(neigh->dev) * step];
	}
	rcu_read_unlock();
	if (!l2t) {
		printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
		       __func__);
@@ -1820,6 +1822,7 @@ static int c4iw_reconnect(struct c4iw_ep *ep)
	}
	ep->dst = &rt->dst;

	rcu_read_lock();
	neigh = dst_get_neighbour(ep->dst);

	/* get a l2t entry */
@@ -1856,6 +1859,7 @@ static int c4iw_reconnect(struct c4iw_ep *ep)
		ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
			cxgb4_port_idx(neigh->dev) * step];
	}
	rcu_read_unlock();
	if (!ep->l2t) {
		printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
		err = -ENOMEM;
@@ -2301,6 +2305,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
	}
	ep->dst = &rt->dst;

	rcu_read_lock();
	neigh = dst_get_neighbour(ep->dst);

	/* get a l2t entry */
@@ -2339,6 +2344,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
		ep->retry_with_mpa_v1 = 0;
		ep->tried_with_mpa_v1 = 0;
	}
	rcu_read_unlock();
	if (!ep->l2t) {
		printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
		err = -ENOMEM;
+4 −2
Original line number Diff line number Diff line
@@ -1377,9 +1377,11 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
		neigh_release(neigh);
	}

	if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID)))
	if ((neigh == NULL) || (!(neigh->nud_state & NUD_VALID))) {
		rcu_read_lock();
		neigh_event_send(dst_get_neighbour(&rt->dst), NULL);

		rcu_read_unlock();
	}
	ip_rt_put(rt);
	return rc;
}
+11 −7
Original line number Diff line number Diff line
@@ -555,6 +555,7 @@ static int path_rec_start(struct net_device *dev,
	return 0;
}

/* called with rcu_read_lock */
static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
{
	struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -636,6 +637,7 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
	spin_unlock_irqrestore(&priv->lock, flags);
}

/* called with rcu_read_lock */
static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev)
{
	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)
	struct neighbour *n = NULL;
	unsigned long flags;

	rcu_read_lock();
	if (likely(skb_dst(skb)))
		n = dst_get_neighbour(skb_dst(skb));

	if (likely(n)) {
		if (unlikely(!*to_ipoib_neigh(n))) {
			ipoib_path_lookup(skb, dev);
			return NETDEV_TX_OK;
			goto unlock;
		}

		neigh = *to_ipoib_neigh(n);
@@ -749,17 +752,17 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
			ipoib_neigh_free(dev, neigh);
			spin_unlock_irqrestore(&priv->lock, flags);
			ipoib_path_lookup(skb, dev);
			return NETDEV_TX_OK;
			goto unlock;
		}

		if (ipoib_cm_get(neigh)) {
			if (ipoib_cm_up(neigh)) {
				ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
				return NETDEV_TX_OK;
				goto unlock;
			}
		} else if (neigh->ah) {
			ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha));
			return NETDEV_TX_OK;
			goto unlock;
		}

		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)
					   phdr->hwaddr + 4);
				dev_kfree_skb_any(skb);
				++dev->stats.tx_dropped;
				return NETDEV_TX_OK;
				goto unlock;
			}

			unicast_arp_send(skb, dev, phdr);
		}
	}

unlock:
	rcu_read_unlock();
	return NETDEV_TX_OK;
}

@@ -837,7 +841,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
	dst = skb_dst(skb);
	n = NULL;
	if (dst)
		n = dst_get_neighbour(dst);
		n = dst_get_neighbour_raw(dst);
	if ((!dst || !n) && daddr) {
		struct ipoib_pseudoheader *phdr =
			(struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
Loading