[ofa-general] [PATCH] ipoib: racing uses of ipoib_neigh in CM
akepner at sgi.com
akepner at sgi.com
Wed Jun 17 17:44:22 PDT 2009
There is a race condition between the neighbour cleanup code, and
some uses of the closely related ipoib_neigh structure when using
IPoIB-CM. The root cause is that the struct ipoib_neigh pointer
that's stashed away in struct neighbour is read (and subsequently
used) in ipoib_neigh_cleanup() without using locking that's consistent
with other reads/writes of this pointer. (The pointer must be read
before it can be known which lock to use, so it's difficult to
avoid.)
Re-read the pointer in ipoib_neigh_cleanup() after the appropriate
lock is acquired.
Signed-off-by: Arthur Kepner <akepner at sgi.com>
---
ipoib_main.c | 29 +++++++++++++++++++++++------
1 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index ab2c192..901735b 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -841,10 +841,20 @@ static void ipoib_set_mcast_list(struct net_device *dev)
static void ipoib_neigh_cleanup(struct neighbour *n)
{
struct ipoib_neigh *neigh;
- struct ipoib_dev_priv *priv = netdev_priv(n->dev);
+ struct ipoib_dev_priv *priv;
unsigned long flags;
struct ipoib_ah *ah = NULL;
+ /*
+ * Note that the read of the neigh pointer below is not protected
+ * by a ipoib_dev_priv->lock (since we don't yet know which device's
+ * lock to use). Count on the fact that if ipoib_neigh_free() has
+ * already freed the struct ipoib_neigh, to_ipoib_neigh() will
+ * return NULL.
+ *
+ * If to_ipoib_neigh() does not return NULL, we'll re-read neigh
+ * under the appropriate lock below.
+ */
neigh = *to_ipoib_neigh(n);
if (neigh)
priv = netdev_priv(neigh->dev);
@@ -856,11 +866,14 @@ static void ipoib_neigh_cleanup(struct neighbour *n)
n->ha + 4);
spin_lock_irqsave(&priv->lock, flags);
-
- if (neigh->ah)
- ah = neigh->ah;
- list_del(&neigh->list);
- ipoib_neigh_free(n->dev, neigh);
+ /* re-read neigh under priv->lock */
+ neigh = *to_ipoib_neigh(n);
+ if (neigh) {
+ if (neigh->ah)
+ ah = neigh->ah;
+ list_del(&neigh->list);
+ ipoib_neigh_free(n->dev, neigh);
+ }
spin_unlock_irqrestore(&priv->lock, flags);
@@ -888,7 +901,11 @@ struct ipoib_neigh *ipoib_neigh_alloc(struct neighbour *neighbour,
void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh)
{
+ struct ipoib_dev_priv *priv = netdev_priv(neigh->dev);
struct sk_buff *skb;
+
+ BUG_ON(!spin_is_locked(&priv->lock));
+
*to_ipoib_neigh(neigh->neighbour) = NULL;
while ((skb = __skb_dequeue(&neigh->queue))) {
++dev->stats.tx_dropped;
More information about the general
mailing list