From d5e16218b99fd0274764aa4022c0b74ecae6145a Mon Sep 17 00:00:00 2001 From: nbd Date: Thu, 18 Nov 2010 10:06:08 +0000 Subject: mac80211: merge pending cfg80211 patches to fix a race condition with setting the regdomain for multiple cards git-svn-id: svn://svn.openwrt.org/openwrt/trunk@24028 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- package/mac80211/patches/310-pending_work.patch | 178 ++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/package/mac80211/patches/310-pending_work.patch b/package/mac80211/patches/310-pending_work.patch index b693b0a6d..ea02fcbc1 100644 --- a/package/mac80211/patches/310-pending_work.patch +++ b/package/mac80211/patches/310-pending_work.patch @@ -112,3 +112,181 @@ 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(®_requests_lock); ++ if (!list_empty(®_requests_list)) ++ need_more_processing = true; ++ spin_unlock(®_requests_lock); ++ ++ if (need_more_processing) ++ schedule_work(®_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(®_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(®_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(®_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(®_requests_lock); +- while (!list_empty(®_requests_list)) { +- reg_request = list_first_entry(®_requests_list, +- struct regulatory_request, +- list); +- list_del_init(®_request->list); + ++ if (list_empty(®_requests_list)) { + spin_unlock(®_requests_lock); +- reg_process_hint(reg_request); +- spin_lock(®_requests_lock); ++ goto out; + } ++ ++ reg_request = list_first_entry(®_requests_list, ++ struct regulatory_request, ++ list); ++ list_del_init(®_request->list); ++ + spin_unlock(®_requests_lock); ++ ++ reg_process_hint(reg_request); ++ ++out: ++ mutex_unlock(®_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(®_mutex); + + return r; -- cgit v1.2.3