IB/IPoIB: Add destination address when re-queue packet
authorErez Shitrit <erezsh@mellanox.com>
Wed, 1 Feb 2017 17:10:05 +0000 (19:10 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 15 Mar 2017 01:57:13 +0000 (09:57 +0800)
commit 2b0841766a898aba84630fb723989a77a9d3b4e6 upstream.

When sending packet to destination that was not resolved yet
via path query, the driver keeps the skb and tries to re-send it
again when the path is resolved.

But when re-sending via dev_queue_xmit the kernel doesn't call
to dev_hard_header, so IPoIB needs to keep 20 bytes in the skb
and to put the destination address inside them.

In that way the dev_start_xmit will have the correct destination,
and the driver won't take the destination from the skb->data, while
nothing exists there, which causes to packet be be dropped.

The test flow is:
1. Run the SM on remote node,
2. Restart the driver.
4. Ping some destination,
3. Observe that first ICMP request will be dropped.

Fixes: fc791b633515 ("IB/ipoib: move back IB LL address into the hard header")
Signed-off-by: Erez Shitrit <erezsh@mellanox.com>
Signed-off-by: Noa Osherovich <noaos@mellanox.com>
Signed-off-by: Leon Romanovsky <leon@kernel.org>
Tested-by: Yuval Shaia <yuval.shaia@oracle.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/infiniband/ulp/ipoib/ipoib_main.c

index b34f9ffe559a3a476b0912234ba1de8a6da68cf6..8efcff1beb8ff5f93e19d8098693f7f8f13904c9 100644 (file)
@@ -626,6 +626,14 @@ void ipoib_mark_paths_invalid(struct net_device *dev)
        spin_unlock_irq(&priv->lock);
 }
 
+static void push_pseudo_header(struct sk_buff *skb, const char *daddr)
+{
+       struct ipoib_pseudo_header *phdr;
+
+       phdr = (struct ipoib_pseudo_header *)skb_push(skb, sizeof(*phdr));
+       memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
+}
+
 void ipoib_flush_paths(struct net_device *dev)
 {
        struct ipoib_dev_priv *priv = netdev_priv(dev);
@@ -850,8 +858,7 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
                        }
                        if (skb_queue_len(&neigh->queue) <
                            IPOIB_MAX_PATH_REC_QUEUE) {
-                               /* put pseudoheader back on for next time */
-                               skb_push(skb, IPOIB_PSEUDO_LEN);
+                               push_pseudo_header(skb, neigh->daddr);
                                __skb_queue_tail(&neigh->queue, skb);
                        } else {
                                ipoib_warn(priv, "queue length limit %d. Packet drop.\n",
@@ -869,10 +876,12 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
 
                if (!path->query && path_rec_start(dev, path))
                        goto err_path;
-               if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE)
+               if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
+                       push_pseudo_header(skb, neigh->daddr);
                        __skb_queue_tail(&neigh->queue, skb);
-               else
+               } else {
                        goto err_drop;
+               }
        }
 
        spin_unlock_irqrestore(&priv->lock, flags);
@@ -908,8 +917,7 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
                }
                if (path) {
                        if (skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
-                               /* put pseudoheader back on for next time */
-                               skb_push(skb, IPOIB_PSEUDO_LEN);
+                               push_pseudo_header(skb, phdr->hwaddr);
                                __skb_queue_tail(&path->queue, skb);
                        } else {
                                ++dev->stats.tx_dropped;
@@ -941,8 +949,7 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev,
                return;
        } else if ((path->query || !path_rec_start(dev, path)) &&
                   skb_queue_len(&path->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
-               /* put pseudoheader back on for next time */
-               skb_push(skb, IPOIB_PSEUDO_LEN);
+               push_pseudo_header(skb, phdr->hwaddr);
                __skb_queue_tail(&path->queue, skb);
        } else {
                ++dev->stats.tx_dropped;
@@ -1023,8 +1030,7 @@ send_using_neigh:
        }
 
        if (skb_queue_len(&neigh->queue) < IPOIB_MAX_PATH_REC_QUEUE) {
-               /* put pseudoheader back on for next time */
-               skb_push(skb, sizeof(*phdr));
+               push_pseudo_header(skb, phdr->hwaddr);
                spin_lock_irqsave(&priv->lock, flags);
                __skb_queue_tail(&neigh->queue, skb);
                spin_unlock_irqrestore(&priv->lock, flags);
@@ -1056,7 +1062,6 @@ static int ipoib_hard_header(struct sk_buff *skb,
                             unsigned short type,
                             const void *daddr, const void *saddr, unsigned len)
 {
-       struct ipoib_pseudo_header *phdr;
        struct ipoib_header *header;
 
        header = (struct ipoib_header *) skb_push(skb, sizeof *header);
@@ -1069,8 +1074,7 @@ static int ipoib_hard_header(struct sk_buff *skb,
         * destination address into skb hard header so we can figure out where
         * to send the packet later.
         */
-       phdr = (struct ipoib_pseudo_header *) skb_push(skb, sizeof(*phdr));
-       memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
+       push_pseudo_header(skb, daddr);
 
        return IPOIB_HARD_LEN;
 }