--- x/drivers/infiniband/core/device.c +++ y/drivers/infiniband/core/device.c @@ -2093,29 +2093,6 @@ int ib_query_port(struct ib_device *devi } EXPORT_SYMBOL(ib_query_port); -static void add_ndev_hash(struct ib_port_data *pdata) -{ - unsigned long flags; - - might_sleep(); - - spin_lock_irqsave(&ndev_hash_lock, flags); - if (hash_hashed(&pdata->ndev_hash_link)) { - hash_del_rcu(&pdata->ndev_hash_link); - spin_unlock_irqrestore(&ndev_hash_lock, flags); - /* - * We cannot do hash_add_rcu after a hash_del_rcu until the - * grace period - */ - synchronize_rcu(); - spin_lock_irqsave(&ndev_hash_lock, flags); - } - if (pdata->netdev) - hash_add_rcu(ndev_hash, &pdata->ndev_hash_link, - (uintptr_t)pdata->netdev); - spin_unlock_irqrestore(&ndev_hash_lock, flags); -} - /** * ib_device_set_netdev - Associate the ib_dev with an underlying net_device * @ib_dev: Device to modify @@ -2139,6 +2116,10 @@ int ib_device_set_netdev(struct ib_devic unsigned long flags; int ret; + might_sleep(); + + if (!rdma_is_port_valid(ib_dev, port)) + return -EINVAL; /* * Drivers wish to call this before ib_register_driver, so we have to * setup the port data early. @@ -2147,28 +2128,32 @@ int ib_device_set_netdev(struct ib_devic if (ret) return ret; - if (!rdma_is_port_valid(ib_dev, port)) - return -EINVAL; - pdata = &ib_dev->port_data[port]; +again: spin_lock_irqsave(&pdata->netdev_lock, flags); - old_ndev = rcu_dereference_protected( - pdata->netdev, lockdep_is_held(&pdata->netdev_lock)); + old_ndev = rcu_dereference_protected(pdata->netdev, lockdep_is_held(&pdata->netdev_lock)); if (old_ndev == ndev) { spin_unlock_irqrestore(&pdata->netdev_lock, flags); return 0; } - if (old_ndev) - netdev_tracker_free(ndev, &pdata->netdev_tracker); - if (ndev) - netdev_hold(ndev, &pdata->netdev_tracker, GFP_ATOMIC); + spin_lock(&ndev_hash_lock); + if (hash_hashed(&pdata->ndev_hash_link)) { + hash_del_rcu(&pdata->ndev_hash_link); + spin_unlock(&ndev_hash_lock); + spin_unlock_irqrestore(&pdata->netdev_lock, flags); + synchronize_rcu(); + goto again; + } rcu_assign_pointer(pdata->netdev, ndev); + if (ndev) { + dev_hold(ndev); + hash_add_rcu(ndev_hash, &pdata->ndev_hash_link, (uintptr_t)pdata->netdev); + } + spin_unlock(&ndev_hash_lock); spin_unlock_irqrestore(&pdata->netdev_lock, flags); - add_ndev_hash(pdata); - if (old_ndev) - __dev_put(old_ndev); + dev_put(old_ndev); return 0; }