summaryrefslogtreecommitdiffstats
path: root/net/wireless
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2019-02-05 15:08:29 -0500
committerJohannes Berg <johannes.berg@intel.com>2019-02-11 09:46:29 -0500
commite646a0257b64dbef9d168e0f90daafa3fc1a83af (patch)
tree305a86576873ac7e682a2f2639c760731be6026c /net/wireless
parent61edb116cab9bf7d623e31bf7455a82bc042c087 (diff)
cfg80211: restore regulatory without calling userspace
Jouni reports that in some cases it is possible that getting disconnected (or stopping AP, after previous patches) results in further operations hitting the window within the regulatory core restoring the regdomain to the defaults. The reason for this is that we have to call out to CRDA or otherwise do some asynchronous work, and thus can't do the restore atomically. However, we've previously seen all the data we need to do the restore, so we can hang on to that data and use it later for the restore. This makes the whole thing happen within a single locked section and thus atomic. However, we can't *always* do this - there are unfortunately cases where the restore needs to re-request, because this is also used (abused?) as an error recovery process, so make the new behaviour optional and only use it when doing a regular restore as described above. Reported-by: Jouni Malinen <j@w1.fi> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r--net/wireless/reg.c73
-rw-r--r--net/wireless/reg.h2
2 files changed, 60 insertions, 15 deletions
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index adfa58fa6536..2f1bf91eb226 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -5,7 +5,7 @@
5 * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> 5 * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
6 * Copyright 2013-2014 Intel Mobile Communications GmbH 6 * Copyright 2013-2014 Intel Mobile Communications GmbH
7 * Copyright 2017 Intel Deutschland GmbH 7 * Copyright 2017 Intel Deutschland GmbH
8 * Copyright (C) 2018 Intel Corporation 8 * Copyright (C) 2018 - 2019 Intel Corporation
9 * 9 *
10 * Permission to use, copy, modify, and/or distribute this software for any 10 * Permission to use, copy, modify, and/or distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above 11 * purpose with or without fee is hereby granted, provided that the above
@@ -131,7 +131,8 @@ static spinlock_t reg_indoor_lock;
131/* Used to track the userspace process controlling the indoor setting */ 131/* Used to track the userspace process controlling the indoor setting */
132static u32 reg_is_indoor_portid; 132static u32 reg_is_indoor_portid;
133 133
134static void restore_regulatory_settings(bool reset_user); 134static void restore_regulatory_settings(bool reset_user, bool cached);
135static void print_regdomain(const struct ieee80211_regdomain *rd);
135 136
136static const struct ieee80211_regdomain *get_cfg80211_regdom(void) 137static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
137{ 138{
@@ -263,6 +264,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
263 264
264static char *ieee80211_regdom = "00"; 265static char *ieee80211_regdom = "00";
265static char user_alpha2[2]; 266static char user_alpha2[2];
267static const struct ieee80211_regdomain *cfg80211_user_regdom;
266 268
267module_param(ieee80211_regdom, charp, 0444); 269module_param(ieee80211_regdom, charp, 0444);
268MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); 270MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@@ -445,6 +447,15 @@ reg_copy_regd(const struct ieee80211_regdomain *src_regd)
445 return regd; 447 return regd;
446} 448}
447 449
450static void cfg80211_save_user_regdom(const struct ieee80211_regdomain *rd)
451{
452 ASSERT_RTNL();
453
454 if (!IS_ERR(cfg80211_user_regdom))
455 kfree(cfg80211_user_regdom);
456 cfg80211_user_regdom = reg_copy_regd(rd);
457}
458
448struct reg_regdb_apply_request { 459struct reg_regdb_apply_request {
449 struct list_head list; 460 struct list_head list;
450 const struct ieee80211_regdomain *regdom; 461 const struct ieee80211_regdomain *regdom;
@@ -510,7 +521,7 @@ static void crda_timeout_work(struct work_struct *work)
510 pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n"); 521 pr_debug("Timeout while waiting for CRDA to reply, restoring regulatory settings\n");
511 rtnl_lock(); 522 rtnl_lock();
512 reg_crda_timeouts++; 523 reg_crda_timeouts++;
513 restore_regulatory_settings(true); 524 restore_regulatory_settings(true, false);
514 rtnl_unlock(); 525 rtnl_unlock();
515} 526}
516 527
@@ -1044,7 +1055,7 @@ static void regdb_fw_cb(const struct firmware *fw, void *context)
1044 } 1055 }
1045 1056
1046 if (restore) 1057 if (restore)
1047 restore_regulatory_settings(true); 1058 restore_regulatory_settings(true, false);
1048 1059
1049 rtnl_unlock(); 1060 rtnl_unlock();
1050 1061
@@ -3117,7 +3128,7 @@ static void restore_custom_reg_settings(struct wiphy *wiphy)
3117 * keep their own regulatory domain on wiphy->regd so that does does 3128 * keep their own regulatory domain on wiphy->regd so that does does
3118 * not need to be remembered. 3129 * not need to be remembered.
3119 */ 3130 */
3120static void restore_regulatory_settings(bool reset_user) 3131static void restore_regulatory_settings(bool reset_user, bool cached)
3121{ 3132{
3122 char alpha2[2]; 3133 char alpha2[2];
3123 char world_alpha2[2]; 3134 char world_alpha2[2];
@@ -3176,15 +3187,41 @@ static void restore_regulatory_settings(bool reset_user)
3176 restore_custom_reg_settings(&rdev->wiphy); 3187 restore_custom_reg_settings(&rdev->wiphy);
3177 } 3188 }
3178 3189
3179 regulatory_hint_core(world_alpha2); 3190 if (cached && (!is_an_alpha2(alpha2) ||
3191 !IS_ERR_OR_NULL(cfg80211_user_regdom))) {
3192 reset_regdomains(false, cfg80211_world_regdom);
3193 update_all_wiphy_regulatory(NL80211_REGDOM_SET_BY_CORE);
3194 print_regdomain(get_cfg80211_regdom());
3195 nl80211_send_reg_change_event(&core_request_world);
3196 reg_set_request_processed();
3180 3197
3181 /* 3198 if (is_an_alpha2(alpha2) &&
3182 * This restores the ieee80211_regdom module parameter 3199 !regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER)) {
3183 * preference or the last user requested regulatory 3200 struct regulatory_request *ureq;
3184 * settings, user regulatory settings takes precedence. 3201
3185 */ 3202 spin_lock(&reg_requests_lock);
3186 if (is_an_alpha2(alpha2)) 3203 ureq = list_last_entry(&reg_requests_list,
3187 regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER); 3204 struct regulatory_request,
3205 list);
3206 list_del(&ureq->list);
3207 spin_unlock(&reg_requests_lock);
3208
3209 notify_self_managed_wiphys(ureq);
3210 reg_update_last_request(ureq);
3211 set_regdom(reg_copy_regd(cfg80211_user_regdom),
3212 REGD_SOURCE_CACHED);
3213 }
3214 } else {
3215 regulatory_hint_core(world_alpha2);
3216
3217 /*
3218 * This restores the ieee80211_regdom module parameter
3219 * preference or the last user requested regulatory
3220 * settings, user regulatory settings takes precedence.
3221 */
3222 if (is_an_alpha2(alpha2))
3223 regulatory_hint_user(alpha2, NL80211_USER_REG_HINT_USER);
3224 }
3188 3225
3189 spin_lock(&reg_requests_lock); 3226 spin_lock(&reg_requests_lock);
3190 list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list); 3227 list_splice_tail_init(&tmp_reg_req_list, &reg_requests_list);
@@ -3244,7 +3281,7 @@ void regulatory_hint_disconnect(void)
3244 } 3281 }
3245 3282
3246 pr_debug("All devices are disconnected, going to restore regulatory settings\n"); 3283 pr_debug("All devices are disconnected, going to restore regulatory settings\n");
3247 restore_regulatory_settings(false); 3284 restore_regulatory_settings(false, true);
3248} 3285}
3249 3286
3250static bool freq_is_chan_12_13_14(u32 freq) 3287static bool freq_is_chan_12_13_14(u32 freq)
@@ -3561,6 +3598,9 @@ int set_regdom(const struct ieee80211_regdomain *rd,
3561 bool user_reset = false; 3598 bool user_reset = false;
3562 int r; 3599 int r;
3563 3600
3601 if (IS_ERR_OR_NULL(rd))
3602 return -ENODATA;
3603
3564 if (!reg_is_valid_request(rd->alpha2)) { 3604 if (!reg_is_valid_request(rd->alpha2)) {
3565 kfree(rd); 3605 kfree(rd);
3566 return -EINVAL; 3606 return -EINVAL;
@@ -3577,6 +3617,7 @@ int set_regdom(const struct ieee80211_regdomain *rd,
3577 r = reg_set_rd_core(rd); 3617 r = reg_set_rd_core(rd);
3578 break; 3618 break;
3579 case NL80211_REGDOM_SET_BY_USER: 3619 case NL80211_REGDOM_SET_BY_USER:
3620 cfg80211_save_user_regdom(rd);
3580 r = reg_set_rd_user(rd, lr); 3621 r = reg_set_rd_user(rd, lr);
3581 user_reset = true; 3622 user_reset = true;
3582 break; 3623 break;
@@ -3599,7 +3640,7 @@ int set_regdom(const struct ieee80211_regdomain *rd,
3599 break; 3640 break;
3600 default: 3641 default:
3601 /* Back to world regulatory in case of errors */ 3642 /* Back to world regulatory in case of errors */
3602 restore_regulatory_settings(user_reset); 3643 restore_regulatory_settings(user_reset, false);
3603 } 3644 }
3604 3645
3605 kfree(rd); 3646 kfree(rd);
@@ -3935,6 +3976,8 @@ void regulatory_exit(void)
3935 3976
3936 if (!IS_ERR_OR_NULL(regdb)) 3977 if (!IS_ERR_OR_NULL(regdb))
3937 kfree(regdb); 3978 kfree(regdb);
3979 if (!IS_ERR_OR_NULL(cfg80211_user_regdom))
3980 kfree(cfg80211_user_regdom);
3938 3981
3939 free_regdb_keyring(); 3982 free_regdb_keyring();
3940} 3983}
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 9ceeb5f3a7cb..504133d76de4 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -5,6 +5,7 @@
5 5
6/* 6/*
7 * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com> 7 * Copyright 2008-2011 Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
8 * Copyright (C) 2019 Intel Corporation
8 * 9 *
9 * Permission to use, copy, modify, and/or distribute this software for any 10 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above 11 * purpose with or without fee is hereby granted, provided that the above
@@ -22,6 +23,7 @@
22enum ieee80211_regd_source { 23enum ieee80211_regd_source {
23 REGD_SOURCE_INTERNAL_DB, 24 REGD_SOURCE_INTERNAL_DB,
24 REGD_SOURCE_CRDA, 25 REGD_SOURCE_CRDA,
26 REGD_SOURCE_CACHED,
25}; 27};
26 28
27extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; 29extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;