Skip to content

Commit

Permalink
IPoIB: Fix deadlock on RTNL in ipoib_stop()
Browse files Browse the repository at this point in the history
Commit c8c2afe ("IPoIB: Use rtnl lock/unlock when changing device
flags") added a call to rtnl_lock() in ipoib_mcast_join_task(), which
is run from the ipoib_workqueue.  However, ipoib_stop() (which is run
inside rtnl_lock()) flushes this workqueue, which leads to a deadlock
if the join task is pending.

Fix this by simply not flushing the workqueue from ipoib_stop().  It
turns out that we really don't care about workqueue tasks running
during or after ipoib_stop(), as long as we make sure to flush the
workqueue before unregistering a netdev.

This fixes <https://bugs.openfabrics.org/show_bug.cgi?id=1114>.

Signed-off-by: Roland Dreier <rolandd@cisco.com>
  • Loading branch information
Roland Dreier committed Aug 19, 2008
1 parent ffaa5b9 commit a77a57a
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 11 deletions.
19 changes: 9 additions & 10 deletions drivers/infiniband/ulp/ipoib/ipoib_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,14 +156,8 @@ static int ipoib_stop(struct net_device *dev)

netif_stop_queue(dev);

/*
* Now flush workqueue to make sure a scheduled task doesn't
* bring our internal state back up.
*/
flush_workqueue(ipoib_workqueue);

ipoib_ib_dev_down(dev, 1);
ipoib_ib_dev_stop(dev, 1);
ipoib_ib_dev_down(dev, 0);
ipoib_ib_dev_stop(dev, 0);

if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
struct ipoib_dev_priv *cpriv;
Expand Down Expand Up @@ -1314,7 +1308,7 @@ static struct net_device *ipoib_add_port(const char *format,

register_failed:
ib_unregister_event_handler(&priv->event_handler);
flush_scheduled_work();
flush_workqueue(ipoib_workqueue);

event_failed:
ipoib_dev_cleanup(priv->dev);
Expand Down Expand Up @@ -1373,7 +1367,12 @@ static void ipoib_remove_one(struct ib_device *device)

list_for_each_entry_safe(priv, tmp, dev_list, list) {
ib_unregister_event_handler(&priv->event_handler);
flush_scheduled_work();

rtnl_lock();
dev_change_flags(priv->dev, priv->dev->flags & ~IFF_UP);
rtnl_unlock();

flush_workqueue(ipoib_workqueue);

unregister_netdev(priv->dev);
ipoib_dev_cleanup(priv->dev);
Expand Down
10 changes: 9 additions & 1 deletion drivers/infiniband/ulp/ipoib/ipoib_multicast.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,16 @@ static int ipoib_mcast_join_complete(int status,
&priv->mcast_task, 0);
mutex_unlock(&mcast_mutex);

if (mcast == priv->broadcast)
if (mcast == priv->broadcast) {
/*
* Take RTNL lock here to avoid racing with
* ipoib_stop() and turning the carrier back
* on while a device is being removed.
*/
rtnl_lock();
netif_carrier_on(dev);
rtnl_unlock();
}

return 0;
}
Expand Down

0 comments on commit a77a57a

Please sign in to comment.