From: Michael S. Tsirkin Date: Thu, 16 Nov 2006 12:16:47 +0000 (+0200) Subject: IPoIB: Fix skb leak when freeing neighbour X-Git-Tag: v2.6.20-rc1~34^2~40^2~471^2~2 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2745b5b713bf3457d8977c62dc2b3aa61f4a14b0;p=linux-2.6 IPoIB: Fix skb leak when freeing neighbour ipoib_neigh_free() is sometimes called while neighbour is still alive, so it might still have queued skbs. Fix skb leak in this case. Signed-off-by: Michael S. Tsirkin Signed-off-by: Roland Dreier --- diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 0b8a79d53a..f2b61851a4 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -233,7 +233,7 @@ static inline struct ipoib_neigh **to_ipoib_neigh(struct neighbour *neigh) } struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neigh); -void ipoib_neigh_free(struct ipoib_neigh *neigh); +void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh); extern struct workqueue_struct *ipoib_workqueue; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 85522daeb9..5ba3154320 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -264,7 +264,7 @@ static void path_free(struct net_device *dev, struct ipoib_path *path) if (neigh->ah) ipoib_put_ah(neigh->ah); - ipoib_neigh_free(neigh); + ipoib_neigh_free(dev, neigh); } spin_unlock_irqrestore(&priv->lock, flags); @@ -525,10 +525,11 @@ static void neigh_add_path(struct sk_buff *skb, struct net_device *dev) ipoib_send(dev, skb, path->ah, IPOIB_QPN(skb->dst->neighbour->ha)); } else { neigh->ah = NULL; - __skb_queue_tail(&neigh->queue, skb); if (!path->query && path_rec_start(dev, path)) goto err_list; + + __skb_queue_tail(&neigh->queue, skb); } spin_unlock(&priv->lock); @@ -538,7 +539,7 @@ err_list: list_del(&neigh->list); err_path: - ipoib_neigh_free(neigh); + ipoib_neigh_free(dev, neigh); ++priv->stats.tx_dropped; dev_kfree_skb_any(skb); @@ -655,7 +656,7 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev) */ ipoib_put_ah(neigh->ah); list_del(&neigh->list); - ipoib_neigh_free(neigh); + ipoib_neigh_free(dev, neigh); spin_unlock(&priv->lock); ipoib_path_lookup(skb, dev); goto out; @@ -786,7 +787,7 @@ static void ipoib_neigh_destructor(struct neighbour *n) if (neigh->ah) ah = neigh->ah; list_del(&neigh->list); - ipoib_neigh_free(neigh); + ipoib_neigh_free(n->dev, neigh); } spin_unlock_irqrestore(&priv->lock, flags); @@ -809,9 +810,15 @@ struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour) return neigh; } -void ipoib_neigh_free(struct ipoib_neigh *neigh) +void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh) { + struct ipoib_dev_priv *priv = netdev_priv(dev); + struct sk_buff *skb; *to_ipoib_neigh(neigh->neighbour) = NULL; + while ((skb = __skb_dequeue(&neigh->queue))) { + ++priv->stats.tx_dropped; + dev_kfree_skb_any(skb); + } kfree(neigh); } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 3faa1820f0..d282d65e3e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -114,7 +114,7 @@ static void ipoib_mcast_free(struct ipoib_mcast *mcast) */ if (neigh->ah) ipoib_put_ah(neigh->ah); - ipoib_neigh_free(neigh); + ipoib_neigh_free(dev, neigh); } spin_unlock_irqrestore(&priv->lock, flags);