Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

IB/ipoib: Fix packet loss after hardware address update

The neighbour ha field may get updated without destroying the
neighbour. In this case, the ha field gets out of sync with the
address handle stored in ipoib_neigh->ah, with the result that
the ah field would point to an incorrect path, resulting in all
packets being lost.

Signed-off-by: Michael S. Tsirkin <mst@mellanox.co.il>
Signed-off-by: Roland Dreier <rolandd@cisco.com>

authored by

Michael S. Tsirkin and committed by
Roland Dreier
8a7f7521 624d01f8

+24
+1
drivers/infiniband/ulp/ipoib/ipoib.h
··· 212 212 213 213 struct ipoib_neigh { 214 214 struct ipoib_ah *ah; 215 + union ib_gid dgid; 215 216 struct sk_buff_head queue; 216 217 217 218 struct neighbour *neighbour;
+23
drivers/infiniband/ulp/ipoib/ipoib_main.c
··· 404 404 list_for_each_entry(neigh, &path->neigh_list, list) { 405 405 kref_get(&path->ah->ref); 406 406 neigh->ah = path->ah; 407 + memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw, 408 + sizeof(union ib_gid)); 407 409 408 410 while ((skb = __skb_dequeue(&neigh->queue))) 409 411 __skb_queue_tail(&skqueue, skb); ··· 512 510 if (path->ah) { 513 511 kref_get(&path->ah->ref); 514 512 neigh->ah = path->ah; 513 + memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw, 514 + sizeof(union ib_gid)); 515 515 516 516 ipoib_send(dev, skb, path->ah, 517 517 be32_to_cpup((__be32 *) skb->dst->neighbour->ha)); ··· 637 633 neigh = *to_ipoib_neigh(skb->dst->neighbour); 638 634 639 635 if (likely(neigh->ah)) { 636 + if (unlikely(memcmp(&neigh->dgid.raw, 637 + skb->dst->neighbour->ha + 4, 638 + sizeof(union ib_gid)))) { 639 + spin_lock(&priv->lock); 640 + /* 641 + * It's safe to call ipoib_put_ah() inside 642 + * priv->lock here, because we know that 643 + * path->ah will always hold one more reference, 644 + * so ipoib_put_ah() will never do more than 645 + * decrement the ref count. 646 + */ 647 + ipoib_put_ah(neigh->ah); 648 + list_del(&neigh->list); 649 + ipoib_neigh_free(neigh); 650 + spin_unlock(&priv->lock); 651 + ipoib_path_lookup(skb, dev); 652 + goto out; 653 + } 654 + 640 655 ipoib_send(dev, skb, neigh->ah, 641 656 be32_to_cpup((__be32 *) skb->dst->neighbour->ha)); 642 657 goto out;