mac80211: fix race in ieee80211_register_hw()
[sfrench/cifs-2.6.git] / net / mac80211 / main.c
index 8345926193de487c6f783b0a452ff0c74eba02c1..0e9ad60fb2b3d9dc9a0bcacfb4e8ec08fd112df0 100644 (file)
@@ -1069,7 +1069,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
                if (hw->max_signal <= 0) {
                        result = -EINVAL;
-                       goto fail_wiphy_register;
+                       goto fail_workqueue;
                }
        }
 
@@ -1135,7 +1135,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        result = ieee80211_init_cipher_suites(local);
        if (result < 0)
-               goto fail_wiphy_register;
+               goto fail_workqueue;
 
        if (!local->ops->remain_on_channel)
                local->hw.wiphy->max_remain_on_channel_duration = 5000;
@@ -1161,10 +1161,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
        local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
 
-       result = wiphy_register(local->hw.wiphy);
-       if (result < 0)
-               goto fail_wiphy_register;
-
        /*
         * We use the number of queues for feature tests (QoS, HT) internally
         * so restrict them appropriately.
@@ -1217,9 +1213,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                goto fail_flows;
 
        rtnl_lock();
-
        result = ieee80211_init_rate_ctrl_alg(local,
                                              hw->rate_control_algorithm);
+       rtnl_unlock();
        if (result < 0) {
                wiphy_debug(local->hw.wiphy,
                            "Failed to initialize rate control algorithm\n");
@@ -1273,6 +1269,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
                local->sband_allocated |= BIT(band);
        }
 
+       result = wiphy_register(local->hw.wiphy);
+       if (result < 0)
+               goto fail_wiphy_register;
+
+       rtnl_lock();
+
        /* add one default STA interface if supported */
        if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
            !ieee80211_hw_check(hw, NO_AUTO_VIF)) {
@@ -1312,17 +1314,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 #if defined(CONFIG_INET) || defined(CONFIG_IPV6)
  fail_ifa:
 #endif
+       wiphy_unregister(local->hw.wiphy);
+ fail_wiphy_register:
        rtnl_lock();
        rate_control_deinitialize(local);
        ieee80211_remove_interfaces(local);
- fail_rate:
        rtnl_unlock();
+ fail_rate:
  fail_flows:
        ieee80211_led_exit(local);
        destroy_workqueue(local->workqueue);
  fail_workqueue:
-       wiphy_unregister(local->hw.wiphy);
- fail_wiphy_register:
        if (local->wiphy_ciphers_allocated)
                kfree(local->hw.wiphy->cipher_suites);
        kfree(local->int_scan_req);
@@ -1372,8 +1374,8 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
        skb_queue_purge(&local->skb_queue_unreliable);
        skb_queue_purge(&local->skb_queue_tdls_chsw);
 
-       destroy_workqueue(local->workqueue);
        wiphy_unregister(local->hw.wiphy);
+       destroy_workqueue(local->workqueue);
        ieee80211_led_exit(local);
        kfree(local->int_scan_req);
 }