Commit cbbe1efa authored by Roland Dreier's avatar Roland Dreier

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: default avatarYossi Etigin <yosefe@voltaire.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent b8a1b1ce
...@@ -61,6 +61,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) ...@@ -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) ...@@ -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) ...@@ -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 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) ...@@ -149,6 +151,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
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) ...@@ -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) ...@@ -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;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment