aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211')
-rw-r--r--net/mac80211/cfg.c6
-rw-r--r--net/mac80211/chan.c17
-rw-r--r--net/mac80211/ieee80211_i.h4
-rw-r--r--net/mac80211/iface.c2
-rw-r--r--net/mac80211/offchannel.c23
5 files changed, 39 insertions, 13 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 1d1ddabd89ca..c34e6d78a592 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2636,7 +2636,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
2636 list_del(&dep->list); 2636 list_del(&dep->list);
2637 mutex_unlock(&local->mtx); 2637 mutex_unlock(&local->mtx);
2638 2638
2639 ieee80211_roc_notify_destroy(dep); 2639 ieee80211_roc_notify_destroy(dep, true);
2640 return 0; 2640 return 0;
2641 } 2641 }
2642 2642
@@ -2676,7 +2676,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
2676 ieee80211_start_next_roc(local); 2676 ieee80211_start_next_roc(local);
2677 mutex_unlock(&local->mtx); 2677 mutex_unlock(&local->mtx);
2678 2678
2679 ieee80211_roc_notify_destroy(found); 2679 ieee80211_roc_notify_destroy(found, true);
2680 } else { 2680 } else {
2681 /* work may be pending so use it all the time */ 2681 /* work may be pending so use it all the time */
2682 found->abort = true; 2682 found->abort = true;
@@ -2686,6 +2686,8 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
2686 2686
2687 /* work will clean up etc */ 2687 /* work will clean up etc */
2688 flush_delayed_work(&found->work); 2688 flush_delayed_work(&found->work);
2689 WARN_ON(!found->to_be_freed);
2690 kfree(found);
2689 } 2691 }
2690 2692
2691 return 0; 2693 return 0;
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 78c0d90dd641..931be419ab5a 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -63,6 +63,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local,
63 enum ieee80211_chanctx_mode mode) 63 enum ieee80211_chanctx_mode mode)
64{ 64{
65 struct ieee80211_chanctx *ctx; 65 struct ieee80211_chanctx *ctx;
66 u32 changed;
66 int err; 67 int err;
67 68
68 lockdep_assert_held(&local->chanctx_mtx); 69 lockdep_assert_held(&local->chanctx_mtx);
@@ -76,6 +77,13 @@ ieee80211_new_chanctx(struct ieee80211_local *local,
76 ctx->conf.rx_chains_dynamic = 1; 77 ctx->conf.rx_chains_dynamic = 1;
77 ctx->mode = mode; 78 ctx->mode = mode;
78 79
80 /* 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
79 if (!local->use_chanctx) { 87 if (!local->use_chanctx) {
80 local->_oper_channel_type = 88 local->_oper_channel_type =
81 cfg80211_get_chandef_type(chandef); 89 cfg80211_get_chandef_type(chandef);
@@ -85,14 +93,17 @@ ieee80211_new_chanctx(struct ieee80211_local *local,
85 err = drv_add_chanctx(local, ctx); 93 err = drv_add_chanctx(local, ctx);
86 if (err) { 94 if (err) {
87 kfree(ctx); 95 kfree(ctx);
88 return ERR_PTR(err); 96 ctx = ERR_PTR(err);
97
98 ieee80211_recalc_idle(local);
99 goto out;
89 } 100 }
90 } 101 }
91 102
103 /* and keep the mutex held until the new chanctx is on the list */
92 list_add_rcu(&ctx->list, &local->chanctx_list); 104 list_add_rcu(&ctx->list, &local->chanctx_list);
93 105
94 mutex_lock(&local->mtx); 106 out:
95 ieee80211_recalc_idle(local);
96 mutex_unlock(&local->mtx); 107 mutex_unlock(&local->mtx);
97 108
98 return ctx; 109 return ctx;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f4433f081e77..e140184c28ce 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -309,6 +309,7 @@ struct ieee80211_roc_work {
309 struct ieee80211_channel *chan; 309 struct ieee80211_channel *chan;
310 310
311 bool started, abort, hw_begun, notified; 311 bool started, abort, hw_begun, notified;
312 bool to_be_freed;
312 313
313 unsigned long hw_start_time; 314 unsigned long hw_start_time;
314 315
@@ -1335,7 +1336,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local);
1335void ieee80211_roc_setup(struct ieee80211_local *local); 1336void ieee80211_roc_setup(struct ieee80211_local *local);
1336void ieee80211_start_next_roc(struct ieee80211_local *local); 1337void ieee80211_start_next_roc(struct ieee80211_local *local);
1337void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata); 1338void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata);
1338void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc); 1339void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free);
1339void ieee80211_sw_roc_work(struct work_struct *work); 1340void ieee80211_sw_roc_work(struct work_struct *work);
1340void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); 1341void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
1341 1342
@@ -1349,6 +1350,7 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
1349 enum nl80211_iftype type); 1350 enum nl80211_iftype type);
1350void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata); 1351void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata);
1351void ieee80211_remove_interfaces(struct ieee80211_local *local); 1352void ieee80211_remove_interfaces(struct ieee80211_local *local);
1353u32 ieee80211_idle_off(struct ieee80211_local *local);
1352void ieee80211_recalc_idle(struct ieee80211_local *local); 1354void ieee80211_recalc_idle(struct ieee80211_local *local);
1353void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, 1355void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
1354 const int offset); 1356 const int offset);
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7530c60fe502..a2b5e17036bb 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -78,7 +78,7 @@ void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
78 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER); 78 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
79} 79}
80 80
81static u32 ieee80211_idle_off(struct ieee80211_local *local) 81u32 ieee80211_idle_off(struct ieee80211_local *local)
82{ 82{
83 if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE)) 83 if (!(local->hw.conf.flags & IEEE80211_CONF_IDLE))
84 return 0; 84 return 0;
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index db547fceaeb9..950c95bec13d 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -297,10 +297,13 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
297 } 297 }
298} 298}
299 299
300void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) 300void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc, bool free)
301{ 301{
302 struct ieee80211_roc_work *dep, *tmp; 302 struct ieee80211_roc_work *dep, *tmp;
303 303
304 if (WARN_ON(roc->to_be_freed))
305 return;
306
304 /* was never transmitted */ 307 /* was never transmitted */
305 if (roc->frame) { 308 if (roc->frame) {
306 cfg80211_mgmt_tx_status(&roc->sdata->wdev, 309 cfg80211_mgmt_tx_status(&roc->sdata->wdev,
@@ -316,9 +319,12 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
316 GFP_KERNEL); 319 GFP_KERNEL);
317 320
318 list_for_each_entry_safe(dep, tmp, &roc->dependents, list) 321 list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
319 ieee80211_roc_notify_destroy(dep); 322 ieee80211_roc_notify_destroy(dep, true);
320 323
321 kfree(roc); 324 if (free)
325 kfree(roc);
326 else
327 roc->to_be_freed = true;
322} 328}
323 329
324void ieee80211_sw_roc_work(struct work_struct *work) 330void ieee80211_sw_roc_work(struct work_struct *work)
@@ -331,6 +337,9 @@ void ieee80211_sw_roc_work(struct work_struct *work)
331 337
332 mutex_lock(&local->mtx); 338 mutex_lock(&local->mtx);
333 339
340 if (roc->to_be_freed)
341 goto out_unlock;
342
334 if (roc->abort) 343 if (roc->abort)
335 goto finish; 344 goto finish;
336 345
@@ -370,7 +379,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)
370 finish: 379 finish:
371 list_del(&roc->list); 380 list_del(&roc->list);
372 started = roc->started; 381 started = roc->started;
373 ieee80211_roc_notify_destroy(roc); 382 ieee80211_roc_notify_destroy(roc, !roc->abort);
374 383
375 if (started) { 384 if (started) {
376 drv_flush(local, false); 385 drv_flush(local, false);
@@ -410,7 +419,7 @@ static void ieee80211_hw_roc_done(struct work_struct *work)
410 419
411 list_del(&roc->list); 420 list_del(&roc->list);
412 421
413 ieee80211_roc_notify_destroy(roc); 422 ieee80211_roc_notify_destroy(roc, true);
414 423
415 /* if there's another roc, start it now */ 424 /* if there's another roc, start it now */
416 ieee80211_start_next_roc(local); 425 ieee80211_start_next_roc(local);
@@ -460,12 +469,14 @@ void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)
460 list_for_each_entry_safe(roc, tmp, &tmp_list, list) { 469 list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
461 if (local->ops->remain_on_channel) { 470 if (local->ops->remain_on_channel) {
462 list_del(&roc->list); 471 list_del(&roc->list);
463 ieee80211_roc_notify_destroy(roc); 472 ieee80211_roc_notify_destroy(roc, true);
464 } else { 473 } else {
465 ieee80211_queue_delayed_work(&local->hw, &roc->work, 0); 474 ieee80211_queue_delayed_work(&local->hw, &roc->work, 0);
466 475
467 /* work will clean up etc */ 476 /* work will clean up etc */
468 flush_delayed_work(&roc->work); 477 flush_delayed_work(&roc->work);
478 WARN_ON(!roc->to_be_freed);
479 kfree(roc);
469 } 480 }
470 } 481 }
471 482