Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[pandora-kernel.git] / drivers / infiniband / ulp / ipoib / ipoib_main.c
index 1c6ea1c..cf71d2a 100644 (file)
@@ -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);
@@ -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));
@@ -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;