mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-17 18:56:24 +00:00
IB: Fix RCU lockdep splats
commit 580da35a31f91a594f3090b7a2c39b85cb051a12 upstream. 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> Signed-off-by: Roland Dreier <roland@purestorage.com>
This commit is contained in:
parent
829d5a92cb
commit
de5f8dc383
@ -215,7 +215,9 @@ static int addr4_resolve(struct sockaddr_in *src_in,
|
|||||||
|
|
||||||
neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev);
|
neigh = neigh_lookup(&arp_tbl, &rt->rt_gateway, rt->dst.dev);
|
||||||
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
|
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
|
||||||
|
rcu_read_lock();
|
||||||
neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
|
neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
|
||||||
|
rcu_read_unlock();
|
||||||
ret = -ENODATA;
|
ret = -ENODATA;
|
||||||
if (neigh)
|
if (neigh)
|
||||||
goto release;
|
goto release;
|
||||||
@ -273,15 +275,16 @@ static int addr6_resolve(struct sockaddr_in6 *src_in,
|
|||||||
goto put;
|
goto put;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
neigh = dst_get_neighbour(dst);
|
neigh = dst_get_neighbour(dst);
|
||||||
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
|
if (!neigh || !(neigh->nud_state & NUD_VALID)) {
|
||||||
if (neigh)
|
if (neigh)
|
||||||
neigh_event_send(neigh, NULL);
|
neigh_event_send(neigh, NULL);
|
||||||
ret = -ENODATA;
|
ret = -ENODATA;
|
||||||
goto put;
|
} else {
|
||||||
|
ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
ret = rdma_copy_addr(addr, dst->dev, neigh->ha);
|
|
||||||
put:
|
put:
|
||||||
dst_release(dst);
|
dst_release(dst);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1365,8 +1365,10 @@ static int pass_accept_req(struct t3cdev *tdev, struct sk_buff *skb, void *ctx)
|
|||||||
goto reject;
|
goto reject;
|
||||||
}
|
}
|
||||||
dst = &rt->dst;
|
dst = &rt->dst;
|
||||||
|
rcu_read_lock();
|
||||||
neigh = dst_get_neighbour(dst);
|
neigh = dst_get_neighbour(dst);
|
||||||
l2t = t3_l2t_get(tdev, neigh, neigh->dev);
|
l2t = t3_l2t_get(tdev, neigh, neigh->dev);
|
||||||
|
rcu_read_unlock();
|
||||||
if (!l2t) {
|
if (!l2t) {
|
||||||
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
||||||
__func__);
|
__func__);
|
||||||
@ -1936,10 +1938,12 @@ int iwch_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||||||
}
|
}
|
||||||
ep->dst = &rt->dst;
|
ep->dst = &rt->dst;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
neigh = dst_get_neighbour(ep->dst);
|
neigh = dst_get_neighbour(ep->dst);
|
||||||
|
|
||||||
/* get a l2t entry */
|
/* get a l2t entry */
|
||||||
ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev);
|
ep->l2t = t3_l2t_get(ep->com.tdev, neigh, neigh->dev);
|
||||||
|
rcu_read_unlock();
|
||||||
if (!ep->l2t) {
|
if (!ep->l2t) {
|
||||||
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -1358,6 +1358,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
|
|||||||
goto reject;
|
goto reject;
|
||||||
}
|
}
|
||||||
dst = &rt->dst;
|
dst = &rt->dst;
|
||||||
|
rcu_read_lock();
|
||||||
neigh = dst_get_neighbour(dst);
|
neigh = dst_get_neighbour(dst);
|
||||||
if (neigh->dev->flags & IFF_LOOPBACK) {
|
if (neigh->dev->flags & IFF_LOOPBACK) {
|
||||||
pdev = ip_dev_find(&init_net, peer_ip);
|
pdev = ip_dev_find(&init_net, peer_ip);
|
||||||
@ -1384,6 +1385,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb)
|
|||||||
rss_qid = dev->rdev.lldi.rxq_ids[
|
rss_qid = dev->rdev.lldi.rxq_ids[
|
||||||
cxgb4_port_idx(neigh->dev) * step];
|
cxgb4_port_idx(neigh->dev) * step];
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
if (!l2t) {
|
if (!l2t) {
|
||||||
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
printk(KERN_ERR MOD "%s - failed to allocate l2t entry!\n",
|
||||||
__func__);
|
__func__);
|
||||||
@ -1909,6 +1911,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||||||
}
|
}
|
||||||
ep->dst = &rt->dst;
|
ep->dst = &rt->dst;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
neigh = dst_get_neighbour(ep->dst);
|
neigh = dst_get_neighbour(ep->dst);
|
||||||
|
|
||||||
/* get a l2t entry */
|
/* get a l2t entry */
|
||||||
@ -1945,6 +1948,7 @@ int c4iw_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *conn_param)
|
|||||||
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
|
ep->rss_qid = ep->com.dev->rdev.lldi.rxq_ids[
|
||||||
cxgb4_port_idx(neigh->dev) * step];
|
cxgb4_port_idx(neigh->dev) * step];
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
if (!ep->l2t) {
|
if (!ep->l2t) {
|
||||||
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
printk(KERN_ERR MOD "%s - cannot alloc l2e.\n", __func__);
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
@ -1309,7 +1309,7 @@ static int build_mlx_header(struct mlx4_ib_sqp *sqp, struct ib_send_wr *wr,
|
|||||||
int is_eth;
|
int is_eth;
|
||||||
int is_vlan = 0;
|
int is_vlan = 0;
|
||||||
int is_grh;
|
int is_grh;
|
||||||
u16 vlan;
|
u16 vlan = 0;
|
||||||
|
|
||||||
send_size = 0;
|
send_size = 0;
|
||||||
for (i = 0; i < wr->num_sge; ++i)
|
for (i = 0; i < wr->num_sge; ++i)
|
||||||
|
@ -1150,9 +1150,11 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi
|
|||||||
neigh_release(neigh);
|
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);
|
neigh_event_send(dst_get_neighbour(&rt->dst), NULL);
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
ip_rt_put(rt);
|
ip_rt_put(rt);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -555,6 +555,7 @@ static int path_rec_start(struct net_device *dev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* called with rcu_read_lock */
|
||||||
static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
|
static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
struct ipoib_dev_priv *priv = netdev_priv(dev);
|
||||||
@ -636,6 +637,7 @@ err_drop:
|
|||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* called with rcu_read_lock */
|
||||||
static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev)
|
static void ipoib_path_lookup(struct sk_buff *skb, struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct ipoib_dev_priv *priv = netdev_priv(skb->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;
|
struct neighbour *n = NULL;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
if (likely(skb_dst(skb)))
|
if (likely(skb_dst(skb)))
|
||||||
n = dst_get_neighbour(skb_dst(skb));
|
n = dst_get_neighbour(skb_dst(skb));
|
||||||
|
|
||||||
if (likely(n)) {
|
if (likely(n)) {
|
||||||
if (unlikely(!*to_ipoib_neigh(n))) {
|
if (unlikely(!*to_ipoib_neigh(n))) {
|
||||||
ipoib_path_lookup(skb, dev);
|
ipoib_path_lookup(skb, dev);
|
||||||
return NETDEV_TX_OK;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
neigh = *to_ipoib_neigh(n);
|
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);
|
ipoib_neigh_free(dev, neigh);
|
||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
ipoib_path_lookup(skb, dev);
|
ipoib_path_lookup(skb, dev);
|
||||||
return NETDEV_TX_OK;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ipoib_cm_get(neigh)) {
|
if (ipoib_cm_get(neigh)) {
|
||||||
if (ipoib_cm_up(neigh)) {
|
if (ipoib_cm_up(neigh)) {
|
||||||
ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
|
ipoib_cm_send(dev, skb, ipoib_cm_get(neigh));
|
||||||
return NETDEV_TX_OK;
|
goto unlock;
|
||||||
}
|
}
|
||||||
} else if (neigh->ah) {
|
} else if (neigh->ah) {
|
||||||
ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(n->ha));
|
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) {
|
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);
|
phdr->hwaddr + 4);
|
||||||
dev_kfree_skb_any(skb);
|
dev_kfree_skb_any(skb);
|
||||||
++dev->stats.tx_dropped;
|
++dev->stats.tx_dropped;
|
||||||
return NETDEV_TX_OK;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
unicast_arp_send(skb, dev, phdr);
|
unicast_arp_send(skb, dev, phdr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unlock:
|
||||||
|
rcu_read_unlock();
|
||||||
return NETDEV_TX_OK;
|
return NETDEV_TX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -837,7 +841,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
|
|||||||
dst = skb_dst(skb);
|
dst = skb_dst(skb);
|
||||||
n = NULL;
|
n = NULL;
|
||||||
if (dst)
|
if (dst)
|
||||||
n = dst_get_neighbour(dst);
|
n = dst_get_neighbour_raw(dst);
|
||||||
if ((!dst || !n) && daddr) {
|
if ((!dst || !n) && daddr) {
|
||||||
struct ipoib_pseudoheader *phdr =
|
struct ipoib_pseudoheader *phdr =
|
||||||
(struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
|
(struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
|
||||||
|
@ -265,7 +265,7 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast,
|
|||||||
|
|
||||||
skb->dev = dev;
|
skb->dev = dev;
|
||||||
if (dst)
|
if (dst)
|
||||||
n = dst_get_neighbour(dst);
|
n = dst_get_neighbour_raw(dst);
|
||||||
if (!dst || !n) {
|
if (!dst || !n) {
|
||||||
/* put pseudoheader back on for next time */
|
/* put pseudoheader back on for next time */
|
||||||
skb_push(skb, sizeof (struct ipoib_pseudoheader));
|
skb_push(skb, sizeof (struct ipoib_pseudoheader));
|
||||||
@ -721,6 +721,8 @@ out:
|
|||||||
if (mcast && mcast->ah) {
|
if (mcast && mcast->ah) {
|
||||||
struct dst_entry *dst = skb_dst(skb);
|
struct dst_entry *dst = skb_dst(skb);
|
||||||
struct neighbour *n = NULL;
|
struct neighbour *n = NULL;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
if (dst)
|
if (dst)
|
||||||
n = dst_get_neighbour(dst);
|
n = dst_get_neighbour(dst);
|
||||||
if (n && !*to_ipoib_neigh(n)) {
|
if (n && !*to_ipoib_neigh(n)) {
|
||||||
@ -733,7 +735,7 @@ out:
|
|||||||
list_add_tail(&neigh->list, &mcast->neigh_list);
|
list_add_tail(&neigh->list, &mcast->neigh_list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
rcu_read_unlock();
|
||||||
spin_unlock_irqrestore(&priv->lock, flags);
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
|
ipoib_send(dev, skb, mcast->ah, IB_MULTICAST_QPN);
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user