diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-12-06 09:47:38 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-01-03 07:01:29 -0500 |
commit | 458f4f9e960b9a3b674c4b87d996eef186b1fe83 (patch) | |
tree | febf655badc2a045b3aaaf63271226729c76e325 | |
parent | 379b82f4c9dc6e67bf61aa61b096c06a2f320f60 (diff) |
regulatory: use RCU to protect global and wiphy regdomains
To simplify the locking and not require cfg80211_mutex
(which nl80211 uses to access the global regdomain) and
also to make it possible for drivers to access their
wiphy->regd safely, use RCU to protect these pointers.
Acked-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | include/net/cfg80211.h | 2 | ||||
-rw-r--r-- | include/net/regulatory.h | 2 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 35 | ||||
-rw-r--r-- | net/wireless/reg.c | 113 | ||||
-rw-r--r-- | net/wireless/reg.h | 2 |
5 files changed, 88 insertions, 66 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c222e5fbf53a..f3be58a29642 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -2369,7 +2369,7 @@ struct wiphy { | |||
2369 | 2369 | ||
2370 | /* fields below are read-only, assigned by cfg80211 */ | 2370 | /* fields below are read-only, assigned by cfg80211 */ |
2371 | 2371 | ||
2372 | const struct ieee80211_regdomain *regd; | 2372 | const struct ieee80211_regdomain __rcu *regd; |
2373 | 2373 | ||
2374 | /* the item in /sys/class/ieee80211/ points to this, | 2374 | /* the item in /sys/class/ieee80211/ points to this, |
2375 | * you need use set_wiphy_dev() (see below) */ | 2375 | * you need use set_wiphy_dev() (see below) */ |
diff --git a/include/net/regulatory.h b/include/net/regulatory.h index 7dcaa2794fde..96b0f07cb85b 100644 --- a/include/net/regulatory.h +++ b/include/net/regulatory.h | |||
@@ -18,6 +18,7 @@ | |||
18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <linux/rcupdate.h> | ||
21 | 22 | ||
22 | /** | 23 | /** |
23 | * enum environment_cap - Environment parsed from country IE | 24 | * enum environment_cap - Environment parsed from country IE |
@@ -101,6 +102,7 @@ struct ieee80211_reg_rule { | |||
101 | }; | 102 | }; |
102 | 103 | ||
103 | struct ieee80211_regdomain { | 104 | struct ieee80211_regdomain { |
105 | struct rcu_head rcu_head; | ||
104 | u32 n_reg_rules; | 106 | u32 n_reg_rules; |
105 | char alpha2[2]; | 107 | char alpha2[2]; |
106 | u8 dfs_region; | 108 | u8 dfs_region; |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index b387deaf1132..b3cf7cc0d4a1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -3787,12 +3787,8 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
3787 | * window between nl80211_init() and regulatory_init(), if that is | 3787 | * window between nl80211_init() and regulatory_init(), if that is |
3788 | * even possible. | 3788 | * even possible. |
3789 | */ | 3789 | */ |
3790 | mutex_lock(&cfg80211_mutex); | 3790 | if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) |
3791 | if (unlikely(!cfg80211_regdomain)) { | ||
3792 | mutex_unlock(&cfg80211_mutex); | ||
3793 | return -EINPROGRESS; | 3791 | return -EINPROGRESS; |
3794 | } | ||
3795 | mutex_unlock(&cfg80211_mutex); | ||
3796 | 3792 | ||
3797 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | 3793 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) |
3798 | return -EINVAL; | 3794 | return -EINVAL; |
@@ -4152,6 +4148,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb, | |||
4152 | 4148 | ||
4153 | static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | 4149 | static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) |
4154 | { | 4150 | { |
4151 | const struct ieee80211_regdomain *regdom; | ||
4155 | struct sk_buff *msg; | 4152 | struct sk_buff *msg; |
4156 | void *hdr = NULL; | 4153 | void *hdr = NULL; |
4157 | struct nlattr *nl_reg_rules; | 4154 | struct nlattr *nl_reg_rules; |
@@ -4174,35 +4171,36 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
4174 | if (!hdr) | 4171 | if (!hdr) |
4175 | goto put_failure; | 4172 | goto put_failure; |
4176 | 4173 | ||
4177 | if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, | ||
4178 | cfg80211_regdomain->alpha2) || | ||
4179 | (cfg80211_regdomain->dfs_region && | ||
4180 | nla_put_u8(msg, NL80211_ATTR_DFS_REGION, | ||
4181 | cfg80211_regdomain->dfs_region))) | ||
4182 | goto nla_put_failure; | ||
4183 | |||
4184 | if (reg_last_request_cell_base() && | 4174 | if (reg_last_request_cell_base() && |
4185 | nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, | 4175 | nla_put_u32(msg, NL80211_ATTR_USER_REG_HINT_TYPE, |
4186 | NL80211_USER_REG_HINT_CELL_BASE)) | 4176 | NL80211_USER_REG_HINT_CELL_BASE)) |
4187 | goto nla_put_failure; | 4177 | goto nla_put_failure; |
4188 | 4178 | ||
4179 | rcu_read_lock(); | ||
4180 | regdom = rcu_dereference(cfg80211_regdomain); | ||
4181 | |||
4182 | if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, regdom->alpha2) || | ||
4183 | (regdom->dfs_region && | ||
4184 | nla_put_u8(msg, NL80211_ATTR_DFS_REGION, regdom->dfs_region))) | ||
4185 | goto nla_put_failure_rcu; | ||
4186 | |||
4189 | nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); | 4187 | nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES); |
4190 | if (!nl_reg_rules) | 4188 | if (!nl_reg_rules) |
4191 | goto nla_put_failure; | 4189 | goto nla_put_failure_rcu; |
4192 | 4190 | ||
4193 | for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { | 4191 | for (i = 0; i < regdom->n_reg_rules; i++) { |
4194 | struct nlattr *nl_reg_rule; | 4192 | struct nlattr *nl_reg_rule; |
4195 | const struct ieee80211_reg_rule *reg_rule; | 4193 | const struct ieee80211_reg_rule *reg_rule; |
4196 | const struct ieee80211_freq_range *freq_range; | 4194 | const struct ieee80211_freq_range *freq_range; |
4197 | const struct ieee80211_power_rule *power_rule; | 4195 | const struct ieee80211_power_rule *power_rule; |
4198 | 4196 | ||
4199 | reg_rule = &cfg80211_regdomain->reg_rules[i]; | 4197 | reg_rule = ®dom->reg_rules[i]; |
4200 | freq_range = ®_rule->freq_range; | 4198 | freq_range = ®_rule->freq_range; |
4201 | power_rule = ®_rule->power_rule; | 4199 | power_rule = ®_rule->power_rule; |
4202 | 4200 | ||
4203 | nl_reg_rule = nla_nest_start(msg, i); | 4201 | nl_reg_rule = nla_nest_start(msg, i); |
4204 | if (!nl_reg_rule) | 4202 | if (!nl_reg_rule) |
4205 | goto nla_put_failure; | 4203 | goto nla_put_failure_rcu; |
4206 | 4204 | ||
4207 | if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, | 4205 | if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS, |
4208 | reg_rule->flags) || | 4206 | reg_rule->flags) || |
@@ -4216,10 +4214,11 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
4216 | power_rule->max_antenna_gain) || | 4214 | power_rule->max_antenna_gain) || |
4217 | nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, | 4215 | nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP, |
4218 | power_rule->max_eirp)) | 4216 | power_rule->max_eirp)) |
4219 | goto nla_put_failure; | 4217 | goto nla_put_failure_rcu; |
4220 | 4218 | ||
4221 | nla_nest_end(msg, nl_reg_rule); | 4219 | nla_nest_end(msg, nl_reg_rule); |
4222 | } | 4220 | } |
4221 | rcu_read_unlock(); | ||
4223 | 4222 | ||
4224 | nla_nest_end(msg, nl_reg_rules); | 4223 | nla_nest_end(msg, nl_reg_rules); |
4225 | 4224 | ||
@@ -4227,6 +4226,8 @@ static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) | |||
4227 | err = genlmsg_reply(msg, info); | 4226 | err = genlmsg_reply(msg, info); |
4228 | goto out; | 4227 | goto out; |
4229 | 4228 | ||
4229 | nla_put_failure_rcu: | ||
4230 | rcu_read_unlock(); | ||
4230 | nla_put_failure: | 4231 | nla_put_failure: |
4231 | genlmsg_cancel(msg, hdr); | 4232 | genlmsg_cancel(msg, hdr); |
4232 | put_failure: | 4233 | put_failure: |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 35541d6d4145..9b64b201cdf1 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -95,15 +95,15 @@ static struct device_type reg_device_type = { | |||
95 | * Central wireless core regulatory domains, we only need two, | 95 | * Central wireless core regulatory domains, we only need two, |
96 | * the current one and a world regulatory domain in case we have no | 96 | * the current one and a world regulatory domain in case we have no |
97 | * information to give us an alpha2. | 97 | * information to give us an alpha2. |
98 | * Protected by the cfg80211_mutex. | ||
99 | */ | 98 | */ |
100 | const struct ieee80211_regdomain *cfg80211_regdomain; | 99 | const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
101 | 100 | ||
102 | /* | 101 | /* |
103 | * Protects static reg.c components: | 102 | * Protects static reg.c components: |
104 | * - cfg80211_world_regdom | 103 | * - cfg80211_regdomain (if not used with RCU) |
105 | * - last_request | 104 | * - cfg80211_world_regdom |
106 | * - reg_num_devs_support_basehint | 105 | * - last_request |
106 | * - reg_num_devs_support_basehint | ||
107 | */ | 107 | */ |
108 | static DEFINE_MUTEX(reg_mutex); | 108 | static DEFINE_MUTEX(reg_mutex); |
109 | 109 | ||
@@ -118,6 +118,25 @@ static inline void assert_reg_lock(void) | |||
118 | lockdep_assert_held(®_mutex); | 118 | lockdep_assert_held(®_mutex); |
119 | } | 119 | } |
120 | 120 | ||
121 | static const struct ieee80211_regdomain *get_cfg80211_regdom(void) | ||
122 | { | ||
123 | return rcu_dereference_protected(cfg80211_regdomain, | ||
124 | lockdep_is_held(®_mutex)); | ||
125 | } | ||
126 | |||
127 | static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy) | ||
128 | { | ||
129 | return rcu_dereference_protected(wiphy->regd, | ||
130 | lockdep_is_held(®_mutex)); | ||
131 | } | ||
132 | |||
133 | static void rcu_free_regdom(const struct ieee80211_regdomain *r) | ||
134 | { | ||
135 | if (!r) | ||
136 | return; | ||
137 | kfree_rcu((struct ieee80211_regdomain *)r, rcu_head); | ||
138 | } | ||
139 | |||
121 | /* Used to queue up regulatory hints */ | 140 | /* Used to queue up regulatory hints */ |
122 | static LIST_HEAD(reg_requests_list); | 141 | static LIST_HEAD(reg_requests_list); |
123 | static spinlock_t reg_requests_lock; | 142 | static spinlock_t reg_requests_lock; |
@@ -186,22 +205,25 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code"); | |||
186 | static void reset_regdomains(bool full_reset, | 205 | static void reset_regdomains(bool full_reset, |
187 | const struct ieee80211_regdomain *new_regdom) | 206 | const struct ieee80211_regdomain *new_regdom) |
188 | { | 207 | { |
189 | assert_cfg80211_lock(); | 208 | const struct ieee80211_regdomain *r; |
209 | |||
190 | assert_reg_lock(); | 210 | assert_reg_lock(); |
191 | 211 | ||
212 | r = get_cfg80211_regdom(); | ||
213 | |||
192 | /* avoid freeing static information or freeing something twice */ | 214 | /* avoid freeing static information or freeing something twice */ |
193 | if (cfg80211_regdomain == cfg80211_world_regdom) | 215 | if (r == cfg80211_world_regdom) |
194 | cfg80211_regdomain = NULL; | 216 | r = NULL; |
195 | if (cfg80211_world_regdom == &world_regdom) | 217 | if (cfg80211_world_regdom == &world_regdom) |
196 | cfg80211_world_regdom = NULL; | 218 | cfg80211_world_regdom = NULL; |
197 | if (cfg80211_regdomain == &world_regdom) | 219 | if (r == &world_regdom) |
198 | cfg80211_regdomain = NULL; | 220 | r = NULL; |
199 | 221 | ||
200 | kfree(cfg80211_regdomain); | 222 | rcu_free_regdom(r); |
201 | kfree(cfg80211_world_regdom); | 223 | rcu_free_regdom(cfg80211_world_regdom); |
202 | 224 | ||
203 | cfg80211_world_regdom = &world_regdom; | 225 | cfg80211_world_regdom = &world_regdom; |
204 | cfg80211_regdomain = new_regdom; | 226 | rcu_assign_pointer(cfg80211_regdomain, new_regdom); |
205 | 227 | ||
206 | if (!full_reset) | 228 | if (!full_reset) |
207 | return; | 229 | return; |
@@ -219,7 +241,6 @@ static void update_world_regdomain(const struct ieee80211_regdomain *rd) | |||
219 | { | 241 | { |
220 | WARN_ON(!last_request); | 242 | WARN_ON(!last_request); |
221 | 243 | ||
222 | assert_cfg80211_lock(); | ||
223 | assert_reg_lock(); | 244 | assert_reg_lock(); |
224 | 245 | ||
225 | reset_regdomains(false, rd); | 246 | reset_regdomains(false, rd); |
@@ -280,11 +301,11 @@ static bool alpha2_equal(const char *alpha2_x, const char *alpha2_y) | |||
280 | 301 | ||
281 | static bool regdom_changes(const char *alpha2) | 302 | static bool regdom_changes(const char *alpha2) |
282 | { | 303 | { |
283 | assert_cfg80211_lock(); | 304 | const struct ieee80211_regdomain *r = get_cfg80211_regdom(); |
284 | 305 | ||
285 | if (!cfg80211_regdomain) | 306 | if (!r) |
286 | return true; | 307 | return true; |
287 | return !alpha2_equal(cfg80211_regdomain->alpha2, alpha2); | 308 | return !alpha2_equal(r->alpha2, alpha2); |
288 | } | 309 | } |
289 | 310 | ||
290 | /* | 311 | /* |
@@ -727,7 +748,6 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, | |||
727 | const struct ieee80211_regdomain *regd; | 748 | const struct ieee80211_regdomain *regd; |
728 | 749 | ||
729 | assert_reg_lock(); | 750 | assert_reg_lock(); |
730 | assert_cfg80211_lock(); | ||
731 | 751 | ||
732 | /* | 752 | /* |
733 | * Follow the driver's regulatory domain, if present, unless a country | 753 | * Follow the driver's regulatory domain, if present, unless a country |
@@ -736,9 +756,9 @@ int freq_reg_info(struct wiphy *wiphy, u32 center_freq, | |||
736 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 756 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
737 | last_request->initiator != NL80211_REGDOM_SET_BY_USER && | 757 | last_request->initiator != NL80211_REGDOM_SET_BY_USER && |
738 | wiphy->regd) | 758 | wiphy->regd) |
739 | regd = wiphy->regd; | 759 | regd = get_wiphy_regdom(wiphy); |
740 | else | 760 | else |
741 | regd = cfg80211_regdomain; | 761 | regd = get_cfg80211_regdom(); |
742 | 762 | ||
743 | return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); | 763 | return freq_reg_info_regd(wiphy, center_freq, reg_rule, regd); |
744 | } | 764 | } |
@@ -809,8 +829,6 @@ static void handle_channel(struct wiphy *wiphy, | |||
809 | const struct ieee80211_freq_range *freq_range = NULL; | 829 | const struct ieee80211_freq_range *freq_range = NULL; |
810 | struct wiphy *request_wiphy = NULL; | 830 | struct wiphy *request_wiphy = NULL; |
811 | 831 | ||
812 | assert_cfg80211_lock(); | ||
813 | |||
814 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 832 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
815 | 833 | ||
816 | flags = chan->orig_flags; | 834 | flags = chan->orig_flags; |
@@ -1060,15 +1078,17 @@ static void wiphy_update_beacon_reg(struct wiphy *wiphy) | |||
1060 | 1078 | ||
1061 | static bool reg_is_world_roaming(struct wiphy *wiphy) | 1079 | static bool reg_is_world_roaming(struct wiphy *wiphy) |
1062 | { | 1080 | { |
1063 | assert_cfg80211_lock(); | 1081 | const struct ieee80211_regdomain *cr = get_cfg80211_regdom(); |
1082 | const struct ieee80211_regdomain *wr = get_wiphy_regdom(wiphy); | ||
1064 | 1083 | ||
1065 | if (is_world_regdom(cfg80211_regdomain->alpha2) || | 1084 | if (is_world_regdom(cr->alpha2) || (wr && is_world_regdom(wr->alpha2))) |
1066 | (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) | ||
1067 | return true; | 1085 | return true; |
1086 | |||
1068 | if (last_request && | 1087 | if (last_request && |
1069 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && | 1088 | last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && |
1070 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) | 1089 | wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) |
1071 | return true; | 1090 | return true; |
1091 | |||
1072 | return false; | 1092 | return false; |
1073 | } | 1093 | } |
1074 | 1094 | ||
@@ -1165,13 +1185,12 @@ static void wiphy_update_regulatory(struct wiphy *wiphy, | |||
1165 | { | 1185 | { |
1166 | enum ieee80211_band band; | 1186 | enum ieee80211_band band; |
1167 | 1187 | ||
1168 | assert_cfg80211_lock(); | ||
1169 | assert_reg_lock(); | 1188 | assert_reg_lock(); |
1170 | 1189 | ||
1171 | if (ignore_reg_update(wiphy, initiator)) | 1190 | if (ignore_reg_update(wiphy, initiator)) |
1172 | return; | 1191 | return; |
1173 | 1192 | ||
1174 | last_request->dfs_region = cfg80211_regdomain->dfs_region; | 1193 | last_request->dfs_region = get_cfg80211_regdom()->dfs_region; |
1175 | 1194 | ||
1176 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) | 1195 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) |
1177 | handle_band(wiphy, initiator, wiphy->bands[band]); | 1196 | handle_band(wiphy, initiator, wiphy->bands[band]); |
@@ -1188,6 +1207,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator) | |||
1188 | struct cfg80211_registered_device *rdev; | 1207 | struct cfg80211_registered_device *rdev; |
1189 | struct wiphy *wiphy; | 1208 | struct wiphy *wiphy; |
1190 | 1209 | ||
1210 | assert_cfg80211_lock(); | ||
1211 | |||
1191 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { | 1212 | list_for_each_entry(rdev, &cfg80211_rdev_list, list) { |
1192 | wiphy = &rdev->wiphy; | 1213 | wiphy = &rdev->wiphy; |
1193 | wiphy_update_regulatory(wiphy, initiator); | 1214 | wiphy_update_regulatory(wiphy, initiator); |
@@ -1402,7 +1423,7 @@ static void reg_set_request_processed(void) | |||
1402 | * | 1423 | * |
1403 | * Returns one of the different reg request treatment values. | 1424 | * Returns one of the different reg request treatment values. |
1404 | * | 1425 | * |
1405 | * Caller must hold &cfg80211_mutex and ®_mutex | 1426 | * Caller must hold ®_mutex |
1406 | */ | 1427 | */ |
1407 | static enum reg_request_treatment | 1428 | static enum reg_request_treatment |
1408 | __regulatory_hint(struct wiphy *wiphy, | 1429 | __regulatory_hint(struct wiphy *wiphy, |
@@ -1412,20 +1433,18 @@ __regulatory_hint(struct wiphy *wiphy, | |||
1412 | bool intersect = false; | 1433 | bool intersect = false; |
1413 | enum reg_request_treatment treatment; | 1434 | enum reg_request_treatment treatment; |
1414 | 1435 | ||
1415 | assert_cfg80211_lock(); | ||
1416 | |||
1417 | treatment = get_reg_request_treatment(wiphy, pending_request); | 1436 | treatment = get_reg_request_treatment(wiphy, pending_request); |
1418 | 1437 | ||
1419 | switch (treatment) { | 1438 | switch (treatment) { |
1420 | case REG_REQ_INTERSECT: | 1439 | case REG_REQ_INTERSECT: |
1421 | if (pending_request->initiator == | 1440 | if (pending_request->initiator == |
1422 | NL80211_REGDOM_SET_BY_DRIVER) { | 1441 | NL80211_REGDOM_SET_BY_DRIVER) { |
1423 | regd = reg_copy_regd(cfg80211_regdomain); | 1442 | regd = reg_copy_regd(get_cfg80211_regdom()); |
1424 | if (IS_ERR(regd)) { | 1443 | if (IS_ERR(regd)) { |
1425 | kfree(pending_request); | 1444 | kfree(pending_request); |
1426 | return PTR_ERR(regd); | 1445 | return PTR_ERR(regd); |
1427 | } | 1446 | } |
1428 | wiphy->regd = regd; | 1447 | rcu_assign_pointer(wiphy->regd, regd); |
1429 | } | 1448 | } |
1430 | intersect = true; | 1449 | intersect = true; |
1431 | break; | 1450 | break; |
@@ -1439,13 +1458,13 @@ __regulatory_hint(struct wiphy *wiphy, | |||
1439 | */ | 1458 | */ |
1440 | if (treatment == REG_REQ_ALREADY_SET && | 1459 | if (treatment == REG_REQ_ALREADY_SET && |
1441 | pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { | 1460 | pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { |
1442 | regd = reg_copy_regd(cfg80211_regdomain); | 1461 | regd = reg_copy_regd(get_cfg80211_regdom()); |
1443 | if (IS_ERR(regd)) { | 1462 | if (IS_ERR(regd)) { |
1444 | kfree(pending_request); | 1463 | kfree(pending_request); |
1445 | return REG_REQ_IGNORE; | 1464 | return REG_REQ_IGNORE; |
1446 | } | 1465 | } |
1447 | treatment = REG_REQ_ALREADY_SET; | 1466 | treatment = REG_REQ_ALREADY_SET; |
1448 | wiphy->regd = regd; | 1467 | rcu_assign_pointer(wiphy->regd, regd); |
1449 | goto new_request; | 1468 | goto new_request; |
1450 | } | 1469 | } |
1451 | kfree(pending_request); | 1470 | kfree(pending_request); |
@@ -2051,6 +2070,8 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2051 | 2070 | ||
2052 | /* Some basic sanity checks first */ | 2071 | /* Some basic sanity checks first */ |
2053 | 2072 | ||
2073 | assert_reg_lock(); | ||
2074 | |||
2054 | if (!reg_is_valid_request(rd->alpha2)) | 2075 | if (!reg_is_valid_request(rd->alpha2)) |
2055 | return -EINVAL; | 2076 | return -EINVAL; |
2056 | 2077 | ||
@@ -2120,7 +2141,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2120 | if (IS_ERR(regd)) | 2141 | if (IS_ERR(regd)) |
2121 | return PTR_ERR(regd); | 2142 | return PTR_ERR(regd); |
2122 | 2143 | ||
2123 | request_wiphy->regd = regd; | 2144 | rcu_assign_pointer(request_wiphy->regd, regd); |
2124 | reset_regdomains(false, rd); | 2145 | reset_regdomains(false, rd); |
2125 | return 0; | 2146 | return 0; |
2126 | } | 2147 | } |
@@ -2128,7 +2149,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2128 | /* Intersection requires a bit more work */ | 2149 | /* Intersection requires a bit more work */ |
2129 | 2150 | ||
2130 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { | 2151 | if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
2131 | intersected_rd = regdom_intersect(rd, cfg80211_regdomain); | 2152 | intersected_rd = regdom_intersect(rd, get_cfg80211_regdom()); |
2132 | if (!intersected_rd) | 2153 | if (!intersected_rd) |
2133 | return -EINVAL; | 2154 | return -EINVAL; |
2134 | 2155 | ||
@@ -2138,7 +2159,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2138 | * domain we keep it for its private use | 2159 | * domain we keep it for its private use |
2139 | */ | 2160 | */ |
2140 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) | 2161 | if (last_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) |
2141 | request_wiphy->regd = rd; | 2162 | rcu_assign_pointer(request_wiphy->regd, rd); |
2142 | else | 2163 | else |
2143 | kfree(rd); | 2164 | kfree(rd); |
2144 | 2165 | ||
@@ -2156,14 +2177,12 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
2156 | /* | 2177 | /* |
2157 | * Use this call to set the current regulatory domain. Conflicts with | 2178 | * Use this call to set the current regulatory domain. Conflicts with |
2158 | * multiple drivers can be ironed out later. Caller must've already | 2179 | * multiple drivers can be ironed out later. Caller must've already |
2159 | * kmalloc'd the rd structure. Caller must hold cfg80211_mutex | 2180 | * kmalloc'd the rd structure. |
2160 | */ | 2181 | */ |
2161 | int set_regdom(const struct ieee80211_regdomain *rd) | 2182 | int set_regdom(const struct ieee80211_regdomain *rd) |
2162 | { | 2183 | { |
2163 | int r; | 2184 | int r; |
2164 | 2185 | ||
2165 | assert_cfg80211_lock(); | ||
2166 | |||
2167 | mutex_lock(®_mutex); | 2186 | mutex_lock(®_mutex); |
2168 | 2187 | ||
2169 | /* Note that this doesn't update the wiphys, this is done below */ | 2188 | /* Note that this doesn't update the wiphys, this is done below */ |
@@ -2177,7 +2196,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2177 | } | 2196 | } |
2178 | 2197 | ||
2179 | /* This would make this whole thing pointless */ | 2198 | /* This would make this whole thing pointless */ |
2180 | if (WARN_ON(!last_request->intersect && rd != cfg80211_regdomain)) { | 2199 | if (WARN_ON(!last_request->intersect && |
2200 | rd != get_cfg80211_regdom())) { | ||
2181 | r = -EINVAL; | 2201 | r = -EINVAL; |
2182 | goto out; | 2202 | goto out; |
2183 | } | 2203 | } |
@@ -2185,7 +2205,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
2185 | /* update all wiphys now with the new established regulatory domain */ | 2205 | /* update all wiphys now with the new established regulatory domain */ |
2186 | update_all_wiphy_regulatory(last_request->initiator); | 2206 | update_all_wiphy_regulatory(last_request->initiator); |
2187 | 2207 | ||
2188 | print_regdomain(cfg80211_regdomain); | 2208 | print_regdomain(get_cfg80211_regdom()); |
2189 | 2209 | ||
2190 | nl80211_send_reg_change_event(last_request); | 2210 | nl80211_send_reg_change_event(last_request); |
2191 | 2211 | ||
@@ -2238,7 +2258,8 @@ void wiphy_regulatory_deregister(struct wiphy *wiphy) | |||
2238 | if (!reg_dev_ignore_cell_hint(wiphy)) | 2258 | if (!reg_dev_ignore_cell_hint(wiphy)) |
2239 | reg_num_devs_support_basehint--; | 2259 | reg_num_devs_support_basehint--; |
2240 | 2260 | ||
2241 | kfree(wiphy->regd); | 2261 | rcu_free_regdom(get_wiphy_regdom(wiphy)); |
2262 | rcu_assign_pointer(wiphy->regd, NULL); | ||
2242 | 2263 | ||
2243 | if (last_request) | 2264 | if (last_request) |
2244 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); | 2265 | request_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx); |
@@ -2273,13 +2294,13 @@ int __init regulatory_init(void) | |||
2273 | 2294 | ||
2274 | reg_regdb_size_check(); | 2295 | reg_regdb_size_check(); |
2275 | 2296 | ||
2276 | cfg80211_regdomain = cfg80211_world_regdom; | 2297 | rcu_assign_pointer(cfg80211_regdomain, cfg80211_world_regdom); |
2277 | 2298 | ||
2278 | user_alpha2[0] = '9'; | 2299 | user_alpha2[0] = '9'; |
2279 | user_alpha2[1] = '7'; | 2300 | user_alpha2[1] = '7'; |
2280 | 2301 | ||
2281 | /* We always try to get an update for the static regdomain */ | 2302 | /* We always try to get an update for the static regdomain */ |
2282 | err = regulatory_hint_core(cfg80211_regdomain->alpha2); | 2303 | err = regulatory_hint_core(cfg80211_world_regdom->alpha2); |
2283 | if (err) { | 2304 | if (err) { |
2284 | if (err == -ENOMEM) | 2305 | if (err == -ENOMEM) |
2285 | return err; | 2306 | return err; |
@@ -2313,10 +2334,8 @@ void regulatory_exit(void) | |||
2313 | cancel_delayed_work_sync(®_timeout); | 2334 | cancel_delayed_work_sync(®_timeout); |
2314 | 2335 | ||
2315 | /* Lock to suppress warnings */ | 2336 | /* Lock to suppress warnings */ |
2316 | mutex_lock(&cfg80211_mutex); | ||
2317 | mutex_lock(®_mutex); | 2337 | mutex_lock(®_mutex); |
2318 | reset_regdomains(true, NULL); | 2338 | reset_regdomains(true, NULL); |
2319 | mutex_unlock(&cfg80211_mutex); | ||
2320 | mutex_unlock(®_mutex); | 2339 | mutex_unlock(®_mutex); |
2321 | 2340 | ||
2322 | dev_set_uevent_suppress(®_pdev->dev, true); | 2341 | dev_set_uevent_suppress(®_pdev->dev, true); |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index d391b50d2829..af2d5f8a5d82 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -16,7 +16,7 @@ | |||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | */ | 17 | */ |
18 | 18 | ||
19 | extern const struct ieee80211_regdomain *cfg80211_regdomain; | 19 | extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; |
20 | 20 | ||
21 | bool is_world_regdom(const char *alpha2); | 21 | bool is_world_regdom(const char *alpha2); |
22 | bool reg_supported_dfs_region(u8 dfs_region); | 22 | bool reg_supported_dfs_region(u8 dfs_region); |