@@ -55,8 +55,6 @@ MODULE_PARM_DESC(mcast_debug_level,
"Enable multicast debug tracing if > 0");
#endif
-static DEFINE_MUTEX(mcast_mutex);
-
struct ipoib_mcast_iter {
struct net_device *dev;
union ib_gid mgid;
@@ -340,16 +338,6 @@ static int ipoib_mcast_join_complete(int status,
"sendonly " : "",
mcast->mcmember.mgid.raw, status);
- /*
- * We have to take the mutex to force mcast_join to
- * return from ib_sa_multicast_join and set mcast->mc to a
- * valid value. Otherwise we were racing with ourselves in
- * that we might fail here, but get a valid return from
- * ib_sa_multicast_join after we had cleared mcast->mc here,
- * resulting in mis-matched joins and leaves and a deadlock
- */
- mutex_lock(&mcast_mutex);
-
/* We trap for port events ourselves. */
if (status == -ENETRESET) {
status = 0;
@@ -410,11 +398,14 @@ static int ipoib_mcast_join_complete(int status,
__ipoib_mcast_continue_join_thread(priv, mcast, 1);
}
out:
+ spin_lock_irq(&priv->lock);
clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
if (status)
mcast->mc = NULL;
+ else
+ mcast->mc = multicast;
complete(&mcast->done);
- mutex_unlock(&mcast_mutex);
+ spin_unlock_irq(&priv->lock);
return status;
}
@@ -423,6 +414,7 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
int create)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ struct ib_sa_multicast *multicast;
struct ib_sa_mcmember_rec rec = {
.join_state = 1
};
@@ -464,19 +456,20 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast,
rec.hop_limit = priv->broadcast->mcmember.hop_limit;
}
- mutex_lock(&mcast_mutex);
- mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port,
+ multicast = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port,
&rec, comp_mask, GFP_KERNEL,
ipoib_mcast_join_complete, mcast);
- if (IS_ERR(mcast->mc)) {
- clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
- complete(&mcast->done);
- ret = PTR_ERR(mcast->mc);
+ if (IS_ERR(multicast)) {
+ ret = PTR_ERR(multicast);
+ mcast->mc = NULL;
ipoib_warn(priv, "ib_sa_join_multicast failed, status %d\n", ret);
+ spin_lock_irq(&priv->lock);
+ clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
/* Requeue this join task with a backoff delay */
__ipoib_mcast_continue_join_thread(priv, mcast, 1);
+ complete(&mcast->done);
+ spin_unlock_irq(&priv->lock);
}
- mutex_unlock(&mcast_mutex);
}
void ipoib_mcast_join_task(struct work_struct *work)
@@ -505,15 +498,6 @@ void ipoib_mcast_join_task(struct work_struct *work)
else
memcpy(priv->dev->dev_addr + 4, priv->local_gid.raw, sizeof (union ib_gid));
- /*
- * We have to hold the mutex to keep from racing with the join
- * completion threads on setting flags on mcasts, and we have
- * to hold the priv->lock because dev_flush will remove entries
- * out from underneath us, so at a minimum we need the lock
- * through the time that we do the for_each loop of the mcast
- * list or else dev_flush can make us oops.
- */
- mutex_lock(&mcast_mutex);
spin_lock_irq(&priv->lock);
if (!test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags))
goto out;
@@ -573,9 +557,7 @@ void ipoib_mcast_join_task(struct work_struct *work)
else
create = 1;
spin_unlock_irq(&priv->lock);
- mutex_unlock(&mcast_mutex);
ipoib_mcast_join(dev, mcast, create);
- mutex_lock(&mcast_mutex);
spin_lock_irq(&priv->lock);
} else if (!delay_until ||
time_before(mcast->delay_until, delay_until))
@@ -597,7 +579,6 @@ out:
set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags);
}
spin_unlock_irq(&priv->lock);
- mutex_unlock(&mcast_mutex);
if (mcast)
ipoib_mcast_join(dev, mcast, create);
return;
@@ -606,13 +587,14 @@ out:
int ipoib_mcast_start_thread(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ unsigned long flags;
ipoib_dbg_mcast(priv, "starting multicast thread\n");
- mutex_lock(&mcast_mutex);
+ spin_lock_irqsave(&priv->lock, flags);
set_bit(IPOIB_MCAST_RUN, &priv->flags);
queue_delayed_work(priv->wq, &priv->mcast_task, 0);
- mutex_unlock(&mcast_mutex);
+ spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
@@ -620,13 +602,14 @@ int ipoib_mcast_start_thread(struct net_device *dev)
int ipoib_mcast_stop_thread(struct net_device *dev)
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
+ unsigned long flags;
ipoib_dbg_mcast(priv, "stopping multicast thread\n");
- mutex_lock(&mcast_mutex);
+ spin_lock_irqsave(&priv->lock, flags);
clear_bit(IPOIB_MCAST_RUN, &priv->flags);
cancel_delayed_work(&priv->mcast_task);
- mutex_unlock(&mcast_mutex);
+ spin_unlock_irqrestore(&priv->lock, flags);
flush_workqueue(priv->wq);
We needed the mcast_mutex when we had to protect two different workqueue threads against stomping on each others work. By no longer using mcast->mc to directly store the return value of ib_sa_join_multicast, we can switch all of the mcast flag/completion locking to being only the priv->lock spinlock. Signed-off-by: Doug Ledford <dledford@redhat.com> --- drivers/infiniband/ulp/ipoib/ipoib_multicast.c | 55 +++++++++----------------- 1 file changed, 19 insertions(+), 36 deletions(-)