aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband/ulp/ipoib/ipoib_main.c
diff options
context:
space:
mode:
authorJason Gunthorpe <jgg@mellanox.com>2018-07-29 11:34:58 +0300
committerJason Gunthorpe <jgg@mellanox.com>2018-08-02 20:27:43 -0600
commit25405d98a2aa0b9983bb9c36b0b00815d39394f4 (patch)
treec953e4b89a1d828969828d35aefa8cabac97fb7f /drivers/infiniband/ulp/ipoib/ipoib_main.c
parentIB/ipoib: Get rid of the sysfs_mutex (diff)
downloadlinux-dev-25405d98a2aa0b9983bb9c36b0b00815d39394f4.tar.xz
linux-dev-25405d98a2aa0b9983bb9c36b0b00815d39394f4.zip
IB/ipoib: Do not remove child devices from within the ndo_uninit
Switching to priv_destructor and needs_free_netdev created a subtle ordering problem in ipoib_remove_one. Now that unregister_netdev frees the netdev and priv we must ensure that the children are unregistered before trying to unregister the parent, or child unregister will use after free. The solution is to unregister the children, then parent, in the same batch all while holding the rtnl_lock. This closes all the races where a new child could have been added and ensures proper ordering. Signed-off-by: Jason Gunthorpe <jgg@mellanox.com> Signed-off-by: Leon Romanovsky <leonro@mellanox.com>
Diffstat (limited to 'drivers/infiniband/ulp/ipoib/ipoib_main.c')
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c28
1 files changed, 17 insertions, 11 deletions
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index e9f4f261fe20..b2fe23d60103 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1939,18 +1939,15 @@ static int ipoib_ndo_init(struct net_device *ndev)
static void ipoib_ndo_uninit(struct net_device *dev)
{
- struct ipoib_dev_priv *priv = ipoib_priv(dev), *cpriv, *tcpriv;
- LIST_HEAD(head);
+ struct ipoib_dev_priv *priv = ipoib_priv(dev);
ASSERT_RTNL();
- /* Delete any child interfaces first */
- list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs, list) {
- /* Stop GC on child */
- cancel_delayed_work_sync(&cpriv->neigh_reap_task);
- unregister_netdevice_queue(cpriv->dev, &head);
- }
- unregister_netdevice_many(&head);
+ /*
+ * ipoib_remove_one guarantees the children are removed before the
+ * parent, and that is the only place where a parent can be removed.
+ */
+ WARN_ON(!list_empty(&priv->child_intfs));
ipoib_neigh_hash_uninit(dev);
@@ -2466,16 +2463,25 @@ static void ipoib_add_one(struct ib_device *device)
static void ipoib_remove_one(struct ib_device *device, void *client_data)
{
- struct ipoib_dev_priv *priv, *tmp;
+ struct ipoib_dev_priv *priv, *tmp, *cpriv, *tcpriv;
struct list_head *dev_list = client_data;
if (!dev_list)
return;
list_for_each_entry_safe(priv, tmp, dev_list, list) {
+ LIST_HEAD(head);
ipoib_parent_unregister_pre(priv->dev);
- unregister_netdev(priv->dev);
+ rtnl_lock();
+
+ list_for_each_entry_safe(cpriv, tcpriv, &priv->child_intfs,
+ list)
+ unregister_netdevice_queue(cpriv->dev, &head);
+ unregister_netdevice_queue(priv->dev, &head);
+ unregister_netdevice_many(&head);
+
+ rtnl_unlock();
}
kfree(dev_list);