diff options
Diffstat (limited to 'net/mac80211/chan.c')
-rw-r--r-- | net/mac80211/chan.c | 1238 |
1 files changed, 1103 insertions, 135 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 0c1ecfdf9a12..399ad82c997f 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c | |||
@@ -9,6 +9,187 @@ | |||
9 | #include "ieee80211_i.h" | 9 | #include "ieee80211_i.h" |
10 | #include "driver-ops.h" | 10 | #include "driver-ops.h" |
11 | 11 | ||
12 | static int ieee80211_chanctx_num_assigned(struct ieee80211_local *local, | ||
13 | struct ieee80211_chanctx *ctx) | ||
14 | { | ||
15 | struct ieee80211_sub_if_data *sdata; | ||
16 | int num = 0; | ||
17 | |||
18 | lockdep_assert_held(&local->chanctx_mtx); | ||
19 | |||
20 | list_for_each_entry(sdata, &ctx->assigned_vifs, assigned_chanctx_list) | ||
21 | num++; | ||
22 | |||
23 | return num; | ||
24 | } | ||
25 | |||
26 | static int ieee80211_chanctx_num_reserved(struct ieee80211_local *local, | ||
27 | struct ieee80211_chanctx *ctx) | ||
28 | { | ||
29 | struct ieee80211_sub_if_data *sdata; | ||
30 | int num = 0; | ||
31 | |||
32 | lockdep_assert_held(&local->chanctx_mtx); | ||
33 | |||
34 | list_for_each_entry(sdata, &ctx->reserved_vifs, reserved_chanctx_list) | ||
35 | num++; | ||
36 | |||
37 | return num; | ||
38 | } | ||
39 | |||
40 | int ieee80211_chanctx_refcount(struct ieee80211_local *local, | ||
41 | struct ieee80211_chanctx *ctx) | ||
42 | { | ||
43 | return ieee80211_chanctx_num_assigned(local, ctx) + | ||
44 | ieee80211_chanctx_num_reserved(local, ctx); | ||
45 | } | ||
46 | |||
47 | static int ieee80211_num_chanctx(struct ieee80211_local *local) | ||
48 | { | ||
49 | struct ieee80211_chanctx *ctx; | ||
50 | int num = 0; | ||
51 | |||
52 | lockdep_assert_held(&local->chanctx_mtx); | ||
53 | |||
54 | list_for_each_entry(ctx, &local->chanctx_list, list) | ||
55 | num++; | ||
56 | |||
57 | return num; | ||
58 | } | ||
59 | |||
60 | static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) | ||
61 | { | ||
62 | lockdep_assert_held(&local->chanctx_mtx); | ||
63 | return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local); | ||
64 | } | ||
65 | |||
66 | static struct ieee80211_chanctx * | ||
67 | ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata) | ||
68 | { | ||
69 | struct ieee80211_local *local __maybe_unused = sdata->local; | ||
70 | struct ieee80211_chanctx_conf *conf; | ||
71 | |||
72 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
73 | lockdep_is_held(&local->chanctx_mtx)); | ||
74 | if (!conf) | ||
75 | return NULL; | ||
76 | |||
77 | return container_of(conf, struct ieee80211_chanctx, conf); | ||
78 | } | ||
79 | |||
80 | static const struct cfg80211_chan_def * | ||
81 | ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, | ||
82 | struct ieee80211_chanctx *ctx, | ||
83 | const struct cfg80211_chan_def *compat) | ||
84 | { | ||
85 | struct ieee80211_sub_if_data *sdata; | ||
86 | |||
87 | lockdep_assert_held(&local->chanctx_mtx); | ||
88 | |||
89 | list_for_each_entry(sdata, &ctx->reserved_vifs, | ||
90 | reserved_chanctx_list) { | ||
91 | if (!compat) | ||
92 | compat = &sdata->reserved_chandef; | ||
93 | |||
94 | compat = cfg80211_chandef_compatible(&sdata->reserved_chandef, | ||
95 | compat); | ||
96 | if (!compat) | ||
97 | break; | ||
98 | } | ||
99 | |||
100 | return compat; | ||
101 | } | ||
102 | |||
103 | static const struct cfg80211_chan_def * | ||
104 | ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, | ||
105 | struct ieee80211_chanctx *ctx, | ||
106 | const struct cfg80211_chan_def *compat) | ||
107 | { | ||
108 | struct ieee80211_sub_if_data *sdata; | ||
109 | |||
110 | lockdep_assert_held(&local->chanctx_mtx); | ||
111 | |||
112 | list_for_each_entry(sdata, &ctx->assigned_vifs, | ||
113 | assigned_chanctx_list) { | ||
114 | if (sdata->reserved_chanctx != NULL) | ||
115 | continue; | ||
116 | |||
117 | if (!compat) | ||
118 | compat = &sdata->vif.bss_conf.chandef; | ||
119 | |||
120 | compat = cfg80211_chandef_compatible( | ||
121 | &sdata->vif.bss_conf.chandef, compat); | ||
122 | if (!compat) | ||
123 | break; | ||
124 | } | ||
125 | |||
126 | return compat; | ||
127 | } | ||
128 | |||
129 | static const struct cfg80211_chan_def * | ||
130 | ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, | ||
131 | struct ieee80211_chanctx *ctx, | ||
132 | const struct cfg80211_chan_def *compat) | ||
133 | { | ||
134 | lockdep_assert_held(&local->chanctx_mtx); | ||
135 | |||
136 | compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); | ||
137 | if (!compat) | ||
138 | return NULL; | ||
139 | |||
140 | compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); | ||
141 | if (!compat) | ||
142 | return NULL; | ||
143 | |||
144 | return compat; | ||
145 | } | ||
146 | |||
147 | static bool | ||
148 | ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, | ||
149 | struct ieee80211_chanctx *ctx, | ||
150 | const struct cfg80211_chan_def *def) | ||
151 | { | ||
152 | lockdep_assert_held(&local->chanctx_mtx); | ||
153 | |||
154 | if (ieee80211_chanctx_combined_chandef(local, ctx, def)) | ||
155 | return true; | ||
156 | |||
157 | if (!list_empty(&ctx->reserved_vifs) && | ||
158 | ieee80211_chanctx_reserved_chandef(local, ctx, def)) | ||
159 | return true; | ||
160 | |||
161 | return false; | ||
162 | } | ||
163 | |||
164 | static struct ieee80211_chanctx * | ||
165 | ieee80211_find_reservation_chanctx(struct ieee80211_local *local, | ||
166 | const struct cfg80211_chan_def *chandef, | ||
167 | enum ieee80211_chanctx_mode mode) | ||
168 | { | ||
169 | struct ieee80211_chanctx *ctx; | ||
170 | |||
171 | lockdep_assert_held(&local->chanctx_mtx); | ||
172 | |||
173 | if (mode == IEEE80211_CHANCTX_EXCLUSIVE) | ||
174 | return NULL; | ||
175 | |||
176 | list_for_each_entry(ctx, &local->chanctx_list, list) { | ||
177 | if (ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) | ||
178 | continue; | ||
179 | |||
180 | if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) | ||
181 | continue; | ||
182 | |||
183 | if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, | ||
184 | chandef)) | ||
185 | continue; | ||
186 | |||
187 | return ctx; | ||
188 | } | ||
189 | |||
190 | return NULL; | ||
191 | } | ||
192 | |||
12 | static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) | 193 | static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) |
13 | { | 194 | { |
14 | switch (sta->bandwidth) { | 195 | switch (sta->bandwidth) { |
@@ -183,6 +364,9 @@ ieee80211_find_chanctx(struct ieee80211_local *local, | |||
183 | list_for_each_entry(ctx, &local->chanctx_list, list) { | 364 | list_for_each_entry(ctx, &local->chanctx_list, list) { |
184 | const struct cfg80211_chan_def *compat; | 365 | const struct cfg80211_chan_def *compat; |
185 | 366 | ||
367 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACE_NONE) | ||
368 | continue; | ||
369 | |||
186 | if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) | 370 | if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) |
187 | continue; | 371 | continue; |
188 | 372 | ||
@@ -190,6 +374,11 @@ ieee80211_find_chanctx(struct ieee80211_local *local, | |||
190 | if (!compat) | 374 | if (!compat) |
191 | continue; | 375 | continue; |
192 | 376 | ||
377 | compat = ieee80211_chanctx_reserved_chandef(local, ctx, | ||
378 | compat); | ||
379 | if (!compat) | ||
380 | continue; | ||
381 | |||
193 | ieee80211_change_chanctx(local, ctx, compat); | 382 | ieee80211_change_chanctx(local, ctx, compat); |
194 | 383 | ||
195 | return ctx; | 384 | return ctx; |
@@ -202,6 +391,8 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local) | |||
202 | { | 391 | { |
203 | struct ieee80211_sub_if_data *sdata; | 392 | struct ieee80211_sub_if_data *sdata; |
204 | 393 | ||
394 | lockdep_assert_held(&local->mtx); | ||
395 | |||
205 | rcu_read_lock(); | 396 | rcu_read_lock(); |
206 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { | 397 | list_for_each_entry_rcu(sdata, &local->interfaces, list) { |
207 | if (sdata->radar_required) { | 398 | if (sdata->radar_required) { |
@@ -215,62 +406,91 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local) | |||
215 | } | 406 | } |
216 | 407 | ||
217 | static struct ieee80211_chanctx * | 408 | static struct ieee80211_chanctx * |
218 | ieee80211_new_chanctx(struct ieee80211_local *local, | 409 | ieee80211_alloc_chanctx(struct ieee80211_local *local, |
219 | const struct cfg80211_chan_def *chandef, | 410 | const struct cfg80211_chan_def *chandef, |
220 | enum ieee80211_chanctx_mode mode) | 411 | enum ieee80211_chanctx_mode mode) |
221 | { | 412 | { |
222 | struct ieee80211_chanctx *ctx; | 413 | struct ieee80211_chanctx *ctx; |
223 | u32 changed; | ||
224 | int err; | ||
225 | 414 | ||
226 | lockdep_assert_held(&local->chanctx_mtx); | 415 | lockdep_assert_held(&local->chanctx_mtx); |
227 | 416 | ||
228 | ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); | 417 | ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); |
229 | if (!ctx) | 418 | if (!ctx) |
230 | return ERR_PTR(-ENOMEM); | 419 | return NULL; |
231 | 420 | ||
421 | INIT_LIST_HEAD(&ctx->assigned_vifs); | ||
422 | INIT_LIST_HEAD(&ctx->reserved_vifs); | ||
232 | ctx->conf.def = *chandef; | 423 | ctx->conf.def = *chandef; |
233 | ctx->conf.rx_chains_static = 1; | 424 | ctx->conf.rx_chains_static = 1; |
234 | ctx->conf.rx_chains_dynamic = 1; | 425 | ctx->conf.rx_chains_dynamic = 1; |
235 | ctx->mode = mode; | 426 | ctx->mode = mode; |
236 | ctx->conf.radar_enabled = ieee80211_is_radar_required(local); | 427 | ctx->conf.radar_enabled = ieee80211_is_radar_required(local); |
237 | ieee80211_recalc_chanctx_min_def(local, ctx); | 428 | ieee80211_recalc_chanctx_min_def(local, ctx); |
429 | |||
430 | return ctx; | ||
431 | } | ||
432 | |||
433 | static int ieee80211_add_chanctx(struct ieee80211_local *local, | ||
434 | struct ieee80211_chanctx *ctx) | ||
435 | { | ||
436 | u32 changed; | ||
437 | int err; | ||
438 | |||
439 | lockdep_assert_held(&local->mtx); | ||
440 | lockdep_assert_held(&local->chanctx_mtx); | ||
441 | |||
238 | if (!local->use_chanctx) | 442 | if (!local->use_chanctx) |
239 | local->hw.conf.radar_enabled = ctx->conf.radar_enabled; | 443 | local->hw.conf.radar_enabled = ctx->conf.radar_enabled; |
240 | 444 | ||
241 | /* we hold the mutex to prevent idle from changing */ | ||
242 | lockdep_assert_held(&local->mtx); | ||
243 | /* turn idle off *before* setting channel -- some drivers need that */ | 445 | /* turn idle off *before* setting channel -- some drivers need that */ |
244 | changed = ieee80211_idle_off(local); | 446 | changed = ieee80211_idle_off(local); |
245 | if (changed) | 447 | if (changed) |
246 | ieee80211_hw_config(local, changed); | 448 | ieee80211_hw_config(local, changed); |
247 | 449 | ||
248 | if (!local->use_chanctx) { | 450 | if (!local->use_chanctx) { |
249 | local->_oper_chandef = *chandef; | 451 | local->_oper_chandef = ctx->conf.def; |
250 | ieee80211_hw_config(local, 0); | 452 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
251 | } else { | 453 | } else { |
252 | err = drv_add_chanctx(local, ctx); | 454 | err = drv_add_chanctx(local, ctx); |
253 | if (err) { | 455 | if (err) { |
254 | kfree(ctx); | ||
255 | ieee80211_recalc_idle(local); | 456 | ieee80211_recalc_idle(local); |
256 | return ERR_PTR(err); | 457 | return err; |
257 | } | 458 | } |
258 | } | 459 | } |
259 | 460 | ||
260 | /* and keep the mutex held until the new chanctx is on the list */ | 461 | return 0; |
261 | list_add_rcu(&ctx->list, &local->chanctx_list); | 462 | } |
463 | |||
464 | static struct ieee80211_chanctx * | ||
465 | ieee80211_new_chanctx(struct ieee80211_local *local, | ||
466 | const struct cfg80211_chan_def *chandef, | ||
467 | enum ieee80211_chanctx_mode mode) | ||
468 | { | ||
469 | struct ieee80211_chanctx *ctx; | ||
470 | int err; | ||
471 | |||
472 | lockdep_assert_held(&local->mtx); | ||
473 | lockdep_assert_held(&local->chanctx_mtx); | ||
474 | |||
475 | ctx = ieee80211_alloc_chanctx(local, chandef, mode); | ||
476 | if (!ctx) | ||
477 | return ERR_PTR(-ENOMEM); | ||
478 | |||
479 | err = ieee80211_add_chanctx(local, ctx); | ||
480 | if (err) { | ||
481 | kfree(ctx); | ||
482 | return ERR_PTR(err); | ||
483 | } | ||
262 | 484 | ||
485 | list_add_rcu(&ctx->list, &local->chanctx_list); | ||
263 | return ctx; | 486 | return ctx; |
264 | } | 487 | } |
265 | 488 | ||
266 | static void ieee80211_free_chanctx(struct ieee80211_local *local, | 489 | static void ieee80211_del_chanctx(struct ieee80211_local *local, |
267 | struct ieee80211_chanctx *ctx) | 490 | struct ieee80211_chanctx *ctx) |
268 | { | 491 | { |
269 | bool check_single_channel = false; | ||
270 | lockdep_assert_held(&local->chanctx_mtx); | 492 | lockdep_assert_held(&local->chanctx_mtx); |
271 | 493 | ||
272 | WARN_ON_ONCE(ctx->refcount != 0); | ||
273 | |||
274 | if (!local->use_chanctx) { | 494 | if (!local->use_chanctx) { |
275 | struct cfg80211_chan_def *chandef = &local->_oper_chandef; | 495 | struct cfg80211_chan_def *chandef = &local->_oper_chandef; |
276 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; | 496 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; |
@@ -280,48 +500,29 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local, | |||
280 | /* NOTE: Disabling radar is only valid here for | 500 | /* NOTE: Disabling radar is only valid here for |
281 | * single channel context. To be sure, check it ... | 501 | * single channel context. To be sure, check it ... |
282 | */ | 502 | */ |
283 | if (local->hw.conf.radar_enabled) | 503 | WARN_ON(local->hw.conf.radar_enabled && |
284 | check_single_channel = true; | 504 | !list_empty(&local->chanctx_list)); |
505 | |||
285 | local->hw.conf.radar_enabled = false; | 506 | local->hw.conf.radar_enabled = false; |
286 | 507 | ||
287 | ieee80211_hw_config(local, 0); | 508 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
288 | } else { | 509 | } else { |
289 | drv_remove_chanctx(local, ctx); | 510 | drv_remove_chanctx(local, ctx); |
290 | } | 511 | } |
291 | 512 | ||
292 | list_del_rcu(&ctx->list); | ||
293 | kfree_rcu(ctx, rcu_head); | ||
294 | |||
295 | /* throw a warning if this wasn't the only channel context. */ | ||
296 | WARN_ON(check_single_channel && !list_empty(&local->chanctx_list)); | ||
297 | |||
298 | ieee80211_recalc_idle(local); | 513 | ieee80211_recalc_idle(local); |
299 | } | 514 | } |
300 | 515 | ||
301 | static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, | 516 | static void ieee80211_free_chanctx(struct ieee80211_local *local, |
302 | struct ieee80211_chanctx *ctx) | 517 | struct ieee80211_chanctx *ctx) |
303 | { | 518 | { |
304 | struct ieee80211_local *local = sdata->local; | ||
305 | int ret; | ||
306 | |||
307 | lockdep_assert_held(&local->chanctx_mtx); | 519 | lockdep_assert_held(&local->chanctx_mtx); |
308 | 520 | ||
309 | ret = drv_assign_vif_chanctx(local, sdata, ctx); | 521 | WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); |
310 | if (ret) | ||
311 | return ret; | ||
312 | 522 | ||
313 | rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); | 523 | list_del_rcu(&ctx->list); |
314 | ctx->refcount++; | 524 | ieee80211_del_chanctx(local, ctx); |
315 | 525 | kfree_rcu(ctx, rcu_head); | |
316 | ieee80211_recalc_txpower(sdata); | ||
317 | ieee80211_recalc_chanctx_min_def(local, ctx); | ||
318 | sdata->vif.bss_conf.idle = false; | ||
319 | |||
320 | if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && | ||
321 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | ||
322 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); | ||
323 | |||
324 | return 0; | ||
325 | } | 526 | } |
326 | 527 | ||
327 | static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, | 528 | static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, |
@@ -340,6 +541,8 @@ static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, | |||
340 | continue; | 541 | continue; |
341 | if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) | 542 | if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) |
342 | continue; | 543 | continue; |
544 | if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) | ||
545 | continue; | ||
343 | 546 | ||
344 | if (!compat) | 547 | if (!compat) |
345 | compat = &sdata->vif.bss_conf.chandef; | 548 | compat = &sdata->vif.bss_conf.chandef; |
@@ -382,30 +585,58 @@ static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, | |||
382 | drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); | 585 | drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); |
383 | } | 586 | } |
384 | 587 | ||
385 | static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, | 588 | static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, |
386 | struct ieee80211_chanctx *ctx) | 589 | struct ieee80211_chanctx *new_ctx) |
387 | { | 590 | { |
388 | struct ieee80211_local *local = sdata->local; | 591 | struct ieee80211_local *local = sdata->local; |
592 | struct ieee80211_chanctx_conf *conf; | ||
593 | struct ieee80211_chanctx *curr_ctx = NULL; | ||
594 | int ret = 0; | ||
389 | 595 | ||
390 | lockdep_assert_held(&local->chanctx_mtx); | 596 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
597 | lockdep_is_held(&local->chanctx_mtx)); | ||
391 | 598 | ||
392 | ctx->refcount--; | 599 | if (conf) { |
393 | rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); | 600 | curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); |
394 | 601 | ||
395 | sdata->vif.bss_conf.idle = true; | 602 | drv_unassign_vif_chanctx(local, sdata, curr_ctx); |
603 | conf = NULL; | ||
604 | list_del(&sdata->assigned_chanctx_list); | ||
605 | } | ||
396 | 606 | ||
397 | if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && | 607 | if (new_ctx) { |
398 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | 608 | ret = drv_assign_vif_chanctx(local, sdata, new_ctx); |
399 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); | 609 | if (ret) |
610 | goto out; | ||
400 | 611 | ||
401 | drv_unassign_vif_chanctx(local, sdata, ctx); | 612 | conf = &new_ctx->conf; |
613 | list_add(&sdata->assigned_chanctx_list, | ||
614 | &new_ctx->assigned_vifs); | ||
615 | } | ||
402 | 616 | ||
403 | if (ctx->refcount > 0) { | 617 | out: |
404 | ieee80211_recalc_chanctx_chantype(sdata->local, ctx); | 618 | rcu_assign_pointer(sdata->vif.chanctx_conf, conf); |
405 | ieee80211_recalc_smps_chanctx(local, ctx); | 619 | |
406 | ieee80211_recalc_radar_chanctx(local, ctx); | 620 | sdata->vif.bss_conf.idle = !conf; |
407 | ieee80211_recalc_chanctx_min_def(local, ctx); | 621 | |
622 | if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { | ||
623 | ieee80211_recalc_chanctx_chantype(local, curr_ctx); | ||
624 | ieee80211_recalc_smps_chanctx(local, curr_ctx); | ||
625 | ieee80211_recalc_radar_chanctx(local, curr_ctx); | ||
626 | ieee80211_recalc_chanctx_min_def(local, curr_ctx); | ||
627 | } | ||
628 | |||
629 | if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { | ||
630 | ieee80211_recalc_txpower(sdata); | ||
631 | ieee80211_recalc_chanctx_min_def(local, new_ctx); | ||
408 | } | 632 | } |
633 | |||
634 | if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && | ||
635 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | ||
636 | ieee80211_bss_info_change_notify(sdata, | ||
637 | BSS_CHANGED_IDLE); | ||
638 | |||
639 | return ret; | ||
409 | } | 640 | } |
410 | 641 | ||
411 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | 642 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) |
@@ -413,6 +644,7 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | |||
413 | struct ieee80211_local *local = sdata->local; | 644 | struct ieee80211_local *local = sdata->local; |
414 | struct ieee80211_chanctx_conf *conf; | 645 | struct ieee80211_chanctx_conf *conf; |
415 | struct ieee80211_chanctx *ctx; | 646 | struct ieee80211_chanctx *ctx; |
647 | bool use_reserved_switch = false; | ||
416 | 648 | ||
417 | lockdep_assert_held(&local->chanctx_mtx); | 649 | lockdep_assert_held(&local->chanctx_mtx); |
418 | 650 | ||
@@ -423,9 +655,23 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | |||
423 | 655 | ||
424 | ctx = container_of(conf, struct ieee80211_chanctx, conf); | 656 | ctx = container_of(conf, struct ieee80211_chanctx, conf); |
425 | 657 | ||
426 | ieee80211_unassign_vif_chanctx(sdata, ctx); | 658 | if (sdata->reserved_chanctx) { |
427 | if (ctx->refcount == 0) | 659 | if (sdata->reserved_chanctx->replace_state == |
660 | IEEE80211_CHANCTX_REPLACES_OTHER && | ||
661 | ieee80211_chanctx_num_reserved(local, | ||
662 | sdata->reserved_chanctx) > 1) | ||
663 | use_reserved_switch = true; | ||
664 | |||
665 | ieee80211_vif_unreserve_chanctx(sdata); | ||
666 | } | ||
667 | |||
668 | ieee80211_assign_vif_chanctx(sdata, NULL); | ||
669 | if (ieee80211_chanctx_refcount(local, ctx) == 0) | ||
428 | ieee80211_free_chanctx(local, ctx); | 670 | ieee80211_free_chanctx(local, ctx); |
671 | |||
672 | /* Unreserving may ready an in-place reservation. */ | ||
673 | if (use_reserved_switch) | ||
674 | ieee80211_vif_use_reserved_switch(local); | ||
429 | } | 675 | } |
430 | 676 | ||
431 | void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, | 677 | void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, |
@@ -490,6 +736,13 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, | |||
490 | rx_chains_static = max(rx_chains_static, needed_static); | 736 | rx_chains_static = max(rx_chains_static, needed_static); |
491 | rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); | 737 | rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic); |
492 | } | 738 | } |
739 | |||
740 | /* Disable SMPS for the monitor interface */ | ||
741 | sdata = rcu_dereference(local->monitor_sdata); | ||
742 | if (sdata && | ||
743 | rcu_access_pointer(sdata->vif.chanctx_conf) == &chanctx->conf) | ||
744 | rx_chains_dynamic = rx_chains_static = local->rx_chains; | ||
745 | |||
493 | rcu_read_unlock(); | 746 | rcu_read_unlock(); |
494 | 747 | ||
495 | if (!local->use_chanctx) { | 748 | if (!local->use_chanctx) { |
@@ -517,6 +770,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
517 | { | 770 | { |
518 | struct ieee80211_local *local = sdata->local; | 771 | struct ieee80211_local *local = sdata->local; |
519 | struct ieee80211_chanctx *ctx; | 772 | struct ieee80211_chanctx *ctx; |
773 | u8 radar_detect_width = 0; | ||
520 | int ret; | 774 | int ret; |
521 | 775 | ||
522 | lockdep_assert_held(&local->mtx); | 776 | lockdep_assert_held(&local->mtx); |
@@ -524,6 +778,22 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
524 | WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); | 778 | WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); |
525 | 779 | ||
526 | mutex_lock(&local->chanctx_mtx); | 780 | mutex_lock(&local->chanctx_mtx); |
781 | |||
782 | ret = cfg80211_chandef_dfs_required(local->hw.wiphy, | ||
783 | chandef, | ||
784 | sdata->wdev.iftype); | ||
785 | if (ret < 0) | ||
786 | goto out; | ||
787 | if (ret > 0) | ||
788 | radar_detect_width = BIT(chandef->width); | ||
789 | |||
790 | sdata->radar_required = ret; | ||
791 | |||
792 | ret = ieee80211_check_combinations(sdata, chandef, mode, | ||
793 | radar_detect_width); | ||
794 | if (ret < 0) | ||
795 | goto out; | ||
796 | |||
527 | __ieee80211_vif_release_channel(sdata); | 797 | __ieee80211_vif_release_channel(sdata); |
528 | 798 | ||
529 | ctx = ieee80211_find_chanctx(local, chandef, mode); | 799 | ctx = ieee80211_find_chanctx(local, chandef, mode); |
@@ -539,7 +809,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
539 | ret = ieee80211_assign_vif_chanctx(sdata, ctx); | 809 | ret = ieee80211_assign_vif_chanctx(sdata, ctx); |
540 | if (ret) { | 810 | if (ret) { |
541 | /* if assign fails refcount stays the same */ | 811 | /* if assign fails refcount stays the same */ |
542 | if (ctx->refcount == 0) | 812 | if (ieee80211_chanctx_refcount(local, ctx) == 0) |
543 | ieee80211_free_chanctx(local, ctx); | 813 | ieee80211_free_chanctx(local, ctx); |
544 | goto out; | 814 | goto out; |
545 | } | 815 | } |
@@ -551,60 +821,769 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
551 | return ret; | 821 | return ret; |
552 | } | 822 | } |
553 | 823 | ||
554 | int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | 824 | static void |
555 | u32 *changed) | 825 | __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, |
826 | bool clear) | ||
556 | { | 827 | { |
557 | struct ieee80211_local *local = sdata->local; | 828 | struct ieee80211_local *local __maybe_unused = sdata->local; |
829 | struct ieee80211_sub_if_data *vlan; | ||
558 | struct ieee80211_chanctx_conf *conf; | 830 | struct ieee80211_chanctx_conf *conf; |
559 | struct ieee80211_chanctx *ctx; | 831 | |
560 | const struct cfg80211_chan_def *chandef = &sdata->csa_chandef; | 832 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) |
561 | int ret; | 833 | return; |
562 | u32 chanctx_changed = 0; | ||
563 | 834 | ||
564 | lockdep_assert_held(&local->mtx); | 835 | lockdep_assert_held(&local->mtx); |
565 | 836 | ||
566 | /* should never be called if not performing a channel switch. */ | 837 | /* Check that conf exists, even when clearing this function |
567 | if (WARN_ON(!sdata->vif.csa_active)) | 838 | * must be called with the AP's channel context still there |
839 | * as it would otherwise cause VLANs to have an invalid | ||
840 | * channel context pointer for a while, possibly pointing | ||
841 | * to a channel context that has already been freed. | ||
842 | */ | ||
843 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
844 | lockdep_is_held(&local->chanctx_mtx)); | ||
845 | WARN_ON(!conf); | ||
846 | |||
847 | if (clear) | ||
848 | conf = NULL; | ||
849 | |||
850 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) | ||
851 | rcu_assign_pointer(vlan->vif.chanctx_conf, conf); | ||
852 | } | ||
853 | |||
854 | void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | ||
855 | bool clear) | ||
856 | { | ||
857 | struct ieee80211_local *local = sdata->local; | ||
858 | |||
859 | mutex_lock(&local->chanctx_mtx); | ||
860 | |||
861 | __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); | ||
862 | |||
863 | mutex_unlock(&local->chanctx_mtx); | ||
864 | } | ||
865 | |||
866 | int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) | ||
867 | { | ||
868 | struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; | ||
869 | |||
870 | lockdep_assert_held(&sdata->local->chanctx_mtx); | ||
871 | |||
872 | if (WARN_ON(!ctx)) | ||
568 | return -EINVAL; | 873 | return -EINVAL; |
569 | 874 | ||
570 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, | 875 | list_del(&sdata->reserved_chanctx_list); |
571 | IEEE80211_CHAN_DISABLED)) | 876 | sdata->reserved_chanctx = NULL; |
877 | |||
878 | if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) { | ||
879 | if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { | ||
880 | if (WARN_ON(!ctx->replace_ctx)) | ||
881 | return -EINVAL; | ||
882 | |||
883 | WARN_ON(ctx->replace_ctx->replace_state != | ||
884 | IEEE80211_CHANCTX_WILL_BE_REPLACED); | ||
885 | WARN_ON(ctx->replace_ctx->replace_ctx != ctx); | ||
886 | |||
887 | ctx->replace_ctx->replace_ctx = NULL; | ||
888 | ctx->replace_ctx->replace_state = | ||
889 | IEEE80211_CHANCTX_REPLACE_NONE; | ||
890 | |||
891 | list_del_rcu(&ctx->list); | ||
892 | kfree_rcu(ctx, rcu_head); | ||
893 | } else { | ||
894 | ieee80211_free_chanctx(sdata->local, ctx); | ||
895 | } | ||
896 | } | ||
897 | |||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, | ||
902 | const struct cfg80211_chan_def *chandef, | ||
903 | enum ieee80211_chanctx_mode mode, | ||
904 | bool radar_required) | ||
905 | { | ||
906 | struct ieee80211_local *local = sdata->local; | ||
907 | struct ieee80211_chanctx *new_ctx, *curr_ctx, *ctx; | ||
908 | |||
909 | lockdep_assert_held(&local->chanctx_mtx); | ||
910 | |||
911 | curr_ctx = ieee80211_vif_get_chanctx(sdata); | ||
912 | if (curr_ctx && local->use_chanctx && !local->ops->switch_vif_chanctx) | ||
913 | return -ENOTSUPP; | ||
914 | |||
915 | new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); | ||
916 | if (!new_ctx) { | ||
917 | if (ieee80211_can_create_new_chanctx(local)) { | ||
918 | new_ctx = ieee80211_new_chanctx(local, chandef, mode); | ||
919 | if (IS_ERR(new_ctx)) | ||
920 | return PTR_ERR(new_ctx); | ||
921 | } else { | ||
922 | if (!curr_ctx || | ||
923 | (curr_ctx->replace_state == | ||
924 | IEEE80211_CHANCTX_WILL_BE_REPLACED) || | ||
925 | !list_empty(&curr_ctx->reserved_vifs)) { | ||
926 | /* | ||
927 | * Another vif already requested this context | ||
928 | * for a reservation. Find another one hoping | ||
929 | * all vifs assigned to it will also switch | ||
930 | * soon enough. | ||
931 | * | ||
932 | * TODO: This needs a little more work as some | ||
933 | * cases (more than 2 chanctx capable devices) | ||
934 | * may fail which could otherwise succeed | ||
935 | * provided some channel context juggling was | ||
936 | * performed. | ||
937 | * | ||
938 | * Consider ctx1..3, vif1..6, each ctx has 2 | ||
939 | * vifs. vif1 and vif2 from ctx1 request new | ||
940 | * different chandefs starting 2 in-place | ||
941 | * reserations with ctx4 and ctx5 replacing | ||
942 | * ctx1 and ctx2 respectively. Next vif5 and | ||
943 | * vif6 from ctx3 reserve ctx4. If vif3 and | ||
944 | * vif4 remain on ctx2 as they are then this | ||
945 | * fails unless `replace_ctx` from ctx5 is | ||
946 | * replaced with ctx3. | ||
947 | */ | ||
948 | list_for_each_entry(ctx, &local->chanctx_list, | ||
949 | list) { | ||
950 | if (ctx->replace_state != | ||
951 | IEEE80211_CHANCTX_REPLACE_NONE) | ||
952 | continue; | ||
953 | |||
954 | if (!list_empty(&ctx->reserved_vifs)) | ||
955 | continue; | ||
956 | |||
957 | curr_ctx = ctx; | ||
958 | break; | ||
959 | } | ||
960 | } | ||
961 | |||
962 | /* | ||
963 | * If that's true then all available contexts already | ||
964 | * have reservations and cannot be used. | ||
965 | */ | ||
966 | if (!curr_ctx || | ||
967 | (curr_ctx->replace_state == | ||
968 | IEEE80211_CHANCTX_WILL_BE_REPLACED) || | ||
969 | !list_empty(&curr_ctx->reserved_vifs)) | ||
970 | return -EBUSY; | ||
971 | |||
972 | new_ctx = ieee80211_alloc_chanctx(local, chandef, mode); | ||
973 | if (!new_ctx) | ||
974 | return -ENOMEM; | ||
975 | |||
976 | new_ctx->replace_ctx = curr_ctx; | ||
977 | new_ctx->replace_state = | ||
978 | IEEE80211_CHANCTX_REPLACES_OTHER; | ||
979 | |||
980 | curr_ctx->replace_ctx = new_ctx; | ||
981 | curr_ctx->replace_state = | ||
982 | IEEE80211_CHANCTX_WILL_BE_REPLACED; | ||
983 | |||
984 | list_add_rcu(&new_ctx->list, &local->chanctx_list); | ||
985 | } | ||
986 | } | ||
987 | |||
988 | list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); | ||
989 | sdata->reserved_chanctx = new_ctx; | ||
990 | sdata->reserved_chandef = *chandef; | ||
991 | sdata->reserved_radar_required = radar_required; | ||
992 | sdata->reserved_ready = false; | ||
993 | |||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | static void | ||
998 | ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata) | ||
999 | { | ||
1000 | switch (sdata->vif.type) { | ||
1001 | case NL80211_IFTYPE_ADHOC: | ||
1002 | case NL80211_IFTYPE_AP: | ||
1003 | case NL80211_IFTYPE_MESH_POINT: | ||
1004 | ieee80211_queue_work(&sdata->local->hw, | ||
1005 | &sdata->csa_finalize_work); | ||
1006 | break; | ||
1007 | case NL80211_IFTYPE_STATION: | ||
1008 | ieee80211_queue_work(&sdata->local->hw, | ||
1009 | &sdata->u.mgd.chswitch_work); | ||
1010 | break; | ||
1011 | case NL80211_IFTYPE_UNSPECIFIED: | ||
1012 | case NL80211_IFTYPE_AP_VLAN: | ||
1013 | case NL80211_IFTYPE_WDS: | ||
1014 | case NL80211_IFTYPE_MONITOR: | ||
1015 | case NL80211_IFTYPE_P2P_CLIENT: | ||
1016 | case NL80211_IFTYPE_P2P_GO: | ||
1017 | case NL80211_IFTYPE_P2P_DEVICE: | ||
1018 | case NUM_NL80211_IFTYPES: | ||
1019 | WARN_ON(1); | ||
1020 | break; | ||
1021 | } | ||
1022 | } | ||
1023 | |||
1024 | static int | ||
1025 | ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata) | ||
1026 | { | ||
1027 | struct ieee80211_local *local = sdata->local; | ||
1028 | struct ieee80211_vif_chanctx_switch vif_chsw[1] = {}; | ||
1029 | struct ieee80211_chanctx *old_ctx, *new_ctx; | ||
1030 | const struct cfg80211_chan_def *chandef; | ||
1031 | u32 changed = 0; | ||
1032 | int err; | ||
1033 | |||
1034 | lockdep_assert_held(&local->mtx); | ||
1035 | lockdep_assert_held(&local->chanctx_mtx); | ||
1036 | |||
1037 | new_ctx = sdata->reserved_chanctx; | ||
1038 | old_ctx = ieee80211_vif_get_chanctx(sdata); | ||
1039 | |||
1040 | if (WARN_ON(!sdata->reserved_ready)) | ||
1041 | return -EBUSY; | ||
1042 | |||
1043 | if (WARN_ON(!new_ctx)) | ||
572 | return -EINVAL; | 1044 | return -EINVAL; |
573 | 1045 | ||
574 | mutex_lock(&local->chanctx_mtx); | 1046 | if (WARN_ON(!old_ctx)) |
575 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | 1047 | return -EINVAL; |
576 | lockdep_is_held(&local->chanctx_mtx)); | 1048 | |
577 | if (!conf) { | 1049 | if (WARN_ON(new_ctx->replace_state == |
578 | ret = -EINVAL; | 1050 | IEEE80211_CHANCTX_REPLACES_OTHER)) |
1051 | return -EINVAL; | ||
1052 | |||
1053 | chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, | ||
1054 | &sdata->reserved_chandef); | ||
1055 | if (WARN_ON(!chandef)) | ||
1056 | return -EINVAL; | ||
1057 | |||
1058 | vif_chsw[0].vif = &sdata->vif; | ||
1059 | vif_chsw[0].old_ctx = &old_ctx->conf; | ||
1060 | vif_chsw[0].new_ctx = &new_ctx->conf; | ||
1061 | |||
1062 | list_del(&sdata->reserved_chanctx_list); | ||
1063 | sdata->reserved_chanctx = NULL; | ||
1064 | |||
1065 | err = drv_switch_vif_chanctx(local, vif_chsw, 1, | ||
1066 | CHANCTX_SWMODE_REASSIGN_VIF); | ||
1067 | if (err) { | ||
1068 | if (ieee80211_chanctx_refcount(local, new_ctx) == 0) | ||
1069 | ieee80211_free_chanctx(local, new_ctx); | ||
1070 | |||
579 | goto out; | 1071 | goto out; |
580 | } | 1072 | } |
581 | 1073 | ||
582 | ctx = container_of(conf, struct ieee80211_chanctx, conf); | 1074 | list_move(&sdata->assigned_chanctx_list, &new_ctx->assigned_vifs); |
583 | if (ctx->refcount != 1) { | 1075 | rcu_assign_pointer(sdata->vif.chanctx_conf, &new_ctx->conf); |
584 | ret = -EINVAL; | 1076 | |
1077 | if (sdata->vif.type == NL80211_IFTYPE_AP) | ||
1078 | __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); | ||
1079 | |||
1080 | if (ieee80211_chanctx_refcount(local, old_ctx) == 0) | ||
1081 | ieee80211_free_chanctx(local, old_ctx); | ||
1082 | |||
1083 | if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) | ||
1084 | changed = BSS_CHANGED_BANDWIDTH; | ||
1085 | |||
1086 | sdata->vif.bss_conf.chandef = sdata->reserved_chandef; | ||
1087 | |||
1088 | if (changed) | ||
1089 | ieee80211_bss_info_change_notify(sdata, changed); | ||
1090 | |||
1091 | out: | ||
1092 | ieee80211_vif_chanctx_reservation_complete(sdata); | ||
1093 | return err; | ||
1094 | } | ||
1095 | |||
1096 | static int | ||
1097 | ieee80211_vif_use_reserved_assign(struct ieee80211_sub_if_data *sdata) | ||
1098 | { | ||
1099 | struct ieee80211_local *local = sdata->local; | ||
1100 | struct ieee80211_chanctx *old_ctx, *new_ctx; | ||
1101 | const struct cfg80211_chan_def *chandef; | ||
1102 | int err; | ||
1103 | |||
1104 | old_ctx = ieee80211_vif_get_chanctx(sdata); | ||
1105 | new_ctx = sdata->reserved_chanctx; | ||
1106 | |||
1107 | if (WARN_ON(!sdata->reserved_ready)) | ||
1108 | return -EINVAL; | ||
1109 | |||
1110 | if (WARN_ON(old_ctx)) | ||
1111 | return -EINVAL; | ||
1112 | |||
1113 | if (WARN_ON(!new_ctx)) | ||
1114 | return -EINVAL; | ||
1115 | |||
1116 | if (WARN_ON(new_ctx->replace_state == | ||
1117 | IEEE80211_CHANCTX_REPLACES_OTHER)) | ||
1118 | return -EINVAL; | ||
1119 | |||
1120 | chandef = ieee80211_chanctx_non_reserved_chandef(local, new_ctx, | ||
1121 | &sdata->reserved_chandef); | ||
1122 | if (WARN_ON(!chandef)) | ||
1123 | return -EINVAL; | ||
1124 | |||
1125 | list_del(&sdata->reserved_chanctx_list); | ||
1126 | sdata->reserved_chanctx = NULL; | ||
1127 | |||
1128 | err = ieee80211_assign_vif_chanctx(sdata, new_ctx); | ||
1129 | if (err) { | ||
1130 | if (ieee80211_chanctx_refcount(local, new_ctx) == 0) | ||
1131 | ieee80211_free_chanctx(local, new_ctx); | ||
1132 | |||
585 | goto out; | 1133 | goto out; |
586 | } | 1134 | } |
587 | 1135 | ||
588 | if (sdata->vif.bss_conf.chandef.width != chandef->width) { | 1136 | out: |
589 | chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; | 1137 | ieee80211_vif_chanctx_reservation_complete(sdata); |
590 | *changed |= BSS_CHANGED_BANDWIDTH; | 1138 | return err; |
1139 | } | ||
1140 | |||
1141 | static bool | ||
1142 | ieee80211_vif_has_in_place_reservation(struct ieee80211_sub_if_data *sdata) | ||
1143 | { | ||
1144 | struct ieee80211_chanctx *old_ctx, *new_ctx; | ||
1145 | |||
1146 | lockdep_assert_held(&sdata->local->chanctx_mtx); | ||
1147 | |||
1148 | new_ctx = sdata->reserved_chanctx; | ||
1149 | old_ctx = ieee80211_vif_get_chanctx(sdata); | ||
1150 | |||
1151 | if (!old_ctx) | ||
1152 | return false; | ||
1153 | |||
1154 | if (WARN_ON(!new_ctx)) | ||
1155 | return false; | ||
1156 | |||
1157 | if (old_ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) | ||
1158 | return false; | ||
1159 | |||
1160 | if (new_ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1161 | return false; | ||
1162 | |||
1163 | return true; | ||
1164 | } | ||
1165 | |||
1166 | static int ieee80211_chsw_switch_hwconf(struct ieee80211_local *local, | ||
1167 | struct ieee80211_chanctx *new_ctx) | ||
1168 | { | ||
1169 | const struct cfg80211_chan_def *chandef; | ||
1170 | |||
1171 | lockdep_assert_held(&local->mtx); | ||
1172 | lockdep_assert_held(&local->chanctx_mtx); | ||
1173 | |||
1174 | chandef = ieee80211_chanctx_reserved_chandef(local, new_ctx, NULL); | ||
1175 | if (WARN_ON(!chandef)) | ||
1176 | return -EINVAL; | ||
1177 | |||
1178 | local->hw.conf.radar_enabled = new_ctx->conf.radar_enabled; | ||
1179 | local->_oper_chandef = *chandef; | ||
1180 | ieee80211_hw_config(local, 0); | ||
1181 | |||
1182 | return 0; | ||
1183 | } | ||
1184 | |||
1185 | static int ieee80211_chsw_switch_vifs(struct ieee80211_local *local, | ||
1186 | int n_vifs) | ||
1187 | { | ||
1188 | struct ieee80211_vif_chanctx_switch *vif_chsw; | ||
1189 | struct ieee80211_sub_if_data *sdata; | ||
1190 | struct ieee80211_chanctx *ctx, *old_ctx; | ||
1191 | int i, err; | ||
1192 | |||
1193 | lockdep_assert_held(&local->mtx); | ||
1194 | lockdep_assert_held(&local->chanctx_mtx); | ||
1195 | |||
1196 | vif_chsw = kzalloc(sizeof(vif_chsw[0]) * n_vifs, GFP_KERNEL); | ||
1197 | if (!vif_chsw) | ||
1198 | return -ENOMEM; | ||
1199 | |||
1200 | i = 0; | ||
1201 | list_for_each_entry(ctx, &local->chanctx_list, list) { | ||
1202 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1203 | continue; | ||
1204 | |||
1205 | if (WARN_ON(!ctx->replace_ctx)) { | ||
1206 | err = -EINVAL; | ||
1207 | goto out; | ||
1208 | } | ||
1209 | |||
1210 | list_for_each_entry(sdata, &ctx->reserved_vifs, | ||
1211 | reserved_chanctx_list) { | ||
1212 | if (!ieee80211_vif_has_in_place_reservation( | ||
1213 | sdata)) | ||
1214 | continue; | ||
1215 | |||
1216 | old_ctx = ieee80211_vif_get_chanctx(sdata); | ||
1217 | vif_chsw[i].vif = &sdata->vif; | ||
1218 | vif_chsw[i].old_ctx = &old_ctx->conf; | ||
1219 | vif_chsw[i].new_ctx = &ctx->conf; | ||
1220 | |||
1221 | i++; | ||
1222 | } | ||
591 | } | 1223 | } |
592 | 1224 | ||
593 | sdata->vif.bss_conf.chandef = *chandef; | 1225 | err = drv_switch_vif_chanctx(local, vif_chsw, n_vifs, |
594 | ctx->conf.def = *chandef; | 1226 | CHANCTX_SWMODE_SWAP_CONTEXTS); |
595 | 1227 | ||
596 | chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; | 1228 | out: |
597 | drv_change_chanctx(local, ctx, chanctx_changed); | 1229 | kfree(vif_chsw); |
1230 | return err; | ||
1231 | } | ||
598 | 1232 | ||
599 | ieee80211_recalc_chanctx_chantype(local, ctx); | 1233 | static int ieee80211_chsw_switch_ctxs(struct ieee80211_local *local) |
600 | ieee80211_recalc_smps_chanctx(local, ctx); | 1234 | { |
601 | ieee80211_recalc_radar_chanctx(local, ctx); | 1235 | struct ieee80211_chanctx *ctx; |
602 | ieee80211_recalc_chanctx_min_def(local, ctx); | 1236 | int err; |
603 | 1237 | ||
604 | ret = 0; | 1238 | lockdep_assert_held(&local->mtx); |
605 | out: | 1239 | lockdep_assert_held(&local->chanctx_mtx); |
606 | mutex_unlock(&local->chanctx_mtx); | 1240 | |
607 | return ret; | 1241 | list_for_each_entry(ctx, &local->chanctx_list, list) { |
1242 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1243 | continue; | ||
1244 | |||
1245 | if (!list_empty(&ctx->replace_ctx->assigned_vifs)) | ||
1246 | continue; | ||
1247 | |||
1248 | ieee80211_del_chanctx(local, ctx->replace_ctx); | ||
1249 | err = ieee80211_add_chanctx(local, ctx); | ||
1250 | if (err) | ||
1251 | goto err; | ||
1252 | } | ||
1253 | |||
1254 | return 0; | ||
1255 | |||
1256 | err: | ||
1257 | WARN_ON(ieee80211_add_chanctx(local, ctx)); | ||
1258 | list_for_each_entry_continue_reverse(ctx, &local->chanctx_list, list) { | ||
1259 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1260 | continue; | ||
1261 | |||
1262 | if (!list_empty(&ctx->replace_ctx->assigned_vifs)) | ||
1263 | continue; | ||
1264 | |||
1265 | ieee80211_del_chanctx(local, ctx); | ||
1266 | WARN_ON(ieee80211_add_chanctx(local, ctx->replace_ctx)); | ||
1267 | } | ||
1268 | |||
1269 | return err; | ||
1270 | } | ||
1271 | |||
1272 | int | ||
1273 | ieee80211_vif_use_reserved_switch(struct ieee80211_local *local) | ||
1274 | { | ||
1275 | struct ieee80211_sub_if_data *sdata, *sdata_tmp; | ||
1276 | struct ieee80211_chanctx *ctx, *ctx_tmp, *old_ctx; | ||
1277 | struct ieee80211_chanctx *new_ctx = NULL; | ||
1278 | int i, err, n_assigned, n_reserved, n_ready; | ||
1279 | int n_ctx = 0, n_vifs_switch = 0, n_vifs_assign = 0, n_vifs_ctxless = 0; | ||
1280 | |||
1281 | lockdep_assert_held(&local->mtx); | ||
1282 | lockdep_assert_held(&local->chanctx_mtx); | ||
1283 | |||
1284 | /* | ||
1285 | * If there are 2 independent pairs of channel contexts performing | ||
1286 | * cross-switch of their vifs this code will still wait until both are | ||
1287 | * ready even though it could be possible to switch one before the | ||
1288 | * other is ready. | ||
1289 | * | ||
1290 | * For practical reasons and code simplicity just do a single huge | ||
1291 | * switch. | ||
1292 | */ | ||
1293 | |||
1294 | /* | ||
1295 | * Verify if the reservation is still feasible. | ||
1296 | * - if it's not then disconnect | ||
1297 | * - if it is but not all vifs necessary are ready then defer | ||
1298 | */ | ||
1299 | |||
1300 | list_for_each_entry(ctx, &local->chanctx_list, list) { | ||
1301 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1302 | continue; | ||
1303 | |||
1304 | if (WARN_ON(!ctx->replace_ctx)) { | ||
1305 | err = -EINVAL; | ||
1306 | goto err; | ||
1307 | } | ||
1308 | |||
1309 | if (!local->use_chanctx) | ||
1310 | new_ctx = ctx; | ||
1311 | |||
1312 | n_ctx++; | ||
1313 | |||
1314 | n_assigned = 0; | ||
1315 | n_reserved = 0; | ||
1316 | n_ready = 0; | ||
1317 | |||
1318 | list_for_each_entry(sdata, &ctx->replace_ctx->assigned_vifs, | ||
1319 | assigned_chanctx_list) { | ||
1320 | n_assigned++; | ||
1321 | if (sdata->reserved_chanctx) { | ||
1322 | n_reserved++; | ||
1323 | if (sdata->reserved_ready) | ||
1324 | n_ready++; | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | if (n_assigned != n_reserved) { | ||
1329 | if (n_ready == n_reserved) { | ||
1330 | wiphy_info(local->hw.wiphy, | ||
1331 | "channel context reservation cannot be finalized because some interfaces aren't switching\n"); | ||
1332 | err = -EBUSY; | ||
1333 | goto err; | ||
1334 | } | ||
1335 | |||
1336 | return -EAGAIN; | ||
1337 | } | ||
1338 | |||
1339 | ctx->conf.radar_enabled = false; | ||
1340 | list_for_each_entry(sdata, &ctx->reserved_vifs, | ||
1341 | reserved_chanctx_list) { | ||
1342 | if (ieee80211_vif_has_in_place_reservation(sdata) && | ||
1343 | !sdata->reserved_ready) | ||
1344 | return -EAGAIN; | ||
1345 | |||
1346 | old_ctx = ieee80211_vif_get_chanctx(sdata); | ||
1347 | if (old_ctx) { | ||
1348 | if (old_ctx->replace_state == | ||
1349 | IEEE80211_CHANCTX_WILL_BE_REPLACED) | ||
1350 | n_vifs_switch++; | ||
1351 | else | ||
1352 | n_vifs_assign++; | ||
1353 | } else { | ||
1354 | n_vifs_ctxless++; | ||
1355 | } | ||
1356 | |||
1357 | if (sdata->reserved_radar_required) | ||
1358 | ctx->conf.radar_enabled = true; | ||
1359 | } | ||
1360 | } | ||
1361 | |||
1362 | if (WARN_ON(n_ctx == 0) || | ||
1363 | WARN_ON(n_vifs_switch == 0 && | ||
1364 | n_vifs_assign == 0 && | ||
1365 | n_vifs_ctxless == 0) || | ||
1366 | WARN_ON(n_ctx > 1 && !local->use_chanctx) || | ||
1367 | WARN_ON(!new_ctx && !local->use_chanctx)) { | ||
1368 | err = -EINVAL; | ||
1369 | goto err; | ||
1370 | } | ||
1371 | |||
1372 | /* | ||
1373 | * All necessary vifs are ready. Perform the switch now depending on | ||
1374 | * reservations and driver capabilities. | ||
1375 | */ | ||
1376 | |||
1377 | if (local->use_chanctx) { | ||
1378 | if (n_vifs_switch > 0) { | ||
1379 | err = ieee80211_chsw_switch_vifs(local, n_vifs_switch); | ||
1380 | if (err) | ||
1381 | goto err; | ||
1382 | } | ||
1383 | |||
1384 | if (n_vifs_assign > 0 || n_vifs_ctxless > 0) { | ||
1385 | err = ieee80211_chsw_switch_ctxs(local); | ||
1386 | if (err) | ||
1387 | goto err; | ||
1388 | } | ||
1389 | } else { | ||
1390 | err = ieee80211_chsw_switch_hwconf(local, new_ctx); | ||
1391 | if (err) | ||
1392 | goto err; | ||
1393 | } | ||
1394 | |||
1395 | /* | ||
1396 | * Update all structures, values and pointers to point to new channel | ||
1397 | * context(s). | ||
1398 | */ | ||
1399 | |||
1400 | i = 0; | ||
1401 | list_for_each_entry(ctx, &local->chanctx_list, list) { | ||
1402 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1403 | continue; | ||
1404 | |||
1405 | if (WARN_ON(!ctx->replace_ctx)) { | ||
1406 | err = -EINVAL; | ||
1407 | goto err; | ||
1408 | } | ||
1409 | |||
1410 | list_for_each_entry(sdata, &ctx->reserved_vifs, | ||
1411 | reserved_chanctx_list) { | ||
1412 | u32 changed = 0; | ||
1413 | |||
1414 | if (!ieee80211_vif_has_in_place_reservation(sdata)) | ||
1415 | continue; | ||
1416 | |||
1417 | rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); | ||
1418 | |||
1419 | if (sdata->vif.type == NL80211_IFTYPE_AP) | ||
1420 | __ieee80211_vif_copy_chanctx_to_vlans(sdata, | ||
1421 | false); | ||
1422 | |||
1423 | sdata->radar_required = sdata->reserved_radar_required; | ||
1424 | |||
1425 | if (sdata->vif.bss_conf.chandef.width != | ||
1426 | sdata->reserved_chandef.width) | ||
1427 | changed = BSS_CHANGED_BANDWIDTH; | ||
1428 | |||
1429 | sdata->vif.bss_conf.chandef = sdata->reserved_chandef; | ||
1430 | if (changed) | ||
1431 | ieee80211_bss_info_change_notify(sdata, | ||
1432 | changed); | ||
1433 | |||
1434 | ieee80211_recalc_txpower(sdata); | ||
1435 | } | ||
1436 | |||
1437 | ieee80211_recalc_chanctx_chantype(local, ctx); | ||
1438 | ieee80211_recalc_smps_chanctx(local, ctx); | ||
1439 | ieee80211_recalc_radar_chanctx(local, ctx); | ||
1440 | ieee80211_recalc_chanctx_min_def(local, ctx); | ||
1441 | |||
1442 | list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, | ||
1443 | reserved_chanctx_list) { | ||
1444 | if (ieee80211_vif_get_chanctx(sdata) != ctx) | ||
1445 | continue; | ||
1446 | |||
1447 | list_del(&sdata->reserved_chanctx_list); | ||
1448 | list_move(&sdata->assigned_chanctx_list, | ||
1449 | &ctx->assigned_vifs); | ||
1450 | sdata->reserved_chanctx = NULL; | ||
1451 | |||
1452 | ieee80211_vif_chanctx_reservation_complete(sdata); | ||
1453 | } | ||
1454 | |||
1455 | /* | ||
1456 | * This context might have been a dependency for an already | ||
1457 | * ready re-assign reservation interface that was deferred. Do | ||
1458 | * not propagate error to the caller though. The in-place | ||
1459 | * reservation for originally requested interface has already | ||
1460 | * succeeded at this point. | ||
1461 | */ | ||
1462 | list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, | ||
1463 | reserved_chanctx_list) { | ||
1464 | if (WARN_ON(ieee80211_vif_has_in_place_reservation( | ||
1465 | sdata))) | ||
1466 | continue; | ||
1467 | |||
1468 | if (WARN_ON(sdata->reserved_chanctx != ctx)) | ||
1469 | continue; | ||
1470 | |||
1471 | if (!sdata->reserved_ready) | ||
1472 | continue; | ||
1473 | |||
1474 | if (ieee80211_vif_get_chanctx(sdata)) | ||
1475 | err = ieee80211_vif_use_reserved_reassign( | ||
1476 | sdata); | ||
1477 | else | ||
1478 | err = ieee80211_vif_use_reserved_assign(sdata); | ||
1479 | |||
1480 | if (err) { | ||
1481 | sdata_info(sdata, | ||
1482 | "failed to finalize (re-)assign reservation (err=%d)\n", | ||
1483 | err); | ||
1484 | ieee80211_vif_unreserve_chanctx(sdata); | ||
1485 | cfg80211_stop_iface(local->hw.wiphy, | ||
1486 | &sdata->wdev, | ||
1487 | GFP_KERNEL); | ||
1488 | } | ||
1489 | } | ||
1490 | } | ||
1491 | |||
1492 | /* | ||
1493 | * Finally free old contexts | ||
1494 | */ | ||
1495 | |||
1496 | list_for_each_entry_safe(ctx, ctx_tmp, &local->chanctx_list, list) { | ||
1497 | if (ctx->replace_state != IEEE80211_CHANCTX_WILL_BE_REPLACED) | ||
1498 | continue; | ||
1499 | |||
1500 | ctx->replace_ctx->replace_ctx = NULL; | ||
1501 | ctx->replace_ctx->replace_state = | ||
1502 | IEEE80211_CHANCTX_REPLACE_NONE; | ||
1503 | |||
1504 | list_del_rcu(&ctx->list); | ||
1505 | kfree_rcu(ctx, rcu_head); | ||
1506 | } | ||
1507 | |||
1508 | return 0; | ||
1509 | |||
1510 | err: | ||
1511 | list_for_each_entry(ctx, &local->chanctx_list, list) { | ||
1512 | if (ctx->replace_state != IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1513 | continue; | ||
1514 | |||
1515 | list_for_each_entry_safe(sdata, sdata_tmp, &ctx->reserved_vifs, | ||
1516 | reserved_chanctx_list) { | ||
1517 | ieee80211_vif_unreserve_chanctx(sdata); | ||
1518 | ieee80211_vif_chanctx_reservation_complete(sdata); | ||
1519 | } | ||
1520 | } | ||
1521 | |||
1522 | return err; | ||
1523 | } | ||
1524 | |||
1525 | int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata) | ||
1526 | { | ||
1527 | struct ieee80211_local *local = sdata->local; | ||
1528 | struct ieee80211_chanctx *new_ctx; | ||
1529 | struct ieee80211_chanctx *old_ctx; | ||
1530 | int err; | ||
1531 | |||
1532 | lockdep_assert_held(&local->mtx); | ||
1533 | lockdep_assert_held(&local->chanctx_mtx); | ||
1534 | |||
1535 | new_ctx = sdata->reserved_chanctx; | ||
1536 | old_ctx = ieee80211_vif_get_chanctx(sdata); | ||
1537 | |||
1538 | if (WARN_ON(!new_ctx)) | ||
1539 | return -EINVAL; | ||
1540 | |||
1541 | if (WARN_ON(new_ctx->replace_state == | ||
1542 | IEEE80211_CHANCTX_WILL_BE_REPLACED)) | ||
1543 | return -EINVAL; | ||
1544 | |||
1545 | if (WARN_ON(sdata->reserved_ready)) | ||
1546 | return -EINVAL; | ||
1547 | |||
1548 | sdata->reserved_ready = true; | ||
1549 | |||
1550 | if (new_ctx->replace_state == IEEE80211_CHANCTX_REPLACE_NONE) { | ||
1551 | if (old_ctx) | ||
1552 | err = ieee80211_vif_use_reserved_reassign(sdata); | ||
1553 | else | ||
1554 | err = ieee80211_vif_use_reserved_assign(sdata); | ||
1555 | |||
1556 | if (err) | ||
1557 | return err; | ||
1558 | } | ||
1559 | |||
1560 | /* | ||
1561 | * In-place reservation may need to be finalized now either if: | ||
1562 | * a) sdata is taking part in the swapping itself and is the last one | ||
1563 | * b) sdata has switched with a re-assign reservation to an existing | ||
1564 | * context readying in-place switching of old_ctx | ||
1565 | * | ||
1566 | * In case of (b) do not propagate the error up because the requested | ||
1567 | * sdata already switched successfully. Just spill an extra warning. | ||
1568 | * The ieee80211_vif_use_reserved_switch() already stops all necessary | ||
1569 | * interfaces upon failure. | ||
1570 | */ | ||
1571 | if ((old_ctx && | ||
1572 | old_ctx->replace_state == IEEE80211_CHANCTX_WILL_BE_REPLACED) || | ||
1573 | new_ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER) { | ||
1574 | err = ieee80211_vif_use_reserved_switch(local); | ||
1575 | if (err && err != -EAGAIN) { | ||
1576 | if (new_ctx->replace_state == | ||
1577 | IEEE80211_CHANCTX_REPLACES_OTHER) | ||
1578 | return err; | ||
1579 | |||
1580 | wiphy_info(local->hw.wiphy, | ||
1581 | "depending in-place reservation failed (err=%d)\n", | ||
1582 | err); | ||
1583 | } | ||
1584 | } | ||
1585 | |||
1586 | return 0; | ||
608 | } | 1587 | } |
609 | 1588 | ||
610 | int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, | 1589 | int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, |
@@ -614,6 +1593,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, | |||
614 | struct ieee80211_local *local = sdata->local; | 1593 | struct ieee80211_local *local = sdata->local; |
615 | struct ieee80211_chanctx_conf *conf; | 1594 | struct ieee80211_chanctx_conf *conf; |
616 | struct ieee80211_chanctx *ctx; | 1595 | struct ieee80211_chanctx *ctx; |
1596 | const struct cfg80211_chan_def *compat; | ||
617 | int ret; | 1597 | int ret; |
618 | 1598 | ||
619 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, | 1599 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, |
@@ -640,11 +1620,33 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata, | |||
640 | } | 1620 | } |
641 | 1621 | ||
642 | ctx = container_of(conf, struct ieee80211_chanctx, conf); | 1622 | ctx = container_of(conf, struct ieee80211_chanctx, conf); |
643 | if (!cfg80211_chandef_compatible(&conf->def, chandef)) { | 1623 | |
1624 | compat = cfg80211_chandef_compatible(&conf->def, chandef); | ||
1625 | if (!compat) { | ||
644 | ret = -EINVAL; | 1626 | ret = -EINVAL; |
645 | goto out; | 1627 | goto out; |
646 | } | 1628 | } |
647 | 1629 | ||
1630 | switch (ctx->replace_state) { | ||
1631 | case IEEE80211_CHANCTX_REPLACE_NONE: | ||
1632 | if (!ieee80211_chanctx_reserved_chandef(local, ctx, compat)) { | ||
1633 | ret = -EBUSY; | ||
1634 | goto out; | ||
1635 | } | ||
1636 | break; | ||
1637 | case IEEE80211_CHANCTX_WILL_BE_REPLACED: | ||
1638 | /* TODO: Perhaps the bandwith change could be treated as a | ||
1639 | * reservation itself? */ | ||
1640 | ret = -EBUSY; | ||
1641 | goto out; | ||
1642 | case IEEE80211_CHANCTX_REPLACES_OTHER: | ||
1643 | /* channel context that is going to replace another channel | ||
1644 | * context doesn't really exist and shouldn't be assigned | ||
1645 | * anywhere yet */ | ||
1646 | WARN_ON(1); | ||
1647 | break; | ||
1648 | } | ||
1649 | |||
648 | sdata->vif.bss_conf.chandef = *chandef; | 1650 | sdata->vif.bss_conf.chandef = *chandef; |
649 | 1651 | ||
650 | ieee80211_recalc_chanctx_chantype(local, ctx); | 1652 | ieee80211_recalc_chanctx_chantype(local, ctx); |
@@ -686,40 +1688,6 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) | |||
686 | mutex_unlock(&local->chanctx_mtx); | 1688 | mutex_unlock(&local->chanctx_mtx); |
687 | } | 1689 | } |
688 | 1690 | ||
689 | void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | ||
690 | bool clear) | ||
691 | { | ||
692 | struct ieee80211_local *local = sdata->local; | ||
693 | struct ieee80211_sub_if_data *vlan; | ||
694 | struct ieee80211_chanctx_conf *conf; | ||
695 | |||
696 | ASSERT_RTNL(); | ||
697 | |||
698 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) | ||
699 | return; | ||
700 | |||
701 | mutex_lock(&local->chanctx_mtx); | ||
702 | |||
703 | /* | ||
704 | * Check that conf exists, even when clearing this function | ||
705 | * must be called with the AP's channel context still there | ||
706 | * as it would otherwise cause VLANs to have an invalid | ||
707 | * channel context pointer for a while, possibly pointing | ||
708 | * to a channel context that has already been freed. | ||
709 | */ | ||
710 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
711 | lockdep_is_held(&local->chanctx_mtx)); | ||
712 | WARN_ON(!conf); | ||
713 | |||
714 | if (clear) | ||
715 | conf = NULL; | ||
716 | |||
717 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) | ||
718 | rcu_assign_pointer(vlan->vif.chanctx_conf, conf); | ||
719 | |||
720 | mutex_unlock(&local->chanctx_mtx); | ||
721 | } | ||
722 | |||
723 | void ieee80211_iter_chan_contexts_atomic( | 1691 | void ieee80211_iter_chan_contexts_atomic( |
724 | struct ieee80211_hw *hw, | 1692 | struct ieee80211_hw *hw, |
725 | void (*iter)(struct ieee80211_hw *hw, | 1693 | void (*iter)(struct ieee80211_hw *hw, |