mac80211: merge pending cfg80211 patches to fix a race condition with setting the...
authorFelix Fietkau <nbd@openwrt.org>
Thu, 18 Nov 2010 10:06:08 +0000 (10:06 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Thu, 18 Nov 2010 10:06:08 +0000 (10:06 +0000)
SVN-Revision: 24028

package/mac80211/patches/310-pending_work.patch

index b693b0a..ea02fcb 100644 (file)
        return true;
  }
  
+--- a/include/net/regulatory.h
++++ b/include/net/regulatory.h
+@@ -43,6 +43,12 @@ enum environment_cap {
+  * @intersect: indicates whether the wireless core should intersect
+  *    the requested regulatory domain with the presently set regulatory
+  *    domain.
++ * @processed: indicates whether or not this requests has already been
++ *    processed. When the last request is processed it means that the
++ *    currently regulatory domain set on cfg80211 is updated from
++ *    CRDA and can be used by other regulatory requests. When a
++ *    the last request is not yet processed we must yield until it
++ *    is processed before processing any new requests.
+  * @country_ie_checksum: checksum of the last processed and accepted
+  *    country IE
+  * @country_ie_env: lets us know if the AP is telling us we are outdoor,
+@@ -54,6 +60,7 @@ struct regulatory_request {
+       enum nl80211_reg_initiator initiator;
+       char alpha2[2];
+       bool intersect;
++      bool processed;
+       enum environment_cap country_ie_env;
+       struct list_head list;
+ };
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -96,6 +96,9 @@ struct reg_beacon {
+       struct ieee80211_channel chan;
+ };
++static void reg_todo(struct work_struct *work);
++static DECLARE_WORK(reg_work, reg_todo);
++
+ /* We keep a static world regulatory domain in case of the absence of CRDA */
+ static const struct ieee80211_regdomain world_regdom = {
+       .n_reg_rules = 5,
+@@ -1317,6 +1320,21 @@ static int ignore_request(struct wiphy *
+       return -EINVAL;
+ }
++static void reg_set_request_processed(void)
++{
++      bool need_more_processing = false;
++
++      last_request->processed = true;
++
++      spin_lock(&reg_requests_lock);
++      if (!list_empty(&reg_requests_list))
++              need_more_processing = true;
++      spin_unlock(&reg_requests_lock);
++
++      if (need_more_processing)
++              schedule_work(&reg_work);
++}
++
+ /**
+  * __regulatory_hint - hint to the wireless core a regulatory domain
+  * @wiphy: if the hint comes from country information from an AP, this
+@@ -1392,8 +1410,10 @@ new_request:
+                * have applied the requested regulatory domain before we just
+                * inform userspace we have processed the request
+                */
+-              if (r == -EALREADY)
++              if (r == -EALREADY) {
+                       nl80211_send_reg_change_event(last_request);
++                      reg_set_request_processed();
++              }
+               return r;
+       }
+@@ -1409,16 +1429,13 @@ static void reg_process_hint(struct regu
+       BUG_ON(!reg_request->alpha2);
+-      mutex_lock(&cfg80211_mutex);
+-      mutex_lock(&reg_mutex);
+-
+       if (wiphy_idx_valid(reg_request->wiphy_idx))
+               wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
+       if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+           !wiphy) {
+               kfree(reg_request);
+-              goto out;
++              return;
+       }
+       r = __regulatory_hint(wiphy, reg_request);
+@@ -1426,28 +1443,46 @@ static void reg_process_hint(struct regu
+       if (r == -EALREADY && wiphy &&
+           wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+               wiphy_update_regulatory(wiphy, initiator);
+-out:
+-      mutex_unlock(&reg_mutex);
+-      mutex_unlock(&cfg80211_mutex);
+ }
+-/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */
++/*
++ * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
++ * Regulatory hints come on a first come first serve basis and we
++ * must process each one atomically.
++ */
+ static void reg_process_pending_hints(void)
+-      {
++{
+       struct regulatory_request *reg_request;
++      mutex_lock(&cfg80211_mutex);
++      mutex_lock(&reg_mutex);
++
++      /* When last_request->processed becomes true this will be rescheduled */
++      if (last_request && !last_request->processed) {
++              REG_DBG_PRINT("Pending regulatory request, waiting "
++                            "for it to be processed...");
++              goto out;
++      }
++
+       spin_lock(&reg_requests_lock);
+-      while (!list_empty(&reg_requests_list)) {
+-              reg_request = list_first_entry(&reg_requests_list,
+-                                             struct regulatory_request,
+-                                             list);
+-              list_del_init(&reg_request->list);
++      if (list_empty(&reg_requests_list)) {
+               spin_unlock(&reg_requests_lock);
+-              reg_process_hint(reg_request);
+-              spin_lock(&reg_requests_lock);
++              goto out;
+       }
++
++      reg_request = list_first_entry(&reg_requests_list,
++                                     struct regulatory_request,
++                                     list);
++      list_del_init(&reg_request->list);
++
+       spin_unlock(&reg_requests_lock);
++
++      reg_process_hint(reg_request);
++
++out:
++      mutex_unlock(&reg_mutex);
++      mutex_unlock(&cfg80211_mutex);
+ }
+ /* Processes beacon hints -- this has nothing to do with country IEs */
+@@ -1494,8 +1529,6 @@ static void reg_todo(struct work_struct 
+       reg_process_pending_beacon_hints();
+ }
+-static DECLARE_WORK(reg_work, reg_todo);
+-
+ static void queue_regulatory_request(struct regulatory_request *request)
+ {
+       if (isalpha(request->alpha2[0]))
+@@ -1530,12 +1563,7 @@ static int regulatory_hint_core(const ch
+       request->alpha2[1] = alpha2[1];
+       request->initiator = NL80211_REGDOM_SET_BY_CORE;
+-      /*
+-       * This ensures last_request is populated once modules
+-       * come swinging in and calling regulatory hints and
+-       * wiphy_apply_custom_regulatory().
+-       */
+-      reg_process_hint(request);
++      queue_regulatory_request(request);
+       return 0;
+ }
+@@ -2061,6 +2089,8 @@ int set_regdom(const struct ieee80211_re
+       nl80211_send_reg_change_event(last_request);
++      reg_set_request_processed();
++
+       mutex_unlock(&reg_mutex);
+       return r;