aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/reg.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-12-06 09:47:38 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-01-03 07:01:29 -0500
commit458f4f9e960b9a3b674c4b87d996eef186b1fe83 (patch)
treefebf655badc2a045b3aaaf63271226729c76e325 /net/wireless/reg.c
parent379b82f4c9dc6e67bf61aa61b096c06a2f320f60 (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>
Diffstat (limited to 'net/wireless/reg.c')
-rw-r--r--net/wireless/reg.c113
1 files changed, 66 insertions, 47 deletions
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 */
100const struct ieee80211_regdomain *cfg80211_regdomain; 99const 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 */
108static DEFINE_MUTEX(reg_mutex); 108static DEFINE_MUTEX(reg_mutex);
109 109
@@ -118,6 +118,25 @@ static inline void assert_reg_lock(void)
118 lockdep_assert_held(&reg_mutex); 118 lockdep_assert_held(&reg_mutex);
119} 119}
120 120
121static const struct ieee80211_regdomain *get_cfg80211_regdom(void)
122{
123 return rcu_dereference_protected(cfg80211_regdomain,
124 lockdep_is_held(&reg_mutex));
125}
126
127static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
128{
129 return rcu_dereference_protected(wiphy->regd,
130 lockdep_is_held(&reg_mutex));
131}
132
133static 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 */
122static LIST_HEAD(reg_requests_list); 141static LIST_HEAD(reg_requests_list);
123static spinlock_t reg_requests_lock; 142static spinlock_t reg_requests_lock;
@@ -186,22 +205,25 @@ MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
186static void reset_regdomains(bool full_reset, 205static 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
281static bool regdom_changes(const char *alpha2) 302static 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
1061static bool reg_is_world_roaming(struct wiphy *wiphy) 1079static 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 &reg_mutex 1426 * Caller must hold &reg_mutex
1406 */ 1427 */
1407static enum reg_request_treatment 1428static 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 */
2161int set_regdom(const struct ieee80211_regdomain *rd) 2182int 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(&reg_mutex); 2186 mutex_lock(&reg_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(&reg_timeout); 2334 cancel_delayed_work_sync(&reg_timeout);
2314 2335
2315 /* Lock to suppress warnings */ 2336 /* Lock to suppress warnings */
2316 mutex_lock(&cfg80211_mutex);
2317 mutex_lock(&reg_mutex); 2337 mutex_lock(&reg_mutex);
2318 reset_regdomains(true, NULL); 2338 reset_regdomains(true, NULL);
2319 mutex_unlock(&cfg80211_mutex);
2320 mutex_unlock(&reg_mutex); 2339 mutex_unlock(&reg_mutex);
2321 2340
2322 dev_set_uevent_suppress(&reg_pdev->dev, true); 2341 dev_set_uevent_suppress(&reg_pdev->dev, true);