Commit edb5abb1 authored by Roland Dreier's avatar Roland Dreier

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: default avatarYossi Etigin <yosefe@voltaire.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 5d80f8e5
...@@ -70,12 +70,14 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) ...@@ -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) ...@@ -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) ...@@ -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 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) ...@@ -146,19 +148,19 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
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) ...@@ -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;
} }
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