mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-23 10:29:47 +07:00
IPoIB: Fix deadlock on RTNL in ipoib_stop()
Commit c8c2afe3
("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>
This commit is contained in:
parent
ffaa5b984a
commit
a77a57a1a2
@ -156,14 +156,8 @@ static int ipoib_stop(struct net_device *dev)
|
|||||||
|
|
||||||
netif_stop_queue(dev);
|
netif_stop_queue(dev);
|
||||||
|
|
||||||
/*
|
ipoib_ib_dev_down(dev, 0);
|
||||||
* Now flush workqueue to make sure a scheduled task doesn't
|
ipoib_ib_dev_stop(dev, 0);
|
||||||
* bring our internal state back up.
|
|
||||||
*/
|
|
||||||
flush_workqueue(ipoib_workqueue);
|
|
||||||
|
|
||||||
ipoib_ib_dev_down(dev, 1);
|
|
||||||
ipoib_ib_dev_stop(dev, 1);
|
|
||||||
|
|
||||||
if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
|
if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) {
|
||||||
struct ipoib_dev_priv *cpriv;
|
struct ipoib_dev_priv *cpriv;
|
||||||
@ -1314,7 +1308,7 @@ static struct net_device *ipoib_add_port(const char *format,
|
|||||||
|
|
||||||
register_failed:
|
register_failed:
|
||||||
ib_unregister_event_handler(&priv->event_handler);
|
ib_unregister_event_handler(&priv->event_handler);
|
||||||
flush_scheduled_work();
|
flush_workqueue(ipoib_workqueue);
|
||||||
|
|
||||||
event_failed:
|
event_failed:
|
||||||
ipoib_dev_cleanup(priv->dev);
|
ipoib_dev_cleanup(priv->dev);
|
||||||
@ -1373,7 +1367,12 @@ static void ipoib_remove_one(struct ib_device *device)
|
|||||||
|
|
||||||
list_for_each_entry_safe(priv, tmp, dev_list, list) {
|
list_for_each_entry_safe(priv, tmp, dev_list, list) {
|
||||||
ib_unregister_event_handler(&priv->event_handler);
|
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);
|
unregister_netdev(priv->dev);
|
||||||
ipoib_dev_cleanup(priv->dev);
|
ipoib_dev_cleanup(priv->dev);
|
||||||
|
@ -392,8 +392,16 @@ static int ipoib_mcast_join_complete(int status,
|
|||||||
&priv->mcast_task, 0);
|
&priv->mcast_task, 0);
|
||||||
mutex_unlock(&mcast_mutex);
|
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);
|
netif_carrier_on(dev);
|
||||||
|
rtnl_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user