IPoIB: Avoid free_netdev() BUG when destroying a child interface
We have to release the RTNL before calling free_netdev() so that the device state has a chance to become NETREG_UNREGISTERED. Otherwise when removing a child interface, we hit the BUG() that tests the device state in free_netdev(). Reported-by: Yossi Etigin <yosefe@voltaire.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
@@ -70,12 +70,14 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
|
|||||||
*/
|
*/
|
||||||
if (ppriv->pkey == pkey) {
|
if (ppriv->pkey == pkey) {
|
||||||
result = -ENOTUNIQ;
|
result = -ENOTUNIQ;
|
||||||
|
priv = NULL;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(priv, &ppriv->child_intfs, list) {
|
list_for_each_entry(priv, &ppriv->child_intfs, list) {
|
||||||
if (priv->pkey == pkey) {
|
if (priv->pkey == pkey) {
|
||||||
result = -ENOTUNIQ;
|
result = -ENOTUNIQ;
|
||||||
|
priv = NULL;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,7 +98,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
|
|||||||
|
|
||||||
result = ipoib_set_dev_features(priv, ppriv->ca);
|
result = ipoib_set_dev_features(priv, ppriv->ca);
|
||||||
if (result)
|
if (result)
|
||||||
goto device_init_failed;
|
goto err;
|
||||||
|
|
||||||
priv->pkey = pkey;
|
priv->pkey = pkey;
|
||||||
|
|
||||||
@@ -109,7 +111,7 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
|
|||||||
ipoib_warn(ppriv, "failed to initialize subinterface: "
|
ipoib_warn(ppriv, "failed to initialize subinterface: "
|
||||||
"device %s, port %d",
|
"device %s, port %d",
|
||||||
ppriv->ca->name, ppriv->port);
|
ppriv->ca->name, ppriv->port);
|
||||||
goto device_init_failed;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = register_netdevice(priv->dev);
|
result = register_netdevice(priv->dev);
|
||||||
@@ -146,19 +148,19 @@ sysfs_failed:
|
|||||||
register_failed:
|
register_failed:
|
||||||
ipoib_dev_cleanup(priv->dev);
|
ipoib_dev_cleanup(priv->dev);
|
||||||
|
|
||||||
device_init_failed:
|
|
||||||
free_netdev(priv->dev);
|
|
||||||
|
|
||||||
err:
|
err:
|
||||||
mutex_unlock(&ppriv->vlan_mutex);
|
mutex_unlock(&ppriv->vlan_mutex);
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
|
if (priv)
|
||||||
|
free_netdev(priv->dev);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
|
int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
|
||||||
{
|
{
|
||||||
struct ipoib_dev_priv *ppriv, *priv, *tpriv;
|
struct ipoib_dev_priv *ppriv, *priv, *tpriv;
|
||||||
int ret = -ENOENT;
|
struct net_device *dev = NULL;
|
||||||
|
|
||||||
if (!capable(CAP_NET_ADMIN))
|
if (!capable(CAP_NET_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
@@ -172,14 +174,17 @@ int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
|
|||||||
unregister_netdevice(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);
|
dev = priv->dev;
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&ppriv->vlan_mutex);
|
mutex_unlock(&ppriv->vlan_mutex);
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
|
|
||||||
return ret;
|
if (dev) {
|
||||||
|
free_netdev(dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user