aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2010-11-18 00:46:09 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-11-22 15:48:51 -0500
commitb2e253cf300c5e33f49b7dd8b593bfc722177401 (patch)
tree0da4d4121996f7869b0ce5ac469c6dd5e7c7beaa
parentb0e2880b0518ad11af20c7c93ec5cac93f9f03b0 (diff)
cfg80211: Fix regulatory bug with multiple cards and delays
When two cards are connected with the same regulatory domain if CRDA had a delayed response then cfg80211's own set regulatory domain would still be the world regulatory domain. There was a bug on cfg80211's logic such that it assumed that once you pegged a request as the last request it was already the currently set regulatory domain. This would mean we would race setting a stale regulatory domain to secondary cards which had the same regulatory domain since the alpha2 would match. We fix this by processing each regulatory request atomically, and only move on to the next one once we get it fully processed. In the case CRDA is not present we will simply world roam. This issue is only present when you have a slow system and the CRDA processing is delayed. Because of this it is not a known regression. Without this fix when a delay is present with CRDA the second card would end up with an intersected regulatory domain and not allow it to use the channels it really is designed for. When two cards with two different regulatory domains were inserted you'd end up rejecting the second card's regulatory domain request. This fails with mac80211_hswim's regtest=2 (two requests, same alpha2) and regtest=3 (two requests, different alpha2) module parameter options. This was reproduced and tested against mac80211_hwsim using this CRDA delayer: #!/bin/bash echo $COUNTRY >> /tmp/log sleep 2 /sbin/crda.orig And these regulatory tests: modprobe mac80211_hwsim regtest=2 modprobe mac80211_hwsim regtest=3 Reported-by: Mark Mentovai <mark@moxienet.com> Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com> Tested-by: Mark Mentovai <mark@moxienet.com> Tested-by: Bruno Randolf <br1@einfach.org> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--include/net/regulatory.h7
-rw-r--r--net/wireless/reg.c52
2 files changed, 50 insertions, 9 deletions
diff --git a/include/net/regulatory.h b/include/net/regulatory.h
index 9e103a4e91ee..356d6e3dc20a 100644
--- a/include/net/regulatory.h
+++ b/include/net/regulatory.h
@@ -43,6 +43,12 @@ enum environment_cap {
43 * @intersect: indicates whether the wireless core should intersect 43 * @intersect: indicates whether the wireless core should intersect
44 * the requested regulatory domain with the presently set regulatory 44 * the requested regulatory domain with the presently set regulatory
45 * domain. 45 * domain.
46 * @processed: indicates whether or not this requests has already been
47 * processed. When the last request is processed it means that the
48 * currently regulatory domain set on cfg80211 is updated from
49 * CRDA and can be used by other regulatory requests. When a
50 * the last request is not yet processed we must yield until it
51 * is processed before processing any new requests.
46 * @country_ie_checksum: checksum of the last processed and accepted 52 * @country_ie_checksum: checksum of the last processed and accepted
47 * country IE 53 * country IE
48 * @country_ie_env: lets us know if the AP is telling us we are outdoor, 54 * @country_ie_env: lets us know if the AP is telling us we are outdoor,
@@ -54,6 +60,7 @@ struct regulatory_request {
54 enum nl80211_reg_initiator initiator; 60 enum nl80211_reg_initiator initiator;
55 char alpha2[2]; 61 char alpha2[2];
56 bool intersect; 62 bool intersect;
63 bool processed;
57 enum environment_cap country_ie_env; 64 enum environment_cap country_ie_env;
58 struct list_head list; 65 struct list_head list;
59}; 66};
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index b522c46c4748..bc14caab19cd 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -1320,6 +1320,21 @@ static int ignore_request(struct wiphy *wiphy,
1320 return -EINVAL; 1320 return -EINVAL;
1321} 1321}
1322 1322
1323static void reg_set_request_processed(void)
1324{
1325 bool need_more_processing = false;
1326
1327 last_request->processed = true;
1328
1329 spin_lock(&reg_requests_lock);
1330 if (!list_empty(&reg_requests_list))
1331 need_more_processing = true;
1332 spin_unlock(&reg_requests_lock);
1333
1334 if (need_more_processing)
1335 schedule_work(&reg_work);
1336}
1337
1323/** 1338/**
1324 * __regulatory_hint - hint to the wireless core a regulatory domain 1339 * __regulatory_hint - hint to the wireless core a regulatory domain
1325 * @wiphy: if the hint comes from country information from an AP, this 1340 * @wiphy: if the hint comes from country information from an AP, this
@@ -1395,8 +1410,10 @@ new_request:
1395 * have applied the requested regulatory domain before we just 1410 * have applied the requested regulatory domain before we just
1396 * inform userspace we have processed the request 1411 * inform userspace we have processed the request
1397 */ 1412 */
1398 if (r == -EALREADY) 1413 if (r == -EALREADY) {
1399 nl80211_send_reg_change_event(last_request); 1414 nl80211_send_reg_change_event(last_request);
1415 reg_set_request_processed();
1416 }
1400 return r; 1417 return r;
1401 } 1418 }
1402 1419
@@ -1428,7 +1445,11 @@ static void reg_process_hint(struct regulatory_request *reg_request)
1428 wiphy_update_regulatory(wiphy, initiator); 1445 wiphy_update_regulatory(wiphy, initiator);
1429} 1446}
1430 1447
1431/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */ 1448/*
1449 * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_*
1450 * Regulatory hints come on a first come first serve basis and we
1451 * must process each one atomically.
1452 */
1432static void reg_process_pending_hints(void) 1453static void reg_process_pending_hints(void)
1433{ 1454{
1434 struct regulatory_request *reg_request; 1455 struct regulatory_request *reg_request;
@@ -1436,19 +1457,30 @@ static void reg_process_pending_hints(void)
1436 mutex_lock(&cfg80211_mutex); 1457 mutex_lock(&cfg80211_mutex);
1437 mutex_lock(&reg_mutex); 1458 mutex_lock(&reg_mutex);
1438 1459
1460 /* When last_request->processed becomes true this will be rescheduled */
1461 if (last_request && !last_request->processed) {
1462 REG_DBG_PRINT("Pending regulatory request, waiting "
1463 "for it to be processed...");
1464 goto out;
1465 }
1466
1439 spin_lock(&reg_requests_lock); 1467 spin_lock(&reg_requests_lock);
1440 while (!list_empty(&reg_requests_list)) {
1441 reg_request = list_first_entry(&reg_requests_list,
1442 struct regulatory_request,
1443 list);
1444 list_del_init(&reg_request->list);
1445 1468
1469 if (list_empty(&reg_requests_list)) {
1446 spin_unlock(&reg_requests_lock); 1470 spin_unlock(&reg_requests_lock);
1447 reg_process_hint(reg_request); 1471 goto out;
1448 spin_lock(&reg_requests_lock);
1449 } 1472 }
1473
1474 reg_request = list_first_entry(&reg_requests_list,
1475 struct regulatory_request,
1476 list);
1477 list_del_init(&reg_request->list);
1478
1450 spin_unlock(&reg_requests_lock); 1479 spin_unlock(&reg_requests_lock);
1451 1480
1481 reg_process_hint(reg_request);
1482
1483out:
1452 mutex_unlock(&reg_mutex); 1484 mutex_unlock(&reg_mutex);
1453 mutex_unlock(&cfg80211_mutex); 1485 mutex_unlock(&cfg80211_mutex);
1454} 1486}
@@ -2057,6 +2089,8 @@ int set_regdom(const struct ieee80211_regdomain *rd)
2057 2089
2058 nl80211_send_reg_change_event(last_request); 2090 nl80211_send_reg_change_event(last_request);
2059 2091
2092 reg_set_request_processed();
2093
2060 mutex_unlock(&reg_mutex); 2094 mutex_unlock(&reg_mutex);
2061 2095
2062 return r; 2096 return r;