Skip to content

Commit

Permalink
IB/ipoib: Fix packet loss after hardware address update
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael S. Tsirkin authored and Roland Dreier committed Jul 24, 2006
1 parent 624d01f commit 8a7f752
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 0 deletions.
1 change: 1 addition & 0 deletions drivers/infiniband/ulp/ipoib/ipoib.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ struct ipoib_path {

struct ipoib_neigh {
struct ipoib_ah *ah;
union ib_gid dgid;
struct sk_buff_head queue;

struct neighbour *neighbour;
Expand Down
23 changes: 23 additions & 0 deletions drivers/infiniband/ulp/ipoib/ipoib_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ static void path_rec_completion(int status,
list_for_each_entry(neigh, &path->neigh_list, list) {
kref_get(&path->ah->ref);
neigh->ah = path->ah;
memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
sizeof(union ib_gid));

while ((skb = __skb_dequeue(&neigh->queue)))
__skb_queue_tail(&skqueue, skb);
Expand Down Expand Up @@ -510,6 +512,8 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev)
if (path->ah) {
kref_get(&path->ah->ref);
neigh->ah = path->ah;
memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw,
sizeof(union ib_gid));

ipoib_send(dev, skb, path->ah,
be32_to_cpup((__be32 *) skb->dst->neighbour->ha));
Expand Down Expand Up @@ -633,6 +637,25 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
neigh = *to_ipoib_neigh(skb->dst->neighbour);

if (likely(neigh->ah)) {
if (unlikely(memcmp(&neigh->dgid.raw,
skb->dst->neighbour->ha + 4,
sizeof(union ib_gid)))) {
spin_lock(&priv->lock);
/*
* It's safe to call ipoib_put_ah() inside
* priv->lock here, because we know that
* path->ah will always hold one more reference,
* so ipoib_put_ah() will never do more than
* decrement the ref count.
*/
ipoib_put_ah(neigh->ah);
list_del(&neigh->list);
ipoib_neigh_free(neigh);
spin_unlock(&priv->lock);
ipoib_path_lookup(skb, dev);
goto out;
}

ipoib_send(dev, skb, neigh->ah,
be32_to_cpup((__be32 *) skb->dst->neighbour->ha));
goto out;
Expand Down

0 comments on commit 8a7f752

Please sign in to comment.