diff options
author | John W. Linville <linville@tuxdriver.com> | 2014-04-30 12:04:27 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-04-30 12:04:27 -0400 |
commit | f6595444c1ee798f9c227107bb3ab14730c6497a (patch) | |
tree | e8e06d7c2057c69e19848ee3abd6720927f5f9f9 /net/mac80211/chan.c | |
parent | 0006433a5be9e0e155ad493e33c6e9bf3868a87f (diff) | |
parent | f55ee0834247c88cb6981cb11eb1870392878371 (diff) |
Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211-next
Conflicts:
net/mac80211/chan.c
Diffstat (limited to 'net/mac80211/chan.c')
-rw-r--r-- | net/mac80211/chan.c | 614 |
1 files changed, 500 insertions, 114 deletions
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 75b5dd2c9267..48e6d6f010cd 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c | |||
@@ -9,6 +9,170 @@ | |||
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 const struct cfg80211_chan_def * | ||
67 | ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local, | ||
68 | struct ieee80211_chanctx *ctx, | ||
69 | const struct cfg80211_chan_def *compat) | ||
70 | { | ||
71 | struct ieee80211_sub_if_data *sdata; | ||
72 | |||
73 | lockdep_assert_held(&local->chanctx_mtx); | ||
74 | |||
75 | list_for_each_entry(sdata, &ctx->reserved_vifs, | ||
76 | reserved_chanctx_list) { | ||
77 | if (!compat) | ||
78 | compat = &sdata->reserved_chandef; | ||
79 | |||
80 | compat = cfg80211_chandef_compatible(&sdata->reserved_chandef, | ||
81 | compat); | ||
82 | if (!compat) | ||
83 | break; | ||
84 | } | ||
85 | |||
86 | return compat; | ||
87 | } | ||
88 | |||
89 | static const struct cfg80211_chan_def * | ||
90 | ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local, | ||
91 | struct ieee80211_chanctx *ctx, | ||
92 | const struct cfg80211_chan_def *compat) | ||
93 | { | ||
94 | struct ieee80211_sub_if_data *sdata; | ||
95 | |||
96 | lockdep_assert_held(&local->chanctx_mtx); | ||
97 | |||
98 | list_for_each_entry(sdata, &ctx->assigned_vifs, | ||
99 | assigned_chanctx_list) { | ||
100 | if (sdata->reserved_chanctx != NULL) | ||
101 | continue; | ||
102 | |||
103 | if (!compat) | ||
104 | compat = &sdata->vif.bss_conf.chandef; | ||
105 | |||
106 | compat = cfg80211_chandef_compatible( | ||
107 | &sdata->vif.bss_conf.chandef, compat); | ||
108 | if (!compat) | ||
109 | break; | ||
110 | } | ||
111 | |||
112 | return compat; | ||
113 | } | ||
114 | |||
115 | static const struct cfg80211_chan_def * | ||
116 | ieee80211_chanctx_combined_chandef(struct ieee80211_local *local, | ||
117 | struct ieee80211_chanctx *ctx, | ||
118 | const struct cfg80211_chan_def *compat) | ||
119 | { | ||
120 | lockdep_assert_held(&local->chanctx_mtx); | ||
121 | |||
122 | compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat); | ||
123 | if (!compat) | ||
124 | return NULL; | ||
125 | |||
126 | compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat); | ||
127 | if (!compat) | ||
128 | return NULL; | ||
129 | |||
130 | return compat; | ||
131 | } | ||
132 | |||
133 | static bool | ||
134 | ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local, | ||
135 | struct ieee80211_chanctx *ctx, | ||
136 | const struct cfg80211_chan_def *def) | ||
137 | { | ||
138 | lockdep_assert_held(&local->chanctx_mtx); | ||
139 | |||
140 | if (ieee80211_chanctx_combined_chandef(local, ctx, def)) | ||
141 | return true; | ||
142 | |||
143 | if (!list_empty(&ctx->reserved_vifs) && | ||
144 | ieee80211_chanctx_reserved_chandef(local, ctx, def)) | ||
145 | return true; | ||
146 | |||
147 | return false; | ||
148 | } | ||
149 | |||
150 | static struct ieee80211_chanctx * | ||
151 | ieee80211_find_reservation_chanctx(struct ieee80211_local *local, | ||
152 | const struct cfg80211_chan_def *chandef, | ||
153 | enum ieee80211_chanctx_mode mode) | ||
154 | { | ||
155 | struct ieee80211_chanctx *ctx; | ||
156 | |||
157 | lockdep_assert_held(&local->chanctx_mtx); | ||
158 | |||
159 | if (mode == IEEE80211_CHANCTX_EXCLUSIVE) | ||
160 | return NULL; | ||
161 | |||
162 | list_for_each_entry(ctx, &local->chanctx_list, list) { | ||
163 | if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) | ||
164 | continue; | ||
165 | |||
166 | if (!ieee80211_chanctx_can_reserve_chandef(local, ctx, | ||
167 | chandef)) | ||
168 | continue; | ||
169 | |||
170 | return ctx; | ||
171 | } | ||
172 | |||
173 | return NULL; | ||
174 | } | ||
175 | |||
12 | static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) | 176 | static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta) |
13 | { | 177 | { |
14 | switch (sta->bandwidth) { | 178 | switch (sta->bandwidth) { |
@@ -190,6 +354,11 @@ ieee80211_find_chanctx(struct ieee80211_local *local, | |||
190 | if (!compat) | 354 | if (!compat) |
191 | continue; | 355 | continue; |
192 | 356 | ||
357 | compat = ieee80211_chanctx_reserved_chandef(local, ctx, | ||
358 | compat); | ||
359 | if (!compat) | ||
360 | continue; | ||
361 | |||
193 | ieee80211_change_chanctx(local, ctx, compat); | 362 | ieee80211_change_chanctx(local, ctx, compat); |
194 | 363 | ||
195 | return ctx; | 364 | return ctx; |
@@ -217,62 +386,91 @@ static bool ieee80211_is_radar_required(struct ieee80211_local *local) | |||
217 | } | 386 | } |
218 | 387 | ||
219 | static struct ieee80211_chanctx * | 388 | static struct ieee80211_chanctx * |
220 | ieee80211_new_chanctx(struct ieee80211_local *local, | 389 | ieee80211_alloc_chanctx(struct ieee80211_local *local, |
221 | const struct cfg80211_chan_def *chandef, | 390 | const struct cfg80211_chan_def *chandef, |
222 | enum ieee80211_chanctx_mode mode) | 391 | enum ieee80211_chanctx_mode mode) |
223 | { | 392 | { |
224 | struct ieee80211_chanctx *ctx; | 393 | struct ieee80211_chanctx *ctx; |
225 | u32 changed; | ||
226 | int err; | ||
227 | 394 | ||
228 | lockdep_assert_held(&local->chanctx_mtx); | 395 | lockdep_assert_held(&local->chanctx_mtx); |
229 | 396 | ||
230 | ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); | 397 | ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL); |
231 | if (!ctx) | 398 | if (!ctx) |
232 | return ERR_PTR(-ENOMEM); | 399 | return NULL; |
233 | 400 | ||
401 | INIT_LIST_HEAD(&ctx->assigned_vifs); | ||
402 | INIT_LIST_HEAD(&ctx->reserved_vifs); | ||
234 | ctx->conf.def = *chandef; | 403 | ctx->conf.def = *chandef; |
235 | ctx->conf.rx_chains_static = 1; | 404 | ctx->conf.rx_chains_static = 1; |
236 | ctx->conf.rx_chains_dynamic = 1; | 405 | ctx->conf.rx_chains_dynamic = 1; |
237 | ctx->mode = mode; | 406 | ctx->mode = mode; |
238 | ctx->conf.radar_enabled = ieee80211_is_radar_required(local); | 407 | ctx->conf.radar_enabled = ieee80211_is_radar_required(local); |
239 | ieee80211_recalc_chanctx_min_def(local, ctx); | 408 | ieee80211_recalc_chanctx_min_def(local, ctx); |
409 | |||
410 | return ctx; | ||
411 | } | ||
412 | |||
413 | static int ieee80211_add_chanctx(struct ieee80211_local *local, | ||
414 | struct ieee80211_chanctx *ctx) | ||
415 | { | ||
416 | u32 changed; | ||
417 | int err; | ||
418 | |||
419 | lockdep_assert_held(&local->mtx); | ||
420 | lockdep_assert_held(&local->chanctx_mtx); | ||
421 | |||
240 | if (!local->use_chanctx) | 422 | if (!local->use_chanctx) |
241 | local->hw.conf.radar_enabled = ctx->conf.radar_enabled; | 423 | local->hw.conf.radar_enabled = ctx->conf.radar_enabled; |
242 | 424 | ||
243 | /* we hold the mutex to prevent idle from changing */ | ||
244 | lockdep_assert_held(&local->mtx); | ||
245 | /* turn idle off *before* setting channel -- some drivers need that */ | 425 | /* turn idle off *before* setting channel -- some drivers need that */ |
246 | changed = ieee80211_idle_off(local); | 426 | changed = ieee80211_idle_off(local); |
247 | if (changed) | 427 | if (changed) |
248 | ieee80211_hw_config(local, changed); | 428 | ieee80211_hw_config(local, changed); |
249 | 429 | ||
250 | if (!local->use_chanctx) { | 430 | if (!local->use_chanctx) { |
251 | local->_oper_chandef = *chandef; | 431 | local->_oper_chandef = ctx->conf.def; |
252 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 432 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
253 | } else { | 433 | } else { |
254 | err = drv_add_chanctx(local, ctx); | 434 | err = drv_add_chanctx(local, ctx); |
255 | if (err) { | 435 | if (err) { |
256 | kfree(ctx); | ||
257 | ieee80211_recalc_idle(local); | 436 | ieee80211_recalc_idle(local); |
258 | return ERR_PTR(err); | 437 | return err; |
259 | } | 438 | } |
260 | } | 439 | } |
261 | 440 | ||
262 | /* and keep the mutex held until the new chanctx is on the list */ | 441 | return 0; |
263 | list_add_rcu(&ctx->list, &local->chanctx_list); | 442 | } |
443 | |||
444 | static struct ieee80211_chanctx * | ||
445 | ieee80211_new_chanctx(struct ieee80211_local *local, | ||
446 | const struct cfg80211_chan_def *chandef, | ||
447 | enum ieee80211_chanctx_mode mode) | ||
448 | { | ||
449 | struct ieee80211_chanctx *ctx; | ||
450 | int err; | ||
451 | |||
452 | lockdep_assert_held(&local->mtx); | ||
453 | lockdep_assert_held(&local->chanctx_mtx); | ||
264 | 454 | ||
455 | ctx = ieee80211_alloc_chanctx(local, chandef, mode); | ||
456 | if (!ctx) | ||
457 | return ERR_PTR(-ENOMEM); | ||
458 | |||
459 | err = ieee80211_add_chanctx(local, ctx); | ||
460 | if (err) { | ||
461 | kfree(ctx); | ||
462 | return ERR_PTR(err); | ||
463 | } | ||
464 | |||
465 | list_add_rcu(&ctx->list, &local->chanctx_list); | ||
265 | return ctx; | 466 | return ctx; |
266 | } | 467 | } |
267 | 468 | ||
268 | static void ieee80211_free_chanctx(struct ieee80211_local *local, | 469 | static void ieee80211_del_chanctx(struct ieee80211_local *local, |
269 | struct ieee80211_chanctx *ctx) | 470 | struct ieee80211_chanctx *ctx) |
270 | { | 471 | { |
271 | bool check_single_channel = false; | ||
272 | lockdep_assert_held(&local->chanctx_mtx); | 472 | lockdep_assert_held(&local->chanctx_mtx); |
273 | 473 | ||
274 | WARN_ON_ONCE(ctx->refcount != 0); | ||
275 | |||
276 | if (!local->use_chanctx) { | 474 | if (!local->use_chanctx) { |
277 | struct cfg80211_chan_def *chandef = &local->_oper_chandef; | 475 | struct cfg80211_chan_def *chandef = &local->_oper_chandef; |
278 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; | 476 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; |
@@ -282,8 +480,9 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local, | |||
282 | /* NOTE: Disabling radar is only valid here for | 480 | /* NOTE: Disabling radar is only valid here for |
283 | * single channel context. To be sure, check it ... | 481 | * single channel context. To be sure, check it ... |
284 | */ | 482 | */ |
285 | if (local->hw.conf.radar_enabled) | 483 | WARN_ON(local->hw.conf.radar_enabled && |
286 | check_single_channel = true; | 484 | !list_empty(&local->chanctx_list)); |
485 | |||
287 | local->hw.conf.radar_enabled = false; | 486 | local->hw.conf.radar_enabled = false; |
288 | 487 | ||
289 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 488 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
@@ -291,39 +490,19 @@ static void ieee80211_free_chanctx(struct ieee80211_local *local, | |||
291 | drv_remove_chanctx(local, ctx); | 490 | drv_remove_chanctx(local, ctx); |
292 | } | 491 | } |
293 | 492 | ||
294 | list_del_rcu(&ctx->list); | ||
295 | kfree_rcu(ctx, rcu_head); | ||
296 | |||
297 | /* throw a warning if this wasn't the only channel context. */ | ||
298 | WARN_ON(check_single_channel && !list_empty(&local->chanctx_list)); | ||
299 | |||
300 | ieee80211_recalc_idle(local); | 493 | ieee80211_recalc_idle(local); |
301 | } | 494 | } |
302 | 495 | ||
303 | static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, | 496 | static void ieee80211_free_chanctx(struct ieee80211_local *local, |
304 | struct ieee80211_chanctx *ctx) | 497 | struct ieee80211_chanctx *ctx) |
305 | { | 498 | { |
306 | struct ieee80211_local *local = sdata->local; | ||
307 | int ret; | ||
308 | |||
309 | lockdep_assert_held(&local->chanctx_mtx); | 499 | lockdep_assert_held(&local->chanctx_mtx); |
310 | 500 | ||
311 | ret = drv_assign_vif_chanctx(local, sdata, ctx); | 501 | WARN_ON_ONCE(ieee80211_chanctx_refcount(local, ctx) != 0); |
312 | if (ret) | ||
313 | return ret; | ||
314 | |||
315 | rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); | ||
316 | ctx->refcount++; | ||
317 | |||
318 | ieee80211_recalc_txpower(sdata); | ||
319 | ieee80211_recalc_chanctx_min_def(local, ctx); | ||
320 | sdata->vif.bss_conf.idle = false; | ||
321 | 502 | ||
322 | if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && | 503 | list_del_rcu(&ctx->list); |
323 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | 504 | ieee80211_del_chanctx(local, ctx); |
324 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); | 505 | kfree_rcu(ctx, rcu_head); |
325 | |||
326 | return 0; | ||
327 | } | 506 | } |
328 | 507 | ||
329 | static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, | 508 | static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, |
@@ -384,30 +563,58 @@ static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local, | |||
384 | drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); | 563 | drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR); |
385 | } | 564 | } |
386 | 565 | ||
387 | static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, | 566 | static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, |
388 | struct ieee80211_chanctx *ctx) | 567 | struct ieee80211_chanctx *new_ctx) |
389 | { | 568 | { |
390 | struct ieee80211_local *local = sdata->local; | 569 | struct ieee80211_local *local = sdata->local; |
570 | struct ieee80211_chanctx_conf *conf; | ||
571 | struct ieee80211_chanctx *curr_ctx = NULL; | ||
572 | int ret = 0; | ||
391 | 573 | ||
392 | lockdep_assert_held(&local->chanctx_mtx); | 574 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
575 | lockdep_is_held(&local->chanctx_mtx)); | ||
393 | 576 | ||
394 | ctx->refcount--; | 577 | if (conf) { |
395 | rcu_assign_pointer(sdata->vif.chanctx_conf, NULL); | 578 | curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); |
396 | 579 | ||
397 | sdata->vif.bss_conf.idle = true; | 580 | drv_unassign_vif_chanctx(local, sdata, curr_ctx); |
581 | conf = NULL; | ||
582 | list_del(&sdata->assigned_chanctx_list); | ||
583 | } | ||
398 | 584 | ||
399 | if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && | 585 | if (new_ctx) { |
400 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | 586 | ret = drv_assign_vif_chanctx(local, sdata, new_ctx); |
401 | ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE); | 587 | if (ret) |
588 | goto out; | ||
402 | 589 | ||
403 | drv_unassign_vif_chanctx(local, sdata, ctx); | 590 | conf = &new_ctx->conf; |
591 | list_add(&sdata->assigned_chanctx_list, | ||
592 | &new_ctx->assigned_vifs); | ||
593 | } | ||
404 | 594 | ||
405 | if (ctx->refcount > 0) { | 595 | out: |
406 | ieee80211_recalc_chanctx_chantype(sdata->local, ctx); | 596 | rcu_assign_pointer(sdata->vif.chanctx_conf, conf); |
407 | ieee80211_recalc_smps_chanctx(local, ctx); | 597 | |
408 | ieee80211_recalc_radar_chanctx(local, ctx); | 598 | sdata->vif.bss_conf.idle = !conf; |
409 | ieee80211_recalc_chanctx_min_def(local, ctx); | 599 | |
600 | if (curr_ctx && ieee80211_chanctx_num_assigned(local, curr_ctx) > 0) { | ||
601 | ieee80211_recalc_chanctx_chantype(local, curr_ctx); | ||
602 | ieee80211_recalc_smps_chanctx(local, curr_ctx); | ||
603 | ieee80211_recalc_radar_chanctx(local, curr_ctx); | ||
604 | ieee80211_recalc_chanctx_min_def(local, curr_ctx); | ||
410 | } | 605 | } |
606 | |||
607 | if (new_ctx && ieee80211_chanctx_num_assigned(local, new_ctx) > 0) { | ||
608 | ieee80211_recalc_txpower(sdata); | ||
609 | ieee80211_recalc_chanctx_min_def(local, new_ctx); | ||
610 | } | ||
611 | |||
612 | if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE && | ||
613 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | ||
614 | ieee80211_bss_info_change_notify(sdata, | ||
615 | BSS_CHANGED_IDLE); | ||
616 | |||
617 | return ret; | ||
411 | } | 618 | } |
412 | 619 | ||
413 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | 620 | static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) |
@@ -425,8 +632,11 @@ static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata) | |||
425 | 632 | ||
426 | ctx = container_of(conf, struct ieee80211_chanctx, conf); | 633 | ctx = container_of(conf, struct ieee80211_chanctx, conf); |
427 | 634 | ||
428 | ieee80211_unassign_vif_chanctx(sdata, ctx); | 635 | if (sdata->reserved_chanctx) |
429 | if (ctx->refcount == 0) | 636 | ieee80211_vif_unreserve_chanctx(sdata); |
637 | |||
638 | ieee80211_assign_vif_chanctx(sdata, NULL); | ||
639 | if (ieee80211_chanctx_refcount(local, ctx) == 0) | ||
430 | ieee80211_free_chanctx(local, ctx); | 640 | ieee80211_free_chanctx(local, ctx); |
431 | } | 641 | } |
432 | 642 | ||
@@ -526,6 +736,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
526 | { | 736 | { |
527 | struct ieee80211_local *local = sdata->local; | 737 | struct ieee80211_local *local = sdata->local; |
528 | struct ieee80211_chanctx *ctx; | 738 | struct ieee80211_chanctx *ctx; |
739 | u8 radar_detect_width = 0; | ||
529 | int ret; | 740 | int ret; |
530 | 741 | ||
531 | lockdep_assert_held(&local->mtx); | 742 | lockdep_assert_held(&local->mtx); |
@@ -533,6 +744,22 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
533 | WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); | 744 | WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev)); |
534 | 745 | ||
535 | mutex_lock(&local->chanctx_mtx); | 746 | mutex_lock(&local->chanctx_mtx); |
747 | |||
748 | ret = cfg80211_chandef_dfs_required(local->hw.wiphy, | ||
749 | chandef, | ||
750 | sdata->wdev.iftype); | ||
751 | if (ret < 0) | ||
752 | goto out; | ||
753 | if (ret > 0) | ||
754 | radar_detect_width = BIT(chandef->width); | ||
755 | |||
756 | sdata->radar_required = ret; | ||
757 | |||
758 | ret = ieee80211_check_combinations(sdata, chandef, mode, | ||
759 | radar_detect_width); | ||
760 | if (ret < 0) | ||
761 | goto out; | ||
762 | |||
536 | __ieee80211_vif_release_channel(sdata); | 763 | __ieee80211_vif_release_channel(sdata); |
537 | 764 | ||
538 | ctx = ieee80211_find_chanctx(local, chandef, mode); | 765 | ctx = ieee80211_find_chanctx(local, chandef, mode); |
@@ -548,7 +775,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
548 | ret = ieee80211_assign_vif_chanctx(sdata, ctx); | 775 | ret = ieee80211_assign_vif_chanctx(sdata, ctx); |
549 | if (ret) { | 776 | if (ret) { |
550 | /* if assign fails refcount stays the same */ | 777 | /* if assign fails refcount stays the same */ |
551 | if (ctx->refcount == 0) | 778 | if (ieee80211_chanctx_refcount(local, ctx) == 0) |
552 | ieee80211_free_chanctx(local, ctx); | 779 | ieee80211_free_chanctx(local, ctx); |
553 | goto out; | 780 | goto out; |
554 | } | 781 | } |
@@ -560,15 +787,47 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, | |||
560 | return ret; | 787 | return ret; |
561 | } | 788 | } |
562 | 789 | ||
790 | static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | ||
791 | struct ieee80211_chanctx *ctx, | ||
792 | u32 *changed) | ||
793 | { | ||
794 | struct ieee80211_local *local = sdata->local; | ||
795 | const struct cfg80211_chan_def *chandef = &sdata->csa_chandef; | ||
796 | u32 chanctx_changed = 0; | ||
797 | |||
798 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, | ||
799 | IEEE80211_CHAN_DISABLED)) | ||
800 | return -EINVAL; | ||
801 | |||
802 | if (ieee80211_chanctx_refcount(local, ctx) != 1) | ||
803 | return -EINVAL; | ||
804 | |||
805 | if (sdata->vif.bss_conf.chandef.width != chandef->width) { | ||
806 | chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; | ||
807 | *changed |= BSS_CHANGED_BANDWIDTH; | ||
808 | } | ||
809 | |||
810 | sdata->vif.bss_conf.chandef = *chandef; | ||
811 | ctx->conf.def = *chandef; | ||
812 | |||
813 | chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; | ||
814 | drv_change_chanctx(local, ctx, chanctx_changed); | ||
815 | |||
816 | ieee80211_recalc_chanctx_chantype(local, ctx); | ||
817 | ieee80211_recalc_smps_chanctx(local, ctx); | ||
818 | ieee80211_recalc_radar_chanctx(local, ctx); | ||
819 | ieee80211_recalc_chanctx_min_def(local, ctx); | ||
820 | |||
821 | return 0; | ||
822 | } | ||
823 | |||
563 | int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | 824 | int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, |
564 | u32 *changed) | 825 | u32 *changed) |
565 | { | 826 | { |
566 | struct ieee80211_local *local = sdata->local; | 827 | struct ieee80211_local *local = sdata->local; |
567 | struct ieee80211_chanctx_conf *conf; | 828 | struct ieee80211_chanctx_conf *conf; |
568 | struct ieee80211_chanctx *ctx; | 829 | struct ieee80211_chanctx *ctx; |
569 | const struct cfg80211_chan_def *chandef = &sdata->csa_chandef; | ||
570 | int ret; | 830 | int ret; |
571 | u32 chanctx_changed = 0; | ||
572 | 831 | ||
573 | lockdep_assert_held(&local->mtx); | 832 | lockdep_assert_held(&local->mtx); |
574 | 833 | ||
@@ -576,11 +835,94 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | |||
576 | if (WARN_ON(!sdata->vif.csa_active)) | 835 | if (WARN_ON(!sdata->vif.csa_active)) |
577 | return -EINVAL; | 836 | return -EINVAL; |
578 | 837 | ||
579 | if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef, | 838 | mutex_lock(&local->chanctx_mtx); |
580 | IEEE80211_CHAN_DISABLED)) | 839 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
840 | lockdep_is_held(&local->chanctx_mtx)); | ||
841 | if (!conf) { | ||
842 | ret = -EINVAL; | ||
843 | goto out; | ||
844 | } | ||
845 | |||
846 | ctx = container_of(conf, struct ieee80211_chanctx, conf); | ||
847 | |||
848 | ret = __ieee80211_vif_change_channel(sdata, ctx, changed); | ||
849 | out: | ||
850 | mutex_unlock(&local->chanctx_mtx); | ||
851 | return ret; | ||
852 | } | ||
853 | |||
854 | static void | ||
855 | __ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | ||
856 | bool clear) | ||
857 | { | ||
858 | struct ieee80211_local *local = sdata->local; | ||
859 | struct ieee80211_sub_if_data *vlan; | ||
860 | struct ieee80211_chanctx_conf *conf; | ||
861 | |||
862 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) | ||
863 | return; | ||
864 | |||
865 | lockdep_assert_held(&local->mtx); | ||
866 | |||
867 | /* Check that conf exists, even when clearing this function | ||
868 | * must be called with the AP's channel context still there | ||
869 | * as it would otherwise cause VLANs to have an invalid | ||
870 | * channel context pointer for a while, possibly pointing | ||
871 | * to a channel context that has already been freed. | ||
872 | */ | ||
873 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
874 | lockdep_is_held(&local->chanctx_mtx)); | ||
875 | WARN_ON(!conf); | ||
876 | |||
877 | if (clear) | ||
878 | conf = NULL; | ||
879 | |||
880 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) | ||
881 | rcu_assign_pointer(vlan->vif.chanctx_conf, conf); | ||
882 | } | ||
883 | |||
884 | void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | ||
885 | bool clear) | ||
886 | { | ||
887 | struct ieee80211_local *local = sdata->local; | ||
888 | |||
889 | mutex_lock(&local->chanctx_mtx); | ||
890 | |||
891 | __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear); | ||
892 | |||
893 | mutex_unlock(&local->chanctx_mtx); | ||
894 | } | ||
895 | |||
896 | int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata) | ||
897 | { | ||
898 | struct ieee80211_chanctx *ctx = sdata->reserved_chanctx; | ||
899 | |||
900 | lockdep_assert_held(&sdata->local->chanctx_mtx); | ||
901 | |||
902 | if (WARN_ON(!ctx)) | ||
581 | return -EINVAL; | 903 | return -EINVAL; |
582 | 904 | ||
905 | list_del(&sdata->reserved_chanctx_list); | ||
906 | sdata->reserved_chanctx = NULL; | ||
907 | |||
908 | if (ieee80211_chanctx_refcount(sdata->local, ctx) == 0) | ||
909 | ieee80211_free_chanctx(sdata->local, ctx); | ||
910 | |||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata, | ||
915 | const struct cfg80211_chan_def *chandef, | ||
916 | enum ieee80211_chanctx_mode mode, | ||
917 | bool radar_required) | ||
918 | { | ||
919 | struct ieee80211_local *local = sdata->local; | ||
920 | struct ieee80211_chanctx_conf *conf; | ||
921 | struct ieee80211_chanctx *new_ctx, *curr_ctx; | ||
922 | int ret = 0; | ||
923 | |||
583 | mutex_lock(&local->chanctx_mtx); | 924 | mutex_lock(&local->chanctx_mtx); |
925 | |||
584 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | 926 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
585 | lockdep_is_held(&local->chanctx_mtx)); | 927 | lockdep_is_held(&local->chanctx_mtx)); |
586 | if (!conf) { | 928 | if (!conf) { |
@@ -588,30 +930,108 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, | |||
588 | goto out; | 930 | goto out; |
589 | } | 931 | } |
590 | 932 | ||
591 | ctx = container_of(conf, struct ieee80211_chanctx, conf); | 933 | curr_ctx = container_of(conf, struct ieee80211_chanctx, conf); |
592 | if (ctx->refcount != 1) { | 934 | |
935 | new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode); | ||
936 | if (!new_ctx) { | ||
937 | if (ieee80211_chanctx_refcount(local, curr_ctx) == 1 && | ||
938 | (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) { | ||
939 | /* if we're the only users of the chanctx and | ||
940 | * the driver supports changing a running | ||
941 | * context, reserve our current context | ||
942 | */ | ||
943 | new_ctx = curr_ctx; | ||
944 | } else if (ieee80211_can_create_new_chanctx(local)) { | ||
945 | /* create a new context and reserve it */ | ||
946 | new_ctx = ieee80211_new_chanctx(local, chandef, mode); | ||
947 | if (IS_ERR(new_ctx)) { | ||
948 | ret = PTR_ERR(new_ctx); | ||
949 | goto out; | ||
950 | } | ||
951 | } else { | ||
952 | ret = -EBUSY; | ||
953 | goto out; | ||
954 | } | ||
955 | } | ||
956 | |||
957 | list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs); | ||
958 | sdata->reserved_chanctx = new_ctx; | ||
959 | sdata->reserved_chandef = *chandef; | ||
960 | sdata->reserved_radar_required = radar_required; | ||
961 | out: | ||
962 | mutex_unlock(&local->chanctx_mtx); | ||
963 | return ret; | ||
964 | } | ||
965 | |||
966 | int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata, | ||
967 | u32 *changed) | ||
968 | { | ||
969 | struct ieee80211_local *local = sdata->local; | ||
970 | struct ieee80211_chanctx *ctx; | ||
971 | struct ieee80211_chanctx *old_ctx; | ||
972 | struct ieee80211_chanctx_conf *conf; | ||
973 | int ret; | ||
974 | u32 tmp_changed = *changed; | ||
975 | |||
976 | /* TODO: need to recheck if the chandef is usable etc.? */ | ||
977 | |||
978 | lockdep_assert_held(&local->mtx); | ||
979 | |||
980 | mutex_lock(&local->chanctx_mtx); | ||
981 | |||
982 | ctx = sdata->reserved_chanctx; | ||
983 | if (WARN_ON(!ctx)) { | ||
593 | ret = -EINVAL; | 984 | ret = -EINVAL; |
594 | goto out; | 985 | goto out; |
595 | } | 986 | } |
596 | 987 | ||
597 | if (sdata->vif.bss_conf.chandef.width != chandef->width) { | 988 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, |
598 | chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH; | 989 | lockdep_is_held(&local->chanctx_mtx)); |
599 | *changed |= BSS_CHANGED_BANDWIDTH; | 990 | if (!conf) { |
991 | ret = -EINVAL; | ||
992 | goto out; | ||
600 | } | 993 | } |
601 | 994 | ||
602 | sdata->vif.bss_conf.chandef = *chandef; | 995 | old_ctx = container_of(conf, struct ieee80211_chanctx, conf); |
603 | ctx->conf.def = *chandef; | ||
604 | 996 | ||
605 | chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; | 997 | if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width) |
606 | drv_change_chanctx(local, ctx, chanctx_changed); | 998 | tmp_changed |= BSS_CHANGED_BANDWIDTH; |
999 | |||
1000 | sdata->vif.bss_conf.chandef = sdata->reserved_chandef; | ||
1001 | |||
1002 | /* unref our reservation */ | ||
1003 | sdata->reserved_chanctx = NULL; | ||
1004 | sdata->radar_required = sdata->reserved_radar_required; | ||
1005 | list_del(&sdata->reserved_chanctx_list); | ||
1006 | |||
1007 | if (old_ctx == ctx) { | ||
1008 | /* This is our own context, just change it */ | ||
1009 | ret = __ieee80211_vif_change_channel(sdata, old_ctx, | ||
1010 | &tmp_changed); | ||
1011 | if (ret) | ||
1012 | goto out; | ||
1013 | } else { | ||
1014 | ret = ieee80211_assign_vif_chanctx(sdata, ctx); | ||
1015 | if (ieee80211_chanctx_refcount(local, old_ctx) == 0) | ||
1016 | ieee80211_free_chanctx(local, old_ctx); | ||
1017 | if (ret) { | ||
1018 | /* if assign fails refcount stays the same */ | ||
1019 | if (ieee80211_chanctx_refcount(local, ctx) == 0) | ||
1020 | ieee80211_free_chanctx(local, ctx); | ||
1021 | goto out; | ||
1022 | } | ||
1023 | |||
1024 | if (sdata->vif.type == NL80211_IFTYPE_AP) | ||
1025 | __ieee80211_vif_copy_chanctx_to_vlans(sdata, false); | ||
1026 | } | ||
1027 | |||
1028 | *changed = tmp_changed; | ||
607 | 1029 | ||
608 | ieee80211_recalc_chanctx_chantype(local, ctx); | 1030 | ieee80211_recalc_chanctx_chantype(local, ctx); |
609 | ieee80211_recalc_smps_chanctx(local, ctx); | 1031 | ieee80211_recalc_smps_chanctx(local, ctx); |
610 | ieee80211_recalc_radar_chanctx(local, ctx); | 1032 | ieee80211_recalc_radar_chanctx(local, ctx); |
611 | ieee80211_recalc_chanctx_min_def(local, ctx); | 1033 | ieee80211_recalc_chanctx_min_def(local, ctx); |
612 | 1034 | out: | |
613 | ret = 0; | ||
614 | out: | ||
615 | mutex_unlock(&local->chanctx_mtx); | 1035 | mutex_unlock(&local->chanctx_mtx); |
616 | return ret; | 1036 | return ret; |
617 | } | 1037 | } |
@@ -695,40 +1115,6 @@ void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata) | |||
695 | mutex_unlock(&local->chanctx_mtx); | 1115 | mutex_unlock(&local->chanctx_mtx); |
696 | } | 1116 | } |
697 | 1117 | ||
698 | void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata, | ||
699 | bool clear) | ||
700 | { | ||
701 | struct ieee80211_local *local = sdata->local; | ||
702 | struct ieee80211_sub_if_data *vlan; | ||
703 | struct ieee80211_chanctx_conf *conf; | ||
704 | |||
705 | ASSERT_RTNL(); | ||
706 | |||
707 | if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) | ||
708 | return; | ||
709 | |||
710 | mutex_lock(&local->chanctx_mtx); | ||
711 | |||
712 | /* | ||
713 | * Check that conf exists, even when clearing this function | ||
714 | * must be called with the AP's channel context still there | ||
715 | * as it would otherwise cause VLANs to have an invalid | ||
716 | * channel context pointer for a while, possibly pointing | ||
717 | * to a channel context that has already been freed. | ||
718 | */ | ||
719 | conf = rcu_dereference_protected(sdata->vif.chanctx_conf, | ||
720 | lockdep_is_held(&local->chanctx_mtx)); | ||
721 | WARN_ON(!conf); | ||
722 | |||
723 | if (clear) | ||
724 | conf = NULL; | ||
725 | |||
726 | list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) | ||
727 | rcu_assign_pointer(vlan->vif.chanctx_conf, conf); | ||
728 | |||
729 | mutex_unlock(&local->chanctx_mtx); | ||
730 | } | ||
731 | |||
732 | void ieee80211_iter_chan_contexts_atomic( | 1118 | void ieee80211_iter_chan_contexts_atomic( |
733 | struct ieee80211_hw *hw, | 1119 | struct ieee80211_hw *hw, |
734 | void (*iter)(struct ieee80211_hw *hw, | 1120 | void (*iter)(struct ieee80211_hw *hw, |