diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-12-06 10:29:25 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-01-03 07:01:30 -0500 |
commit | c492db370c17c428a0a58d3673294d4e99634b7d (patch) | |
tree | a350365e3cdb2f37c1249f6871eaeb36a811512b /net/wireless/reg.c | |
parent | 458f4f9e960b9a3b674c4b87d996eef186b1fe83 (diff) |
regulatory: use RCU to protect last_request
This will allow making freq_reg_info() lock-free.
Acked-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r-- | net/wireless/reg.c | 199 |
1 files changed, 109 insertions, 90 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 9b64b201cdf1..2a7c3adf902f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -82,7 +82,8 @@ static struct regulatory_request core_request_world = { | |||
82 | }; | 82 | }; |
83 | 83 | ||
84 | /* Receipt of information from last regulatory request */ | 84 | /* Receipt of information from last regulatory request */ |
85 | static struct regulatory_request *last_request = &core_request_world; | 85 | static struct regulatory_request __rcu *last_request = |
86 | (void __rcu *)&core_request_world; | ||
86 | 87 | ||
87 | /* To trigger userspace events */ | 88 | /* To trigger userspace events */ |
88 | static struct platform_device *reg_pdev; | 89 | static struct platform_device *reg_pdev; |
@@ -102,7 +103,7 @@ const struct ieee80211_regdomain __rcu *cfg80211_regdomain; | |||
102 | * Protects static reg.c components: | 103 | * Protects static reg.c components: |
103 | * - cfg80211_regdomain (if not used with RCU) | 104 | * - cfg80211_regdomain (if not used with RCU) |
104 | * - cfg80211_world_regdom | 105 | * - cfg80211_world_regdom |
105 | * - last_request | 106 | * - last_request (if not used with RCU) |
106 | * - reg_num_devs_support_basehint | 107 | * - reg_num_devs_support_basehint |
107 | */ | 108 | */ |
108 | static DEFINE_MUTEX(reg_mutex); | 109 | static DEFINE_MUTEX(reg_mutex); |
@@ -137,6 +138,12 @@ static void rcu_free_regdom(const struct ieee80211_regdomain *r) | |||
137 | kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); | 138 | kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); |
138 | } | 139 | } |
139 | 140 | ||
141 | static struct regulatory_request *get_last_request(void) | ||
142 | { | ||
143 | return rcu_dereference_protected(last_request, | ||
144 | lockdep_is_held(®_mutex)); | ||
145 | } | ||
146 | |||
140 | /* Used to queue up regulatory hints */ | 147 | /* Used to queue up regulatory hints */ |
141 | static LIST_HEAD(reg_requests_list); | 148 | static LIST_HEAD(reg_requests_list); |
142 | static spinlock_t reg_requests_lock; | 149 | static spinlock_t reg_requests_lock; |
@@ -206,6 +213,7 @@ static void reset_regdomains(bool full_reset, | |||
206 | const struct ieee80211_regdomain *new_regdom) | 213 | const struct ieee80211_regdomain *new_regdom) |
207 | { | 214 | { |
208 | const struct ieee80211_regdomain *r; | 215 | const struct ieee80211_regdomain *r; |
216 | struct regulatory_request *lr; | ||
209 | 217 | ||
210 | assert_reg_lock(); | 218 | assert_reg_lock(); |
211 | 219 | ||
@@ -228,9 +236,10 @@ static void reset_regdomains(bool full_reset, | |||
228 | if (!full_reset) | 236 | if (!full_reset) |
229 | return; | 237 | return; |
230 | 238 | ||
231 | if (last_request != &core_request_world) | 239 | lr = get_last_request(); |
232 | kfree(last_request); | 240 | if (lr != &core_request_world && lr) |
233 | last_request = &core_request_world; | 241 | kfree_rcu(lr, rcu_head); |
242 | rcu_assign_pointer(last_request, &core_request_world); | ||
234 | } | 243 | } |
235 | 244 | ||
236 | /* | 245 | /* |
@@ -239,9 +248,11 @@ static void reset_regdomains(bool full_reset, | |||
239 | */ | 248 | */ |
240 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) | 249 | static void update_world_regdomain(const struct ieee80211_regdomain *rd) |
241 | { | 250 | { |
242 | WARN_ON(!last_request); | 251 | struct regulatory_request *lr; |
243 | 252 | ||
244 | assert_reg_lock(); | 253 | lr = get_last_request(); |
254 | |||
255 | WARN_ON(!lr); | ||
245 | 256 | ||
246 | reset_regdomains(false, rd); | 257 | reset_regdomains(false, rd); |
247 | 258 | ||
@@ -448,15 +459,12 @@ static int call_crda(const char *alpha2) | |||
448 | 459 | ||
449 | static bool reg_is_valid_request(const char *alpha2) | 460 | static bool reg_is_valid_request(const char *alpha2) |
450 | { | 461 | { |
451 | assert_reg_lock(); | 462 | struct regulatory_request *lr = get_last_request(); |
452 | 463 | ||
453 | if (!last_request) | 464 | if (!lr || lr->processed) |
454 | return false; | 465 | return false; |
455 | 466 | ||
456 | if (last_request->processed) | 467 | return alpha2_equal(lr->alpha2, alpha2); |
457 | return false; | ||
458 | |||
459 | return alpha2_equal(last_request->alpha2, alpha2); | ||
460 | } | 468 | } |
461 | 469 | ||
462 | /* Sanity check on a regulatory rule */ | 470 | /* Sanity check on a regulatory rule */ |
@@ -746,15 +754,14 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, | |||
746 | const struct ieee80211_reg_rule **reg_rule) | 754 | const struct ieee80211_reg_rule **reg_rule) |
747 | { | 755 | { |
748 | const struct ieee80211_regdomain *regd; | 756 | const struct ieee80211_regdomain *regd; |
749 | 757 | struct regulatory_request *lr = get_last_request(); | |
750 | assert_reg_lock(); | ||
751 | 758 | ||
752 | /* | 759 | /* |
753 | * Follow the driver's regulatory domain, if present, unless a country | 760 | * Follow the driver's regulatory domain, if present, unless a country |
754 | * IE has been processed or a user wants to help complaince further | 761 | * IE has been processed or a user wants to help complaince further |
755 | */ | 762 | */ |
756 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 763 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
757 | last_request->initiator != NL80211_REGDOM_SET_BY_USER && | 764 | lr->initiator != NL80211_REGDOM_SET_BY_USER && |
758 | wiphy->regd) | 765 | wiphy->regd) |
759 | regd = get_wiphy_regdom(wiphy); | 766 | regd = get_wiphy_regdom(wiphy); |
760 | else | 767 | else |
@@ -828,8 +835,9 @@ static void handle_channel(struct wiphy *wiphy, | |||
828 | const struct ieee80211_power_rule *power_rule = NULL; | 835 | const struct ieee80211_power_rule *power_rule = NULL; |
829 | const struct ieee80211_freq_range *freq_range = NULL; | 836 | const struct ieee80211_freq_range *freq_range = NULL; |
830 | struct wiphy *request_wiphy = NULL; | 837 | struct wiphy *request_wiphy = NULL; |
838 | struct regulatory_request *lr = get_last_request(); | ||
831 | 839 | ||
832 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 840 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
833 | 841 | ||
834 | flags = chan->orig_flags; | 842 | flags = chan->orig_flags; |
835 | 843 | ||
@@ -862,7 +870,7 @@ static void handle_channel(struct wiphy *wiphy, | |||
862 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) | 870 | if (freq_range->max_bandwidth_khz < MHZ_TO_KHZ(40)) |
863 | bw_flags = IEEE80211_CHAN_NO_HT40; | 871 | bw_flags = IEEE80211_CHAN_NO_HT40; |
864 | 872 | ||
865 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 873 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
866 | request_wiphy && request_wiphy == wiphy && | 874 | request_wiphy && request_wiphy == wiphy && |
867 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { | 875 | request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) { |
868 | /* | 876 | /* |
@@ -927,7 +935,7 @@ bool reg_last_request_cell_base(void) | |||
927 | bool val; | 935 | bool val; |
928 | 936 | ||
929 | mutex_lock(®_mutex); | 937 | mutex_lock(®_mutex); |
930 | val = reg_request_cell_base(last_request); | 938 | val = reg_request_cell_base(get_last_request()); |
931 | mutex_unlock(®_mutex); | 939 | mutex_unlock(®_mutex); |
932 | 940 | ||
933 | return val; | 941 | return val; |
@@ -938,10 +946,12 @@ bool reg_last_request_cell_base(void) | |||
938 | static enum reg_request_treatment | 946 | static enum reg_request_treatment |
939 | reg_ignore_cell_hint(struct regulatory_request *pending_request) | 947 | reg_ignore_cell_hint(struct regulatory_request *pending_request) |
940 | { | 948 | { |
949 | struct regulatory_request *lr = get_last_request(); | ||
950 | |||
941 | if (!reg_num_devs_support_basehint) | 951 | if (!reg_num_devs_support_basehint) |
942 | return REG_REQ_IGNORE; | 952 | return REG_REQ_IGNORE; |
943 | 953 | ||
944 | if (reg_request_cell_base(last_request) && | 954 | if (reg_request_cell_base(lr) && |
945 | !regdom_changes(pending_request->alpha2)) | 955 | !regdom_changes(pending_request->alpha2)) |
946 | return REG_REQ_ALREADY_SET; | 956 | return REG_REQ_ALREADY_SET; |
947 | 957 | ||
@@ -969,7 +979,9 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) | |||
969 | static bool ignore_reg_update(struct wiphy *wiphy, | 979 | static bool ignore_reg_update(struct wiphy *wiphy, |
970 | enum nl80211_reg_initiator initiator) | 980 | enum nl80211_reg_initiator initiator) |
971 | { | 981 | { |
972 | if (!last_request) { | 982 | struct regulatory_request *lr = get_last_request(); |
983 | |||
984 | if (!lr) { | ||
973 | REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", | 985 | REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", |
974 | reg_initiator_name(initiator)); | 986 | reg_initiator_name(initiator)); |
975 | return true; | 987 | return true; |
@@ -988,13 +1000,13 @@ static bool ignore_reg_update(struct wiphy *wiphy, | |||
988 | */ | 1000 | */ |
989 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && | 1001 | if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && |
990 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 1002 | initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
991 | !is_world_regdom(last_request->alpha2)) { | 1003 | !is_world_regdom(lr->alpha2)) { |
992 | REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", | 1004 | REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", |
993 | reg_initiator_name(initiator)); | 1005 | reg_initiator_name(initiator)); |
994 | return true; | 1006 | return true; |
995 | } | 1007 | } |
996 | 1008 | ||
997 | if (reg_request_cell_base(last_request)) | 1009 | if (reg_request_cell_base(lr)) |
998 | return reg_dev_ignore_cell_hint(wiphy); | 1010 | return reg_dev_ignore_cell_hint(wiphy); |
999 | 1011 | ||
1000 | return false; | 1012 | return false; |
@@ -1080,12 +1092,12 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) | |||
1080 | { | 1092 | { |
1081 | const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); | 1093 | const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); |
1082 | const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); | 1094 | const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); |
1095 | struct regulatory_request *lr = get_last_request(); | ||
1083 | 1096 | ||
1084 | if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) | 1097 | if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) |
1085 | return true; | 1098 | return true; |
1086 | 1099 | ||
1087 | if (last_request && | 1100 | if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1088 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | ||
1089 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | 1101 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) |
1090 | return true; | 1102 | return true; |
1091 | 1103 | ||
@@ -1184,13 +1196,12 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1184 | enum nl80211_reg_initiator initiator) | 1196 | enum nl80211_reg_initiator initiator) |
1185 | { | 1197 | { |
1186 | enum ieee80211_band band; | 1198 | enum ieee80211_band band; |
1187 | 1199 | struct regulatory_request *lr = get_last_request(); | |
1188 | assert_reg_lock(); | ||
1189 | 1200 | ||
1190 | if (ignore_reg_update(wiphy, initiator)) | 1201 | if (ignore_reg_update(wiphy, initiator)) |
1191 | return; | 1202 | return; |
1192 | 1203 | ||
1193 | last_request->dfs_region = get_cfg80211_regdom()->dfs_region; | 1204 | lr->dfs_region = get_cfg80211_regdom()->dfs_region; |
1194 | 1205 | ||
1195 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | 1206 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) |
1196 | handle_band(wiphy, initiator, wiphy->bands[band]); | 1207 | handle_band(wiphy, initiator, wiphy->bands[band]); |
@@ -1199,7 +1210,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1199 | reg_process_ht_flags(wiphy); | 1210 | reg_process_ht_flags(wiphy); |
1200 | 1211 | ||
1201 | if (wiphy->reg_notifier) | 1212 | if (wiphy->reg_notifier) |
1202 | wiphy->reg_notifier(wiphy, last_request); | 1213 | wiphy->reg_notifier(wiphy, lr); |
1203 | } | 1214 | } |
1204 | 1215 | ||
1205 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | 1216 | static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) |
@@ -1220,7 +1231,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | |||
1220 | if (initiator == NL80211_REGDOM_SET_BY_CORE && | 1231 | if (initiator == NL80211_REGDOM_SET_BY_CORE && |
1221 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && | 1232 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY && |
1222 | wiphy->reg_notifier) | 1233 | wiphy->reg_notifier) |
1223 | wiphy->reg_notifier(wiphy, last_request); | 1234 | wiphy->reg_notifier(wiphy, get_last_request()); |
1224 | } | 1235 | } |
1225 | } | 1236 | } |
1226 | 1237 | ||
@@ -1300,28 +1311,28 @@ get_reg_request_treatment(struct wiphy *wiphy, | |||
1300 | struct regulatory_request *pending_request) | 1311 | struct regulatory_request *pending_request) |
1301 | { | 1312 | { |
1302 | struct wiphy *last_wiphy = NULL; | 1313 | struct wiphy *last_wiphy = NULL; |
1314 | struct regulatory_request *lr = get_last_request(); | ||
1303 | 1315 | ||
1304 | /* All initial requests are respected */ | 1316 | /* All initial requests are respected */ |
1305 | if (!last_request) | 1317 | if (!lr) |
1306 | return REG_REQ_OK; | 1318 | return REG_REQ_OK; |
1307 | 1319 | ||
1308 | switch (pending_request->initiator) { | 1320 | switch (pending_request->initiator) { |
1309 | case NL80211_REGDOM_SET_BY_CORE: | 1321 | case NL80211_REGDOM_SET_BY_CORE: |
1310 | return REG_REQ_OK; | 1322 | return REG_REQ_OK; |
1311 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: | 1323 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
1312 | if (reg_request_cell_base(last_request)) { | 1324 | if (reg_request_cell_base(lr)) { |
1313 | /* Trust a Cell base station over the AP's country IE */ | 1325 | /* Trust a Cell base station over the AP's country IE */ |
1314 | if (regdom_changes(pending_request->alpha2)) | 1326 | if (regdom_changes(pending_request->alpha2)) |
1315 | return REG_REQ_IGNORE; | 1327 | return REG_REQ_IGNORE; |
1316 | return REG_REQ_ALREADY_SET; | 1328 | return REG_REQ_ALREADY_SET; |
1317 | } | 1329 | } |
1318 | 1330 | ||
1319 | last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 1331 | last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
1320 | 1332 | ||
1321 | if (unlikely(!is_an_alpha2(pending_request->alpha2))) | 1333 | if (unlikely(!is_an_alpha2(pending_request->alpha2))) |
1322 | return -EINVAL; | 1334 | return -EINVAL; |
1323 | if (last_request->initiator == | 1335 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
1324 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
1325 | if (last_wiphy != wiphy) { | 1336 | if (last_wiphy != wiphy) { |
1326 | /* | 1337 | /* |
1327 | * Two cards with two APs claiming different | 1338 | * Two cards with two APs claiming different |
@@ -1343,7 +1354,7 @@ get_reg_request_treatment(struct wiphy *wiphy, | |||
1343 | } | 1354 | } |
1344 | return 0; | 1355 | return 0; |
1345 | case NL80211_REGDOM_SET_BY_DRIVER: | 1356 | case NL80211_REGDOM_SET_BY_DRIVER: |
1346 | if (last_request->initiator == NL80211_REGDOM_SET_BY_CORE) { | 1357 | if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) { |
1347 | if (regdom_changes(pending_request->alpha2)) | 1358 | if (regdom_changes(pending_request->alpha2)) |
1348 | return REG_REQ_OK; | 1359 | return REG_REQ_OK; |
1349 | return REG_REQ_ALREADY_SET; | 1360 | return REG_REQ_ALREADY_SET; |
@@ -1354,7 +1365,7 @@ get_reg_request_treatment(struct wiphy *wiphy, | |||
1354 | * back in or if you add a new device for which the previously | 1365 | * back in or if you add a new device for which the previously |
1355 | * loaded card also agrees on the regulatory domain. | 1366 | * loaded card also agrees on the regulatory domain. |
1356 | */ | 1367 | */ |
1357 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && | 1368 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER && |
1358 | !regdom_changes(pending_request->alpha2)) | 1369 | !regdom_changes(pending_request->alpha2)) |
1359 | return REG_REQ_ALREADY_SET; | 1370 | return REG_REQ_ALREADY_SET; |
1360 | 1371 | ||
@@ -1363,26 +1374,26 @@ get_reg_request_treatment(struct wiphy *wiphy, | |||
1363 | if (reg_request_cell_base(pending_request)) | 1374 | if (reg_request_cell_base(pending_request)) |
1364 | return reg_ignore_cell_hint(pending_request); | 1375 | return reg_ignore_cell_hint(pending_request); |
1365 | 1376 | ||
1366 | if (reg_request_cell_base(last_request)) | 1377 | if (reg_request_cell_base(lr)) |
1367 | return REG_REQ_IGNORE; | 1378 | return REG_REQ_IGNORE; |
1368 | 1379 | ||
1369 | if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) | 1380 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) |
1370 | return REG_REQ_INTERSECT; | 1381 | return REG_REQ_INTERSECT; |
1371 | /* | 1382 | /* |
1372 | * If the user knows better the user should set the regdom | 1383 | * If the user knows better the user should set the regdom |
1373 | * to their country before the IE is picked up | 1384 | * to their country before the IE is picked up |
1374 | */ | 1385 | */ |
1375 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER && | 1386 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER && |
1376 | last_request->intersect) | 1387 | lr->intersect) |
1377 | return REG_REQ_IGNORE; | 1388 | return REG_REQ_IGNORE; |
1378 | /* | 1389 | /* |
1379 | * Process user requests only after previous user/driver/core | 1390 | * Process user requests only after previous user/driver/core |
1380 | * requests have been processed | 1391 | * requests have been processed |
1381 | */ | 1392 | */ |
1382 | if ((last_request->initiator == NL80211_REGDOM_SET_BY_CORE || | 1393 | if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE || |
1383 | last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | 1394 | lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || |
1384 | last_request->initiator == NL80211_REGDOM_SET_BY_USER) && | 1395 | lr->initiator == NL80211_REGDOM_SET_BY_USER) && |
1385 | regdom_changes(last_request->alpha2)) | 1396 | regdom_changes(lr->alpha2)) |
1386 | return REG_REQ_IGNORE; | 1397 | return REG_REQ_IGNORE; |
1387 | 1398 | ||
1388 | if (!regdom_changes(pending_request->alpha2)) | 1399 | if (!regdom_changes(pending_request->alpha2)) |
@@ -1397,15 +1408,16 @@ get_reg_request_treatment(struct wiphy *wiphy, | |||
1397 | static void reg_set_request_processed(void) | 1408 | static void reg_set_request_processed(void) |
1398 | { | 1409 | { |
1399 | bool need_more_processing = false; | 1410 | bool need_more_processing = false; |
1411 | struct regulatory_request *lr = get_last_request(); | ||
1400 | 1412 | ||
1401 | last_request->processed = true; | 1413 | lr->processed = true; |
1402 | 1414 | ||
1403 | spin_lock(®_requests_lock); | 1415 | spin_lock(®_requests_lock); |
1404 | if (!list_empty(®_requests_list)) | 1416 | if (!list_empty(®_requests_list)) |
1405 | need_more_processing = true; | 1417 | need_more_processing = true; |
1406 | spin_unlock(®_requests_lock); | 1418 | spin_unlock(®_requests_lock); |
1407 | 1419 | ||
1408 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) | 1420 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) |
1409 | cancel_delayed_work(®_timeout); | 1421 | cancel_delayed_work(®_timeout); |
1410 | 1422 | ||
1411 | if (need_more_processing) | 1423 | if (need_more_processing) |
@@ -1432,6 +1444,7 @@ __regulatory_hint(struct wiphy *wiphy, | |||
1432 | const struct ieee80211_regdomain *regd; | 1444 | const struct ieee80211_regdomain *regd; |
1433 | bool intersect = false; | 1445 | bool intersect = false; |
1434 | enum reg_request_treatment treatment; | 1446 | enum reg_request_treatment treatment; |
1447 | struct regulatory_request *lr; | ||
1435 | 1448 | ||
1436 | treatment = get_reg_request_treatment(wiphy, pending_request); | 1449 | treatment = get_reg_request_treatment(wiphy, pending_request); |
1437 | 1450 | ||
@@ -1472,18 +1485,20 @@ __regulatory_hint(struct wiphy *wiphy, | |||
1472 | } | 1485 | } |
1473 | 1486 | ||
1474 | new_request: | 1487 | new_request: |
1475 | if (last_request != &core_request_world) | 1488 | lr = get_last_request(); |
1476 | kfree(last_request); | 1489 | if (lr != &core_request_world && lr) |
1490 | kfree_rcu(lr, rcu_head); | ||
1477 | 1491 | ||
1478 | last_request = pending_request; | 1492 | pending_request->intersect = intersect; |
1479 | last_request->intersect = intersect; | 1493 | pending_request->processed = false; |
1480 | last_request->processed = false; | 1494 | rcu_assign_pointer(last_request, pending_request); |
1495 | lr = pending_request; | ||
1481 | 1496 | ||
1482 | pending_request = NULL; | 1497 | pending_request = NULL; |
1483 | 1498 | ||
1484 | if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) { | 1499 | if (lr->initiator == NL80211_REGDOM_SET_BY_USER) { |
1485 | user_alpha2[0] = last_request->alpha2[0]; | 1500 | user_alpha2[0] = lr->alpha2[0]; |
1486 | user_alpha2[1] = last_request->alpha2[1]; | 1501 | user_alpha2[1] = lr->alpha2[1]; |
1487 | } | 1502 | } |
1488 | 1503 | ||
1489 | /* When r == REG_REQ_INTERSECT we do need to call CRDA */ | 1504 | /* When r == REG_REQ_INTERSECT we do need to call CRDA */ |
@@ -1494,13 +1509,13 @@ new_request: | |||
1494 | * inform userspace we have processed the request | 1509 | * inform userspace we have processed the request |
1495 | */ | 1510 | */ |
1496 | if (treatment == REG_REQ_ALREADY_SET) { | 1511 | if (treatment == REG_REQ_ALREADY_SET) { |
1497 | nl80211_send_reg_change_event(last_request); | 1512 | nl80211_send_reg_change_event(lr); |
1498 | reg_set_request_processed(); | 1513 | reg_set_request_processed(); |
1499 | } | 1514 | } |
1500 | return treatment; | 1515 | return treatment; |
1501 | } | 1516 | } |
1502 | 1517 | ||
1503 | if (call_crda(last_request->alpha2)) | 1518 | if (call_crda(lr->alpha2)) |
1504 | return REG_REQ_IGNORE; | 1519 | return REG_REQ_IGNORE; |
1505 | return REG_REQ_OK; | 1520 | return REG_REQ_OK; |
1506 | } | 1521 | } |
@@ -1543,13 +1558,14 @@ static void reg_process_hint(struct regulatory_request *reg_request, | |||
1543 | */ | 1558 | */ |
1544 | static void reg_process_pending_hints(void) | 1559 | static void reg_process_pending_hints(void) |
1545 | { | 1560 | { |
1546 | struct regulatory_request *reg_request; | 1561 | struct regulatory_request *reg_request, *lr; |
1547 | 1562 | ||
1548 | mutex_lock(&cfg80211_mutex); | 1563 | mutex_lock(&cfg80211_mutex); |
1549 | mutex_lock(®_mutex); | 1564 | mutex_lock(®_mutex); |
1565 | lr = get_last_request(); | ||
1550 | 1566 | ||
1551 | /* When last_request->processed becomes true this will be rescheduled */ | 1567 | /* When last_request->processed becomes true this will be rescheduled */ |
1552 | if (last_request && !last_request->processed) { | 1568 | if (lr && !lr->processed) { |
1553 | REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); | 1569 | REG_DBG_PRINT("Pending regulatory request, waiting for it to be processed...\n"); |
1554 | goto out; | 1570 | goto out; |
1555 | } | 1571 | } |
@@ -1702,11 +1718,12 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, | |||
1702 | { | 1718 | { |
1703 | char alpha2[2]; | 1719 | char alpha2[2]; |
1704 | enum environment_cap env = ENVIRON_ANY; | 1720 | enum environment_cap env = ENVIRON_ANY; |
1705 | struct regulatory_request *request; | 1721 | struct regulatory_request *request, *lr; |
1706 | 1722 | ||
1707 | mutex_lock(®_mutex); | 1723 | mutex_lock(®_mutex); |
1724 | lr = get_last_request(); | ||
1708 | 1725 | ||
1709 | if (unlikely(!last_request)) | 1726 | if (unlikely(!lr)) |
1710 | goto out; | 1727 | goto out; |
1711 | 1728 | ||
1712 | /* IE len must be evenly divisible by 2 */ | 1729 | /* IE len must be evenly divisible by 2 */ |
@@ -1729,8 +1746,8 @@ void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, | |||
1729 | * We leave conflict resolution to the workqueue, where can hold | 1746 | * We leave conflict resolution to the workqueue, where can hold |
1730 | * cfg80211_mutex. | 1747 | * cfg80211_mutex. |
1731 | */ | 1748 | */ |
1732 | if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && | 1749 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1733 | last_request->wiphy_idx != WIPHY_IDX_INVALID) | 1750 | lr->wiphy_idx != WIPHY_IDX_INVALID) |
1734 | goto out; | 1751 | goto out; |
1735 | 1752 | ||
1736 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); | 1753 | request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL); |
@@ -2021,13 +2038,12 @@ static void print_dfs_region(u8 dfs_region) | |||
2021 | 2038 | ||
2022 | static void print_regdomain(const struct ieee80211_regdomain *rd) | 2039 | static void print_regdomain(const struct ieee80211_regdomain *rd) |
2023 | { | 2040 | { |
2041 | struct regulatory_request *lr = get_last_request(); | ||
2024 | 2042 | ||
2025 | if (is_intersected_alpha2(rd->alpha2)) { | 2043 | if (is_intersected_alpha2(rd->alpha2)) { |
2026 | if (last_request->initiator == | 2044 | if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2027 | NL80211_REGDOM_SET_BY_COUNTRY_IE) { | ||
2028 | struct cfg80211_registered_device *rdev; | 2045 | struct cfg80211_registered_device *rdev; |
2029 | rdev = cfg80211_rdev_by_wiphy_idx( | 2046 | rdev = cfg80211_rdev_by_wiphy_idx(lr->wiphy_idx); |
2030 | last_request->wiphy_idx); | ||
2031 | if (rdev) { | 2047 | if (rdev) { |
2032 | pr_info("Current regulatory domain updated by AP to: %c%c\n", | 2048 | pr_info("Current regulatory domain updated by AP to: %c%c\n", |
2033 | rdev->country_ie_alpha2[0], | 2049 | rdev->country_ie_alpha2[0], |
@@ -2042,7 +2058,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
2042 | if (is_unknown_alpha2(rd->alpha2)) | 2058 | if (is_unknown_alpha2(rd->alpha2)) |
2043 | pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); | 2059 | pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); |
2044 | else { | 2060 | else { |
2045 | if (reg_request_cell_base(last_request)) | 2061 | if (reg_request_cell_base(lr)) |
2046 | pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", | 2062 | pr_info("Regulatory domain changed to country: %c%c by Cell Station\n", |
2047 | rd->alpha2[0], rd->alpha2[1]); | 2063 | rd->alpha2[0], rd->alpha2[1]); |
2048 | else | 2064 | else |
@@ -2067,11 +2083,10 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2067 | const struct ieee80211_regdomain *regd; | 2083 | const struct ieee80211_regdomain *regd; |
2068 | const struct ieee80211_regdomain *intersected_rd = NULL; | 2084 | const struct ieee80211_regdomain *intersected_rd = NULL; |
2069 | struct wiphy *request_wiphy; | 2085 | struct wiphy *request_wiphy; |
2086 | struct regulatory_request *lr = get_last_request(); | ||
2070 | 2087 | ||
2071 | /* Some basic sanity checks first */ | 2088 | /* Some basic sanity checks first */ |
2072 | 2089 | ||
2073 | assert_reg_lock(); | ||
2074 | |||
2075 | if (!reg_is_valid_request(rd->alpha2)) | 2090 | if (!reg_is_valid_request(rd->alpha2)) |
2076 | return -EINVAL; | 2091 | return -EINVAL; |
2077 | 2092 | ||
@@ -2089,7 +2104,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2089 | * rd is non static (it means CRDA was present and was used last) | 2104 | * rd is non static (it means CRDA was present and was used last) |
2090 | * and the pending request came in from a country IE | 2105 | * and the pending request came in from a country IE |
2091 | */ | 2106 | */ |
2092 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2107 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2093 | /* | 2108 | /* |
2094 | * If someone else asked us to change the rd lets only bother | 2109 | * If someone else asked us to change the rd lets only bother |
2095 | * checking if the alpha2 changes if CRDA was already called | 2110 | * checking if the alpha2 changes if CRDA was already called |
@@ -2111,16 +2126,16 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2111 | return -EINVAL; | 2126 | return -EINVAL; |
2112 | } | 2127 | } |
2113 | 2128 | ||
2114 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2129 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
2115 | if (!request_wiphy && | 2130 | if (!request_wiphy && |
2116 | (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER || | 2131 | (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER || |
2117 | last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { | 2132 | lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) { |
2118 | schedule_delayed_work(®_timeout, 0); | 2133 | schedule_delayed_work(®_timeout, 0); |
2119 | return -ENODEV; | 2134 | return -ENODEV; |
2120 | } | 2135 | } |
2121 | 2136 | ||
2122 | if (!last_request->intersect) { | 2137 | if (!lr->intersect) { |
2123 | if (last_request->initiator != NL80211_REGDOM_SET_BY_DRIVER) { | 2138 | if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) { |
2124 | reset_regdomains(false, rd); | 2139 | reset_regdomains(false, rd); |
2125 | return 0; | 2140 | return 0; |
2126 | } | 2141 | } |
@@ -2148,7 +2163,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2148 | 2163 | ||
2149 | /* Intersection requires a bit more work */ | 2164 | /* Intersection requires a bit more work */ |
2150 | 2165 | ||
2151 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2166 | if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2152 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); | 2167 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); |
2153 | if (!intersected_rd) | 2168 | if (!intersected_rd) |
2154 | return -EINVAL; | 2169 | return -EINVAL; |
@@ -2158,7 +2173,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2158 | * However if a driver requested this specific regulatory | 2173 | * However if a driver requested this specific regulatory |
2159 | * domain we keep it for its private use | 2174 | * domain we keep it for its private use |
2160 | */ | 2175 | */ |
2161 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) | 2176 | if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) |
2162 | rcu_assign_pointer(request_wiphy->regd, rd); | 2177 | rcu_assign_pointer(request_wiphy->regd, rd); |
2163 | else | 2178 | else |
2164 | kfree(rd); | 2179 | kfree(rd); |
@@ -2181,9 +2196,11 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2181 | */ | 2196 | */ |
2182 | int set_regdom(const struct ieee80211_regdomain *rd) | 2197 | int set_regdom(const struct ieee80211_regdomain *rd) |
2183 | { | 2198 | { |
2199 | struct regulatory_request *lr; | ||
2184 | int r; | 2200 | int r; |
2185 | 2201 | ||
2186 | mutex_lock(®_mutex); | 2202 | mutex_lock(®_mutex); |
2203 | lr = get_last_request(); | ||
2187 | 2204 | ||
2188 | /* Note that this doesn't update the wiphys, this is done below */ | 2205 | /* Note that this doesn't update the wiphys, this is done below */ |
2189 | r = __set_regdom(rd); | 2206 | r = __set_regdom(rd); |
@@ -2196,18 +2213,17 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2196 | } | 2213 | } |
2197 | 2214 | ||
2198 | /* This would make this whole thing pointless */ | 2215 | /* This would make this whole thing pointless */ |
2199 | if (WARN_ON(!last_request->intersect && | 2216 | if (WARN_ON(!lr->intersect && rd != get_cfg80211_regdom())) { |
2200 | rd != get_cfg80211_regdom())) { | ||
2201 | r = -EINVAL; | 2217 | r = -EINVAL; |
2202 | goto out; | 2218 | goto out; |
2203 | } | 2219 | } |
2204 | 2220 | ||
2205 | /* update all wiphys now with the new established regulatory domain */ | 2221 | /* update all wiphys now with the new established regulatory domain */ |
2206 | update_all_wiphy_regulatory(last_request->initiator); | 2222 | update_all_wiphy_regulatory(lr->initiator); |
2207 | 2223 | ||
2208 | print_regdomain(get_cfg80211_regdom()); | 2224 | print_regdomain(get_cfg80211_regdom()); |
2209 | 2225 | ||
2210 | nl80211_send_reg_change_event(last_request); | 2226 | nl80211_send_reg_change_event(lr); |
2211 | 2227 | ||
2212 | reg_set_request_processed(); | 2228 | reg_set_request_processed(); |
2213 | 2229 | ||
@@ -2220,10 +2236,11 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2220 | #ifdef CONFIG_HOTPLUG | 2236 | #ifdef CONFIG_HOTPLUG |
2221 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) | 2237 | int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env) |
2222 | { | 2238 | { |
2223 | if (last_request && !last_request->processed) { | 2239 | struct regulatory_request *lr = get_last_request(); |
2240 | |||
2241 | if (lr && !lr->processed) { | ||
2224 | if (add_uevent_var(env, "COUNTRY=%c%c", | 2242 | if (add_uevent_var(env, "COUNTRY=%c%c", |
2225 | last_request->alpha2[0], | 2243 | lr->alpha2[0], lr->alpha2[1])) |
2226 | last_request->alpha2[1])) | ||
2227 | return -ENOMEM; | 2244 | return -ENOMEM; |
2228 | } | 2245 | } |
2229 | 2246 | ||
@@ -2252,8 +2269,10 @@ void wiphy_regulatory_register(struct wiphy *wiphy) | |||
2252 | void wiphy_regulatory_deregister(struct wiphy *wiphy) | 2269 | void wiphy_regulatory_deregister(struct wiphy *wiphy) |
2253 | { | 2270 | { |
2254 | struct wiphy *request_wiphy = NULL; | 2271 | struct wiphy *request_wiphy = NULL; |
2272 | struct regulatory_request *lr; | ||
2255 | 2273 | ||
2256 | mutex_lock(®_mutex); | 2274 | mutex_lock(®_mutex); |
2275 | lr = get_last_request(); | ||
2257 | 2276 | ||
2258 | if (!reg_dev_ignore_cell_hint(wiphy)) | 2277 | if (!reg_dev_ignore_cell_hint(wiphy)) |
2259 | reg_num_devs_support_basehint--; | 2278 | reg_num_devs_support_basehint--; |
@@ -2261,14 +2280,14 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) | |||
2261 | rcu_free_regdom(get_wiphy_regdom(wiphy)); | 2280 | rcu_free_regdom(get_wiphy_regdom(wiphy)); |
2262 | rcu_assign_pointer(wiphy->regd, NULL); | 2281 | rcu_assign_pointer(wiphy->regd, NULL); |
2263 | 2282 | ||
2264 | if (last_request) | 2283 | if (lr) |
2265 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2284 | request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx); |
2266 | 2285 | ||
2267 | if (!request_wiphy || request_wiphy != wiphy) | 2286 | if (!request_wiphy || request_wiphy != wiphy) |
2268 | goto out; | 2287 | goto out; |
2269 | 2288 | ||
2270 | last_request->wiphy_idx = WIPHY_IDX_INVALID; | 2289 | lr->wiphy_idx = WIPHY_IDX_INVALID; |
2271 | last_request->country_ie_env = ENVIRON_ANY; | 2290 | lr->country_ie_env = ENVIRON_ANY; |
2272 | out: | 2291 | out: |
2273 | mutex_unlock(®_mutex); | 2292 | mutex_unlock(®_mutex); |
2274 | } | 2293 | } |