Commit 13476d35 authored by Jason Gunthorpe's avatar Jason Gunthorpe

IB/ipoib: Maintain the child_intfs list from ndo_init/uninit

This fixes a bug in the netlink path where the vlan_rwsem was not
held around __ipoib_vlan_add causing the child_intfs to be manipulated
unsafely.

In the process this greatly simplifies the vlan_rwsem write side locking
to only cover a single non-sleeping statement.

This also further increases the safety of the removal ordering by holding
the netdev of the parent while the child is active to ensure most bugs
become either an oops on a NULL priv or a deadlock on the netdev refcount.
Signed-off-by: default avatarJason Gunthorpe <jgg@mellanox.com>
Signed-off-by: default avatarLeon Romanovsky <leonro@mellanox.com>
parent 25405d98
...@@ -1890,6 +1890,12 @@ static void ipoib_child_init(struct net_device *ndev) ...@@ -1890,6 +1890,12 @@ static void ipoib_child_init(struct net_device *ndev)
struct ipoib_dev_priv *priv = ipoib_priv(ndev); struct ipoib_dev_priv *priv = ipoib_priv(ndev);
struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent); struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent);
dev_hold(priv->parent);
down_write(&ppriv->vlan_rwsem);
list_add_tail(&priv->list, &ppriv->child_intfs);
up_write(&ppriv->vlan_rwsem);
priv->max_ib_mtu = ppriv->max_ib_mtu; priv->max_ib_mtu = ppriv->max_ib_mtu;
set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags); set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags);
memcpy(priv->dev->dev_addr, ppriv->dev->dev_addr, INFINIBAND_ALEN); memcpy(priv->dev->dev_addr, ppriv->dev->dev_addr, INFINIBAND_ALEN);
...@@ -1959,6 +1965,16 @@ static void ipoib_ndo_uninit(struct net_device *dev) ...@@ -1959,6 +1965,16 @@ static void ipoib_ndo_uninit(struct net_device *dev)
destroy_workqueue(priv->wq); destroy_workqueue(priv->wq);
priv->wq = NULL; priv->wq = NULL;
} }
if (priv->parent) {
struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent);
down_write(&ppriv->vlan_rwsem);
list_del(&priv->list);
up_write(&ppriv->vlan_rwsem);
dev_put(priv->parent);
}
} }
static int ipoib_set_vf_link_state(struct net_device *dev, int vf, int link_state) static int ipoib_set_vf_link_state(struct net_device *dev, int vf, int link_state)
......
...@@ -133,19 +133,6 @@ static int ipoib_new_child_link(struct net *src_net, struct net_device *dev, ...@@ -133,19 +133,6 @@ static int ipoib_new_child_link(struct net *src_net, struct net_device *dev,
return err; return err;
} }
static void ipoib_unregister_child_dev(struct net_device *dev, struct list_head *head)
{
struct ipoib_dev_priv *priv, *ppriv;
priv = ipoib_priv(dev);
ppriv = ipoib_priv(priv->parent);
down_write(&ppriv->vlan_rwsem);
unregister_netdevice_queue(dev, head);
list_del(&priv->list);
up_write(&ppriv->vlan_rwsem);
}
static size_t ipoib_get_size(const struct net_device *dev) static size_t ipoib_get_size(const struct net_device *dev)
{ {
return nla_total_size(2) + /* IFLA_IPOIB_PKEY */ return nla_total_size(2) + /* IFLA_IPOIB_PKEY */
...@@ -161,7 +148,6 @@ static struct rtnl_link_ops ipoib_link_ops __read_mostly = { ...@@ -161,7 +148,6 @@ static struct rtnl_link_ops ipoib_link_ops __read_mostly = {
.setup = ipoib_setup_common, .setup = ipoib_setup_common,
.newlink = ipoib_new_child_link, .newlink = ipoib_new_child_link,
.changelink = ipoib_changelink, .changelink = ipoib_changelink,
.dellink = ipoib_unregister_child_dev,
.get_size = ipoib_get_size, .get_size = ipoib_get_size,
.fill_info = ipoib_fill_info, .fill_info = ipoib_fill_info,
}; };
......
...@@ -106,8 +106,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv, ...@@ -106,8 +106,6 @@ int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
goto sysfs_failed; goto sysfs_failed;
} }
list_add_tail(&priv->list, &ppriv->child_intfs);
return 0; return 0;
sysfs_failed: sysfs_failed:
...@@ -139,11 +137,6 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) ...@@ -139,11 +137,6 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
return -EPERM; return -EPERM;
} }
if (!down_write_trylock(&ppriv->vlan_rwsem)) {
rtnl_unlock();
return restart_syscall();
}
/* /*
* First ensure this isn't a duplicate. We check the parent device and * First ensure this isn't a duplicate. We check the parent device and
* then all of the legacy child interfaces to make sure the Pkey * then all of the legacy child interfaces to make sure the Pkey
...@@ -175,7 +168,6 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey) ...@@ -175,7 +168,6 @@ int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
free_netdev(ndev); free_netdev(ndev);
out: out:
up_write(&ppriv->vlan_rwsem);
rtnl_unlock(); rtnl_unlock();
return result; return result;
...@@ -209,10 +201,6 @@ static void ipoib_vlan_delete_task(struct work_struct *work) ...@@ -209,10 +201,6 @@ static void ipoib_vlan_delete_task(struct work_struct *work)
struct ipoib_dev_priv *priv = ipoib_priv(dev); struct ipoib_dev_priv *priv = ipoib_priv(dev);
struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent); struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent);
down_write(&ppriv->vlan_rwsem);
list_del(&priv->list);
up_write(&ppriv->vlan_rwsem);
ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name); ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name);
unregister_netdevice(dev); unregister_netdevice(dev);
} }
......
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