blob: 931be419ab5aece83990b530afa5979b2053294c [file] [log] [blame]
Johannes Bergf444de02010-05-05 15:25:02 +02001/*
2 * mac80211 - channel management
3 */
4
Johannes Berg0aaffa92010-05-05 15:28:27 +02005#include <linux/nl80211.h>
Johannes Berg3448c002012-09-11 17:57:42 +02006#include <linux/export.h>
Johannes Berg4d76d212012-12-11 20:38:41 +01007#include <linux/rtnetlink.h>
Paul Stewart3117bbdb2012-03-13 07:46:18 -07008#include <net/cfg80211.h>
Johannes Bergf444de02010-05-05 15:25:02 +02009#include "ieee80211_i.h"
Michal Kazior35f2fce2012-06-26 14:37:20 +020010#include "driver-ops.h"
Johannes Bergf444de02010-05-05 15:25:02 +020011
Johannes Berg18942d32013-02-07 21:30:37 +010012static void ieee80211_change_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +010013 struct ieee80211_chanctx *ctx,
14 const struct cfg80211_chan_def *chandef)
Michal Kazior23a85b452012-06-26 14:37:21 +020015{
Johannes Berg4bf88532012-11-09 11:39:59 +010016 if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
Michal Kaziore89a96f2012-06-26 14:37:22 +020017 return;
18
Johannes Berg4bf88532012-11-09 11:39:59 +010019 WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
20
21 ctx->conf.def = *chandef;
22 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
Johannes Berg55de9082012-07-26 17:24:39 +020023
24 if (!local->use_chanctx) {
Johannes Berg4bf88532012-11-09 11:39:59 +010025 local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
Johannes Berg55de9082012-07-26 17:24:39 +020026 ieee80211_hw_config(local, 0);
27 }
Johannes Berg0aaffa92010-05-05 15:28:27 +020028}
Michal Kaziord01a1e62012-06-26 14:37:16 +020029
30static struct ieee80211_chanctx *
31ieee80211_find_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +010032 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +020033 enum ieee80211_chanctx_mode mode)
34{
35 struct ieee80211_chanctx *ctx;
36
37 lockdep_assert_held(&local->chanctx_mtx);
38
39 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
40 return NULL;
Michal Kaziord01a1e62012-06-26 14:37:16 +020041
42 list_for_each_entry(ctx, &local->chanctx_list, list) {
Johannes Berg4bf88532012-11-09 11:39:59 +010043 const struct cfg80211_chan_def *compat;
Michal Kaziore89a96f2012-06-26 14:37:22 +020044
Michal Kaziord01a1e62012-06-26 14:37:16 +020045 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
46 continue;
Johannes Berg4bf88532012-11-09 11:39:59 +010047
48 compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
49 if (!compat)
Michal Kaziord01a1e62012-06-26 14:37:16 +020050 continue;
51
Johannes Berg18942d32013-02-07 21:30:37 +010052 ieee80211_change_chanctx(local, ctx, compat);
Michal Kaziore89a96f2012-06-26 14:37:22 +020053
Michal Kaziord01a1e62012-06-26 14:37:16 +020054 return ctx;
55 }
56
57 return NULL;
58}
59
60static struct ieee80211_chanctx *
61ieee80211_new_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +010062 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +020063 enum ieee80211_chanctx_mode mode)
64{
65 struct ieee80211_chanctx *ctx;
Johannes Berg382a1032013-03-22 22:30:09 +010066 u32 changed;
Michal Kazior35f2fce2012-06-26 14:37:20 +020067 int err;
Michal Kaziord01a1e62012-06-26 14:37:16 +020068
69 lockdep_assert_held(&local->chanctx_mtx);
70
71 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
72 if (!ctx)
73 return ERR_PTR(-ENOMEM);
74
Johannes Berg4bf88532012-11-09 11:39:59 +010075 ctx->conf.def = *chandef;
Johannes Berg04ecd252012-09-11 14:34:12 +020076 ctx->conf.rx_chains_static = 1;
77 ctx->conf.rx_chains_dynamic = 1;
Michal Kaziord01a1e62012-06-26 14:37:16 +020078 ctx->mode = mode;
79
Johannes Berg382a1032013-03-22 22:30:09 +010080 /* acquire mutex to prevent idle from changing */
81 mutex_lock(&local->mtx);
82 /* turn idle off *before* setting channel -- some drivers need that */
83 changed = ieee80211_idle_off(local);
84 if (changed)
85 ieee80211_hw_config(local, changed);
86
Johannes Berg55de9082012-07-26 17:24:39 +020087 if (!local->use_chanctx) {
Johannes Berg4bf88532012-11-09 11:39:59 +010088 local->_oper_channel_type =
89 cfg80211_get_chandef_type(chandef);
90 local->_oper_channel = chandef->chan;
Johannes Berg55de9082012-07-26 17:24:39 +020091 ieee80211_hw_config(local, 0);
92 } else {
93 err = drv_add_chanctx(local, ctx);
94 if (err) {
95 kfree(ctx);
Johannes Berg382a1032013-03-22 22:30:09 +010096 ctx = ERR_PTR(err);
97
98 ieee80211_recalc_idle(local);
99 goto out;
Johannes Berg55de9082012-07-26 17:24:39 +0200100 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200101 }
102
Johannes Berg382a1032013-03-22 22:30:09 +0100103 /* and keep the mutex held until the new chanctx is on the list */
Johannes Berg3448c002012-09-11 17:57:42 +0200104 list_add_rcu(&ctx->list, &local->chanctx_list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200105
Johannes Berg382a1032013-03-22 22:30:09 +0100106 out:
Johannes Bergfd0f9792013-02-07 00:14:51 +0100107 mutex_unlock(&local->mtx);
108
Michal Kaziord01a1e62012-06-26 14:37:16 +0200109 return ctx;
110}
111
112static void ieee80211_free_chanctx(struct ieee80211_local *local,
113 struct ieee80211_chanctx *ctx)
114{
115 lockdep_assert_held(&local->chanctx_mtx);
116
117 WARN_ON_ONCE(ctx->refcount != 0);
118
Johannes Berg55de9082012-07-26 17:24:39 +0200119 if (!local->use_chanctx) {
120 local->_oper_channel_type = NL80211_CHAN_NO_HT;
121 ieee80211_hw_config(local, 0);
122 } else {
123 drv_remove_chanctx(local, ctx);
124 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200125
Johannes Berg3448c002012-09-11 17:57:42 +0200126 list_del_rcu(&ctx->list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200127 kfree_rcu(ctx, rcu_head);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100128
129 mutex_lock(&local->mtx);
130 ieee80211_recalc_idle(local);
131 mutex_unlock(&local->mtx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200132}
133
134static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
135 struct ieee80211_chanctx *ctx)
136{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200137 struct ieee80211_local *local = sdata->local;
138 int ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200139
140 lockdep_assert_held(&local->chanctx_mtx);
141
Michal Kazior35f2fce2012-06-26 14:37:20 +0200142 ret = drv_assign_vif_chanctx(local, sdata, ctx);
143 if (ret)
144 return ret;
145
Michal Kaziord01a1e62012-06-26 14:37:16 +0200146 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
147 ctx->refcount++;
148
Johannes Berg1ea6f9c2012-10-24 10:59:25 +0200149 ieee80211_recalc_txpower(sdata);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100150 sdata->vif.bss_conf.idle = false;
Johannes Berg5bbe754d2013-02-13 13:50:51 +0100151
152 if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
153 sdata->vif.type != NL80211_IFTYPE_MONITOR)
154 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
Johannes Berg1ea6f9c2012-10-24 10:59:25 +0200155
Michal Kaziord01a1e62012-06-26 14:37:16 +0200156 return 0;
157}
158
Johannes Berg4bf88532012-11-09 11:39:59 +0100159static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
160 struct ieee80211_chanctx *ctx)
Michal Kaziore89a96f2012-06-26 14:37:22 +0200161{
162 struct ieee80211_chanctx_conf *conf = &ctx->conf;
163 struct ieee80211_sub_if_data *sdata;
Johannes Berg4bf88532012-11-09 11:39:59 +0100164 const struct cfg80211_chan_def *compat = NULL;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200165
166 lockdep_assert_held(&local->chanctx_mtx);
167
168 rcu_read_lock();
169 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
Johannes Berg4bf88532012-11-09 11:39:59 +0100170
Michal Kaziore89a96f2012-06-26 14:37:22 +0200171 if (!ieee80211_sdata_running(sdata))
172 continue;
173 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
174 continue;
175
Johannes Berg4bf88532012-11-09 11:39:59 +0100176 if (!compat)
177 compat = &sdata->vif.bss_conf.chandef;
178
179 compat = cfg80211_chandef_compatible(
180 &sdata->vif.bss_conf.chandef, compat);
181 if (!compat)
182 break;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200183 }
184 rcu_read_unlock();
185
Johannes Berg4bf88532012-11-09 11:39:59 +0100186 if (WARN_ON_ONCE(!compat))
187 return;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200188
Johannes Berg18942d32013-02-07 21:30:37 +0100189 ieee80211_change_chanctx(local, ctx, compat);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200190}
191
Michal Kaziord01a1e62012-06-26 14:37:16 +0200192static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
193 struct ieee80211_chanctx *ctx)
194{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200195 struct ieee80211_local *local = sdata->local;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200196
197 lockdep_assert_held(&local->chanctx_mtx);
198
199 ctx->refcount--;
200 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200201
Johannes Bergfd0f9792013-02-07 00:14:51 +0100202 sdata->vif.bss_conf.idle = true;
Johannes Berg5bbe754d2013-02-13 13:50:51 +0100203
204 if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
205 sdata->vif.type != NL80211_IFTYPE_MONITOR)
206 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100207
Michal Kazior35f2fce2012-06-26 14:37:20 +0200208 drv_unassign_vif_chanctx(local, sdata, ctx);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200209
Johannes Berg04ecd252012-09-11 14:34:12 +0200210 if (ctx->refcount > 0) {
Michal Kaziore89a96f2012-06-26 14:37:22 +0200211 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
Johannes Berg04ecd252012-09-11 14:34:12 +0200212 ieee80211_recalc_smps_chanctx(local, ctx);
Simon Wunderlich164eb022013-02-08 18:16:20 +0100213 ieee80211_recalc_radar_chanctx(local, ctx);
Johannes Berg04ecd252012-09-11 14:34:12 +0200214 }
Michal Kaziord01a1e62012-06-26 14:37:16 +0200215}
216
217static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
218{
219 struct ieee80211_local *local = sdata->local;
220 struct ieee80211_chanctx_conf *conf;
221 struct ieee80211_chanctx *ctx;
222
223 lockdep_assert_held(&local->chanctx_mtx);
224
225 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
226 lockdep_is_held(&local->chanctx_mtx));
227 if (!conf)
228 return;
229
230 ctx = container_of(conf, struct ieee80211_chanctx, conf);
231
232 ieee80211_unassign_vif_chanctx(sdata, ctx);
233 if (ctx->refcount == 0)
234 ieee80211_free_chanctx(local, ctx);
235}
236
Simon Wunderlich164eb022013-02-08 18:16:20 +0100237void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
238 struct ieee80211_chanctx *chanctx)
239{
240 struct ieee80211_sub_if_data *sdata;
241 bool radar_enabled = false;
242
243 lockdep_assert_held(&local->chanctx_mtx);
244
245 rcu_read_lock();
246 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
247 if (sdata->radar_required) {
248 radar_enabled = true;
249 break;
250 }
251 }
252 rcu_read_unlock();
253
254 if (radar_enabled == chanctx->conf.radar_enabled)
255 return;
256
257 chanctx->conf.radar_enabled = radar_enabled;
258 local->radar_detect_enabled = chanctx->conf.radar_enabled;
259
260 if (!local->use_chanctx) {
261 local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
262 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
263 }
264
265 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
266}
267
Johannes Berg04ecd252012-09-11 14:34:12 +0200268void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
269 struct ieee80211_chanctx *chanctx)
270{
271 struct ieee80211_sub_if_data *sdata;
272 u8 rx_chains_static, rx_chains_dynamic;
273
274 lockdep_assert_held(&local->chanctx_mtx);
275
276 rx_chains_static = 1;
277 rx_chains_dynamic = 1;
278
279 rcu_read_lock();
280 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
281 u8 needed_static, needed_dynamic;
282
283 if (!ieee80211_sdata_running(sdata))
284 continue;
285
286 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
287 &chanctx->conf)
288 continue;
289
290 switch (sdata->vif.type) {
291 case NL80211_IFTYPE_P2P_DEVICE:
292 continue;
293 case NL80211_IFTYPE_STATION:
294 if (!sdata->u.mgd.associated)
295 continue;
296 break;
297 case NL80211_IFTYPE_AP_VLAN:
298 continue;
299 case NL80211_IFTYPE_AP:
300 case NL80211_IFTYPE_ADHOC:
301 case NL80211_IFTYPE_WDS:
302 case NL80211_IFTYPE_MESH_POINT:
303 break;
304 default:
305 WARN_ON_ONCE(1);
306 }
307
308 switch (sdata->smps_mode) {
309 default:
310 WARN_ONCE(1, "Invalid SMPS mode %d\n",
311 sdata->smps_mode);
312 /* fall through */
313 case IEEE80211_SMPS_OFF:
314 needed_static = sdata->needed_rx_chains;
315 needed_dynamic = sdata->needed_rx_chains;
316 break;
317 case IEEE80211_SMPS_DYNAMIC:
318 needed_static = 1;
319 needed_dynamic = sdata->needed_rx_chains;
320 break;
321 case IEEE80211_SMPS_STATIC:
322 needed_static = 1;
323 needed_dynamic = 1;
324 break;
325 }
326
327 rx_chains_static = max(rx_chains_static, needed_static);
328 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
329 }
330 rcu_read_unlock();
331
332 if (!local->use_chanctx) {
333 if (rx_chains_static > 1)
334 local->smps_mode = IEEE80211_SMPS_OFF;
335 else if (rx_chains_dynamic > 1)
336 local->smps_mode = IEEE80211_SMPS_DYNAMIC;
337 else
338 local->smps_mode = IEEE80211_SMPS_STATIC;
339 ieee80211_hw_config(local, 0);
340 }
341
342 if (rx_chains_static == chanctx->conf.rx_chains_static &&
343 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
344 return;
345
346 chanctx->conf.rx_chains_static = rx_chains_static;
347 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
348 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
349}
350
Michal Kaziord01a1e62012-06-26 14:37:16 +0200351int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
Johannes Berg4bf88532012-11-09 11:39:59 +0100352 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +0200353 enum ieee80211_chanctx_mode mode)
354{
355 struct ieee80211_local *local = sdata->local;
356 struct ieee80211_chanctx *ctx;
357 int ret;
358
Johannes Berg55de9082012-07-26 17:24:39 +0200359 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
360
Michal Kaziord01a1e62012-06-26 14:37:16 +0200361 mutex_lock(&local->chanctx_mtx);
362 __ieee80211_vif_release_channel(sdata);
363
Johannes Berg4bf88532012-11-09 11:39:59 +0100364 ctx = ieee80211_find_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200365 if (!ctx)
Johannes Berg4bf88532012-11-09 11:39:59 +0100366 ctx = ieee80211_new_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200367 if (IS_ERR(ctx)) {
368 ret = PTR_ERR(ctx);
369 goto out;
370 }
371
Johannes Berg4bf88532012-11-09 11:39:59 +0100372 sdata->vif.bss_conf.chandef = *chandef;
Johannes Berg55de9082012-07-26 17:24:39 +0200373
Michal Kaziord01a1e62012-06-26 14:37:16 +0200374 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
375 if (ret) {
376 /* if assign fails refcount stays the same */
377 if (ctx->refcount == 0)
378 ieee80211_free_chanctx(local, ctx);
379 goto out;
380 }
381
Johannes Berg04ecd252012-09-11 14:34:12 +0200382 ieee80211_recalc_smps_chanctx(local, ctx);
Simon Wunderlich164eb022013-02-08 18:16:20 +0100383 ieee80211_recalc_radar_chanctx(local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200384 out:
385 mutex_unlock(&local->chanctx_mtx);
386 return ret;
387}
388
Johannes Berg2c9b7352013-02-07 21:37:29 +0100389int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
390 const struct cfg80211_chan_def *chandef,
391 u32 *changed)
392{
393 struct ieee80211_local *local = sdata->local;
394 struct ieee80211_chanctx_conf *conf;
395 struct ieee80211_chanctx *ctx;
396 int ret;
397
398 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
399 IEEE80211_CHAN_DISABLED))
400 return -EINVAL;
401
402 mutex_lock(&local->chanctx_mtx);
403 if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
404 ret = 0;
405 goto out;
406 }
407
408 if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
409 sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
410 ret = -EINVAL;
411 goto out;
412 }
413
414 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
415 lockdep_is_held(&local->chanctx_mtx));
416 if (!conf) {
417 ret = -EINVAL;
418 goto out;
419 }
420
421 ctx = container_of(conf, struct ieee80211_chanctx, conf);
422 if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
423 ret = -EINVAL;
424 goto out;
425 }
426
427 sdata->vif.bss_conf.chandef = *chandef;
428
429 ieee80211_recalc_chanctx_chantype(local, ctx);
430
431 *changed |= BSS_CHANGED_BANDWIDTH;
432 ret = 0;
433 out:
434 mutex_unlock(&local->chanctx_mtx);
435 return ret;
436}
437
Michal Kaziord01a1e62012-06-26 14:37:16 +0200438void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
439{
Johannes Berg55de9082012-07-26 17:24:39 +0200440 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
441
Michal Kaziord01a1e62012-06-26 14:37:16 +0200442 mutex_lock(&sdata->local->chanctx_mtx);
443 __ieee80211_vif_release_channel(sdata);
444 mutex_unlock(&sdata->local->chanctx_mtx);
445}
Johannes Berg3448c002012-09-11 17:57:42 +0200446
Johannes Berg4d76d212012-12-11 20:38:41 +0100447void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
448{
449 struct ieee80211_local *local = sdata->local;
450 struct ieee80211_sub_if_data *ap;
451 struct ieee80211_chanctx_conf *conf;
452
453 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
454 return;
455
456 ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
457
458 mutex_lock(&local->chanctx_mtx);
459
460 conf = rcu_dereference_protected(ap->vif.chanctx_conf,
461 lockdep_is_held(&local->chanctx_mtx));
462 rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
463 mutex_unlock(&local->chanctx_mtx);
464}
465
Johannes Berg1f4ac5a2013-02-08 12:07:44 +0100466void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
467 bool clear)
468{
469 struct ieee80211_local *local = sdata->local;
470 struct ieee80211_sub_if_data *vlan;
471 struct ieee80211_chanctx_conf *conf;
472
473 ASSERT_RTNL();
474
475 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
476 return;
477
478 mutex_lock(&local->chanctx_mtx);
479
480 /*
481 * Check that conf exists, even when clearing this function
482 * must be called with the AP's channel context still there
483 * as it would otherwise cause VLANs to have an invalid
484 * channel context pointer for a while, possibly pointing
485 * to a channel context that has already been freed.
486 */
487 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
488 lockdep_is_held(&local->chanctx_mtx));
489 WARN_ON(!conf);
490
491 if (clear)
492 conf = NULL;
493
494 list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
495 rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
496
497 mutex_unlock(&local->chanctx_mtx);
498}
499
Johannes Berg3448c002012-09-11 17:57:42 +0200500void ieee80211_iter_chan_contexts_atomic(
501 struct ieee80211_hw *hw,
502 void (*iter)(struct ieee80211_hw *hw,
503 struct ieee80211_chanctx_conf *chanctx_conf,
504 void *data),
505 void *iter_data)
506{
507 struct ieee80211_local *local = hw_to_local(hw);
508 struct ieee80211_chanctx *ctx;
509
510 rcu_read_lock();
511 list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
Johannes Berg8a61af62012-12-13 17:42:30 +0100512 if (ctx->driver_present)
513 iter(hw, &ctx->conf, iter_data);
Johannes Berg3448c002012-09-11 17:57:42 +0200514 rcu_read_unlock();
515}
516EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);