kfree(mc_filter);
        }
 
-       unregister_netdevice(vif->ndev);
+       cfg80211_unregister_netdevice(vif->ndev);
 
        ar->num_vif--;
 }
 
        netdev_set_default_ethtool_ops(ndev, &ath6kl_ethtool_ops);
 
-       if (register_netdevice(ndev))
+       if (cfg80211_register_netdevice(ndev))
                goto err;
 
        ar->avail_idx_map &= ~BIT(fw_vif_idx);
 
                if (rc)
                        return rc;
        }
-       rc = register_netdevice(ndev);
+       rc = cfg80211_register_netdevice(ndev);
        if (rc < 0) {
                dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc);
                if (any_active && vif->mid != 0)
        /* during unregister_netdevice cfg80211_leave may perform operations
         * such as stop AP, disconnect, so we only clear the VIF afterwards
         */
-       unregister_netdevice(ndev);
+       cfg80211_unregister_netdevice(ndev);
 
        if (any_active && vif->mid != 0)
                wmi_port_delete(wil, vif->mid);
 
        INIT_WORK(&ifp->ndoffload_work, _brcmf_update_ndtable);
 
        if (rtnl_locked)
-               err = register_netdevice(ndev);
+               err = cfg80211_register_netdevice(ndev);
        else
                err = register_netdev(ndev);
        if (err != 0) {
 {
        if (ndev->reg_state == NETREG_REGISTERED) {
                if (rtnl_locked)
-                       unregister_netdevice(ndev);
+                       cfg80211_unregister_netdevice(ndev);
                else
                        unregister_netdev(ndev);
        } else {
        ndev = ifp->ndev;
        ndev->netdev_ops = &brcmf_netdev_ops_mon;
 
-       err = register_netdevice(ndev);
+       err = cfg80211_register_netdevice(ndev);
        if (err)
                bphy_err(drvr, "Failed to register %s device\n", ndev->name);
 
 
        mutex_init(&priv->async_mutex);
 
        /* Register network device */
-       if (register_netdevice(dev)) {
+       if (cfg80211_register_netdevice(dev)) {
                mwifiex_dbg(adapter, ERROR, "cannot register network device\n");
                ret = -EFAULT;
                goto err_reg_netdev;
                netif_carrier_off(priv->netdev);
 
        if (wdev->netdev->reg_state == NETREG_REGISTERED)
-               unregister_netdevice(wdev->netdev);
+               cfg80211_unregister_netdevice(wdev->netdev);
 
        if (priv->dfs_cac_workqueue) {
                flush_workqueue(priv->dfs_cac_workqueue);
 
                wilc_wfi_deinit_mon_interface(wl, true);
        vif = netdev_priv(wdev->netdev);
        cfg80211_stop_iface(wiphy, wdev, GFP_KERNEL);
-       unregister_netdevice(vif->ndev);
+       cfg80211_unregister_netdevice(vif->ndev);
        vif->monitor_flag = 0;
 
        wilc_set_operation_mode(vif, 0, 0, 0);
 
        wl->monitor_dev->netdev_ops = &wilc_wfi_netdev_ops;
        wl->monitor_dev->needs_free_netdev = true;
 
-       if (register_netdevice(wl->monitor_dev)) {
+       if (cfg80211_register_netdevice(wl->monitor_dev)) {
                netdev_err(real_dev, "register_netdevice failed\n");
                free_netdev(wl->monitor_dev);
                return NULL;
                return;
 
        if (rtnl_locked)
-               unregister_netdevice(wl->monitor_dev);
+               cfg80211_unregister_netdevice(wl->monitor_dev);
        else
                unregister_netdev(wl->monitor_dev);
        wl->monitor_dev = NULL;
 
        vif->priv.dev = ndev;
 
        if (rtnl_locked)
-               ret = register_netdevice(ndev);
+               ret = cfg80211_register_netdevice(ndev);
        else
                ret = register_netdev(ndev);
 
 
        cancel_work_sync(&vif->high_pri_tx_work);
 
        if (netdev->reg_state == NETREG_REGISTERED)
-               unregister_netdevice(netdev);
+               cfg80211_unregister_netdevice(netdev);
 
        if (qtnf_cmd_send_del_intf(vif))
                pr_err("VIF%u.%u: failed to delete VIF\n", vif->mac->macid,
        if (qtnf_hwcap_is_set(&mac->bus->hw_info, QLINK_HW_CAPAB_HW_BRIDGE)) {
                ret = qtnf_cmd_netdev_changeupper(vif, vif->netdev->ifindex);
                if (ret) {
-                       unregister_netdevice(vif->netdev);
+                       cfg80211_unregister_netdevice(vif->netdev);
                        vif->netdev = NULL;
                        goto error_del_vif;
                }
 
 
        SET_NETDEV_DEV(dev, wiphy_dev(wiphy));
 
-       ret = register_netdevice(dev);
+       ret = cfg80211_register_netdevice(dev);
        if (ret) {
                free_netdev(dev);
                vif->netdev = NULL;
 
  *
  * @wiphy: pointer to hardware description
  * @iftype: interface type
+ * @registered: is this wdev already registered with cfg80211
  * @list: (private) Used to collect the interfaces
  * @netdev: (private) Used to reference back to the netdev, may be %NULL
  * @identifier: (private) Identifier used in nl80211 to identify this
 
        struct mutex mtx;
 
-       bool use_4addr, is_running;
+       bool use_4addr, is_running, registered;
 
        u8 address[ETH_ALEN] __aligned(sizeof(u16));
 
  * cfg80211_unregister_wdev - remove the given wdev
  * @wdev: struct wireless_dev to remove
  *
- * Call this function only for wdevs that have no netdev assigned,
- * e.g. P2P Devices. It removes the device from the list so that
- * it can no longer be used. It is necessary to call this function
- * even when cfg80211 requests the removal of the interface by
- * calling the del_virtual_intf() callback. The function must also
- * be called when the driver wishes to unregister the wdev, e.g.
- * when the device is unbound from the driver.
+ * This function removes the device so it can no longer be used. It is necessary
+ * to call this function even when cfg80211 requests the removal of the device
+ * by calling the del_virtual_intf() callback. The function must also be called
+ * when the driver wishes to unregister the wdev, e.g. when the hardware device
+ * is unbound from the driver.
  *
  * Requires the RTNL to be held.
  */
 void cfg80211_unregister_wdev(struct wireless_dev *wdev);
 
+/**
+ * cfg80211_register_netdevice - register the given netdev
+ * @dev: the netdev to register
+ *
+ * Note: In contexts coming from cfg80211 callbacks, you must call this rather
+ * than register_netdevice(), unregister_netdev() is impossible as the RTNL is
+ * held. Otherwise, both register_netdevice() and register_netdev() are usable
+ * instead as well.
+ */
+int cfg80211_register_netdevice(struct net_device *dev);
+
+/**
+ * cfg80211_unregister_netdevice - unregister the given netdev
+ * @dev: the netdev to register
+ *
+ * Note: In contexts coming from cfg80211 callbacks, you must call this rather
+ * than unregister_netdevice(), unregister_netdev() is impossible as the RTNL
+ * is held. Otherwise, both unregister_netdevice() and unregister_netdev() are
+ * usable instead as well.
+ */
+static inline void cfg80211_unregister_netdevice(struct net_device *dev)
+{
+       cfg80211_unregister_wdev(dev->ieee80211_ptr);
+}
+
 /**
  * struct cfg80211_ft_event_params - FT Information Elements
  * @ies: FT IEs
 
                ndev->min_mtu = 256;
                ndev->max_mtu = local->hw.max_mtu;
 
-               ret = register_netdevice(ndev);
+               ret = cfg80211_register_netdevice(ndev);
                if (ret) {
                        free_netdev(ndev);
                        return ret;
 
        synchronize_rcu();
 
-       if (sdata->dev) {
-               unregister_netdevice(sdata->dev);
-       } else {
-               cfg80211_unregister_wdev(&sdata->wdev);
+       cfg80211_unregister_wdev(&sdata->wdev);
+
+       if (!sdata->dev) {
                ieee80211_teardown_sdata(sdata);
                kfree(sdata);
        }
 
        wdev->cqm_config = NULL;
 }
 
-static void __cfg80211_unregister_wdev(struct wireless_dev *wdev, bool sync)
+static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
+                                     bool unregister_netdev)
 {
        struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 
 
        nl80211_notify_iface(rdev, wdev, NL80211_CMD_DEL_INTERFACE);
 
+       wdev->registered = false;
+
+       if (wdev->netdev) {
+               sysfs_remove_link(&wdev->netdev->dev.kobj, "phy80211");
+               if (unregister_netdev)
+                       unregister_netdevice(wdev->netdev);
+       }
+
        list_del_rcu(&wdev->list);
-       if (sync)
-               synchronize_rcu();
+       synchronize_net();
        rdev->devlist_generation++;
 
        cfg80211_mlme_purge_registrations(wdev);
                flush_work(&wdev->disconnect_wk);
 
        cfg80211_cqm_config_free(wdev);
+
+       /*
+        * Ensure that all events have been processed and
+        * freed.
+        */
+       cfg80211_process_wdev_events(wdev);
+
+       if (WARN_ON(wdev->current_bss)) {
+               cfg80211_unhold_bss(wdev->current_bss);
+               cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
+               wdev->current_bss = NULL;
+       }
 }
 
 void cfg80211_unregister_wdev(struct wireless_dev *wdev)
 {
-       if (WARN_ON(wdev->netdev))
-               return;
-
-       __cfg80211_unregister_wdev(wdev, true);
+       _cfg80211_unregister_wdev(wdev, true);
 }
 EXPORT_SYMBOL(cfg80211_unregister_wdev);
 
                wdev->identifier = ++rdev->wdev_id;
        list_add_rcu(&wdev->list, &rdev->wiphy.wdev_list);
        rdev->devlist_generation++;
+       wdev->registered = true;
 
        nl80211_notify_iface(rdev, wdev, NL80211_CMD_NEW_INTERFACE);
 }
 
+int cfg80211_register_netdevice(struct net_device *dev)
+{
+       struct wireless_dev *wdev = dev->ieee80211_ptr;
+       struct cfg80211_registered_device *rdev;
+       int ret;
+
+       ASSERT_RTNL();
+
+       if (WARN_ON(!wdev))
+               return -EINVAL;
+
+       rdev = wiphy_to_rdev(wdev->wiphy);
+
+       lockdep_assert_held(&rdev->wiphy.mtx);
+
+       /* we'll take care of this */
+       wdev->registered = true;
+       ret = register_netdevice(dev);
+       if (ret)
+               goto out;
+
+       if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
+                             "phy80211")) {
+               pr_err("failed to add phy80211 symlink to netdev!\n");
+               unregister_netdevice(dev);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       cfg80211_register_wdev(rdev, wdev);
+       ret = 0;
+out:
+       if (ret)
+               wdev->registered = false;
+       return ret;
+}
+EXPORT_SYMBOL(cfg80211_register_netdevice);
+
 static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
                                         unsigned long state, void *ptr)
 {
                cfg80211_init_wdev(wdev);
                break;
        case NETDEV_REGISTER:
+               if (!wdev->registered)
+                       cfg80211_register_wdev(rdev, wdev);
+               break;
+       case NETDEV_UNREGISTER:
                /*
-                * NB: cannot take rdev->mtx here because this may be
-                * called within code protected by it when interfaces
-                * are added with nl80211.
+                * It is possible to get NETDEV_UNREGISTER multiple times,
+                * so check wdev->registered.
                 */
-               if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
-                                     "phy80211")) {
-                       pr_err("failed to add phy80211 symlink to netdev!\n");
-               }
-
-               cfg80211_register_wdev(rdev, wdev);
+               if (wdev->registered)
+                       _cfg80211_unregister_wdev(wdev, false);
                break;
        case NETDEV_GOING_DOWN:
                cfg80211_leave(rdev, wdev);
                        wdev->ps = false;
                }
                break;
-       case NETDEV_UNREGISTER:
-               /*
-                * It is possible to get NETDEV_UNREGISTER
-                * multiple times. To detect that, check
-                * that the interface is still on the list
-                * of registered interfaces, and only then
-                * remove and clean it up.
-                */
-               if (!list_empty(&wdev->list)) {
-                       __cfg80211_unregister_wdev(wdev, false);
-                       sysfs_remove_link(&dev->dev.kobj, "phy80211");
-               }
-               /*
-                * synchronise (so that we won't find this netdev
-                * from other code any more) and then clear the list
-                * head so that the above code can safely check for
-                * !list_empty() to avoid double-cleanup.
-                */
-               synchronize_rcu();
-               INIT_LIST_HEAD(&wdev->list);
-               /*
-                * Ensure that all events have been processed and
-                * freed.
-                */
-               cfg80211_process_wdev_events(wdev);
-
-               if (WARN_ON(wdev->current_bss)) {
-                       cfg80211_unhold_bss(wdev->current_bss);
-                       cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
-                       wdev->current_bss = NULL;
-               }
-               break;
        case NETDEV_PRE_UP:
                if (!cfg80211_iftype_allowed(wdev->wiphy, wdev->iftype,
                                             wdev->use_4addr, 0))