IPoIB: Fix deadlock between ipoib_open() and child interface create
Fix a deadlock between child interface creation/deletion and ipoib start/stop. The former takes vlan_mutex, and then might take RTNL via register_netdev()/unregister_netdev(). The latter is executed with RTNL held, and tries to take vlan_mutex, which can lead to an AB-BA deadlock. Fix this by having the child interface creation/deletion code take the RTNL first so vlan_mutex always nests inside RTNL. We can use register_netdevice() for child interfaces because we form the interface name from the parent interface and hence don't need the '%' expansion of register_netdev(). Reported-by: Yossi Etigin <yosefe@voltaire.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
parent
b8a1b1ce14
commit
cbbe1efa49
@ -61,6 +61,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
|
|||||||
|
|
||||||
ppriv = netdev_priv(pdev);
|
ppriv = netdev_priv(pdev);
|
||||||
|
|
||||||
|
rtnl_lock();
|
||||||
mutex_lock(&ppriv->vlan_mutex);
|
mutex_lock(&ppriv->vlan_mutex);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -111,7 +112,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
|
|||||||
goto device_init_failed;
|
goto device_init_failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = register_netdev(priv->dev);
|
result = register_netdevice(priv->dev);
|
||||||
if (result) {
|
if (result) {
|
||||||
ipoib_warn(priv, "failed to initialize; error %i", result);
|
ipoib_warn(priv, "failed to initialize; error %i", result);
|
||||||
goto register_failed;
|
goto register_failed;
|
||||||
@ -134,12 +135,13 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
|
|||||||
list_add_tail(&priv->list, &ppriv->child_intfs);
|
list_add_tail(&priv->list, &ppriv->child_intfs);
|
||||||
|
|
||||||
mutex_unlock(&ppriv->vlan_mutex);
|
mutex_unlock(&ppriv->vlan_mutex);
|
||||||
|
rtnl_unlock();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
sysfs_failed:
|
sysfs_failed:
|
||||||
ipoib_delete_debug_files(priv->dev);
|
ipoib_delete_debug_files(priv->dev);
|
||||||
unregister_netdev(priv->dev);
|
unregister_netdevice(priv->dev);
|
||||||
|
|
||||||
register_failed:
|
register_failed:
|
||||||
ipoib_dev_cleanup(priv->dev);
|
ipoib_dev_cleanup(priv->dev);
|
||||||
@ -149,6 +151,7 @@ device_init_failed:
|
|||||||
|
|
||||||
err:
|
err:
|
||||||
mutex_unlock(&ppriv->vlan_mutex);
|
mutex_unlock(&ppriv->vlan_mutex);
|
||||||
|
rtnl_unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,10 +165,11 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
|
|||||||
|
|
||||||
ppriv = netdev_priv(pdev);
|
ppriv = netdev_priv(pdev);
|
||||||
|
|
||||||
|
rtnl_lock();
|
||||||
mutex_lock(&ppriv->vlan_mutex);
|
mutex_lock(&ppriv->vlan_mutex);
|
||||||
list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
|
list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
|
||||||
if (priv->pkey == pkey) {
|
if (priv->pkey == pkey) {
|
||||||
unregister_netdev(priv->dev);
|
unregister_netdevice(priv->dev);
|
||||||
ipoib_dev_cleanup(priv->dev);
|
ipoib_dev_cleanup(priv->dev);
|
||||||
list_del(&priv->list);
|
list_del(&priv->list);
|
||||||
free_netdev(priv->dev);
|
free_netdev(priv->dev);
|
||||||
@ -175,6 +179,7 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&ppriv->vlan_mutex);
|
mutex_unlock(&ppriv->vlan_mutex);
|
||||||
|
rtnl_unlock();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user