diff options
author | Stanislaw Gruszka <sgruszka@redhat.com> | 2010-10-06 05:22:09 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-10-06 16:30:42 -0400 |
commit | e229f844d7223b7063bea1e649203ac521a58fe1 (patch) | |
tree | 59067ee607b0ad937fd541e981428b6c8cb1c5ab | |
parent | 259b62e35bf44a97983f275de569929a7d2bd5dd (diff) |
mac80211: keep lock when calling __ieee80211_scan_completed()
We are taking local->mtx inside __ieee80211_scan_completed(), but just
before call to that function we drop the lock. Dropping/taking lock is not
good, because can lead to hard to understand race conditions.
Patch split scan_completed() code into two functions, first must be called
with local->mtx taken and second without it.
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | net/mac80211/scan.c | 75 |
1 files changed, 39 insertions, 36 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 830c02bc398a..6964a4598176 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -249,12 +249,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
249 | return true; | 249 | return true; |
250 | } | 250 | } |
251 | 251 | ||
252 | static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 252 | static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, |
253 | bool was_hw_scan) | ||
253 | { | 254 | { |
254 | struct ieee80211_local *local = hw_to_local(hw); | 255 | struct ieee80211_local *local = hw_to_local(hw); |
255 | bool was_hw_scan; | ||
256 | 256 | ||
257 | mutex_lock(&local->mtx); | 257 | lockdep_assert_held(&local->mtx); |
258 | 258 | ||
259 | /* | 259 | /* |
260 | * It's ok to abort a not-yet-running scan (that | 260 | * It's ok to abort a not-yet-running scan (that |
@@ -265,17 +265,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
265 | if (WARN_ON(!local->scanning && !aborted)) | 265 | if (WARN_ON(!local->scanning && !aborted)) |
266 | aborted = true; | 266 | aborted = true; |
267 | 267 | ||
268 | if (WARN_ON(!local->scan_req)) { | 268 | if (WARN_ON(!local->scan_req)) |
269 | mutex_unlock(&local->mtx); | 269 | return false; |
270 | return; | ||
271 | } | ||
272 | 270 | ||
273 | was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | ||
274 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | 271 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { |
275 | ieee80211_queue_delayed_work(&local->hw, | 272 | ieee80211_queue_delayed_work(&local->hw, |
276 | &local->scan_work, 0); | 273 | &local->scan_work, 0); |
277 | mutex_unlock(&local->mtx); | 274 | return false; |
278 | return; | ||
279 | } | 275 | } |
280 | 276 | ||
281 | kfree(local->hw_scan_req); | 277 | kfree(local->hw_scan_req); |
@@ -289,23 +285,25 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
289 | local->scanning = 0; | 285 | local->scanning = 0; |
290 | local->scan_channel = NULL; | 286 | local->scan_channel = NULL; |
291 | 287 | ||
292 | /* we only have to protect scan_req and hw/sw scan */ | 288 | return true; |
293 | mutex_unlock(&local->mtx); | 289 | } |
294 | |||
295 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
296 | if (was_hw_scan) | ||
297 | goto done; | ||
298 | |||
299 | ieee80211_configure_filter(local); | ||
300 | 290 | ||
301 | drv_sw_scan_complete(local); | 291 | static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, |
292 | bool was_hw_scan) | ||
293 | { | ||
294 | struct ieee80211_local *local = hw_to_local(hw); | ||
302 | 295 | ||
303 | ieee80211_offchannel_return(local, true); | 296 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
297 | if (!was_hw_scan) { | ||
298 | ieee80211_configure_filter(local); | ||
299 | drv_sw_scan_complete(local); | ||
300 | ieee80211_offchannel_return(local, true); | ||
301 | } | ||
304 | 302 | ||
305 | done: | ||
306 | mutex_lock(&local->mtx); | 303 | mutex_lock(&local->mtx); |
307 | ieee80211_recalc_idle(local); | 304 | ieee80211_recalc_idle(local); |
308 | mutex_unlock(&local->mtx); | 305 | mutex_unlock(&local->mtx); |
306 | |||
309 | ieee80211_mlme_notify_scan_completed(local); | 307 | ieee80211_mlme_notify_scan_completed(local); |
310 | ieee80211_ibss_notify_scan_completed(local); | 308 | ieee80211_ibss_notify_scan_completed(local); |
311 | ieee80211_mesh_notify_scan_completed(local); | 309 | ieee80211_mesh_notify_scan_completed(local); |
@@ -366,6 +364,8 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, | |||
366 | struct ieee80211_local *local = sdata->local; | 364 | struct ieee80211_local *local = sdata->local; |
367 | int rc; | 365 | int rc; |
368 | 366 | ||
367 | lockdep_assert_held(&local->mtx); | ||
368 | |||
369 | if (local->scan_req) | 369 | if (local->scan_req) |
370 | return -EBUSY; | 370 | return -EBUSY; |
371 | 371 | ||
@@ -447,8 +447,8 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan) | |||
447 | return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME; | 447 | return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME; |
448 | } | 448 | } |
449 | 449 | ||
450 | static int ieee80211_scan_state_decision(struct ieee80211_local *local, | 450 | static void ieee80211_scan_state_decision(struct ieee80211_local *local, |
451 | unsigned long *next_delay) | 451 | unsigned long *next_delay) |
452 | { | 452 | { |
453 | bool associated = false; | 453 | bool associated = false; |
454 | bool tx_empty = true; | 454 | bool tx_empty = true; |
@@ -458,12 +458,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
458 | struct ieee80211_sub_if_data *sdata; | 458 | struct ieee80211_sub_if_data *sdata; |
459 | struct ieee80211_channel *next_chan; | 459 | struct ieee80211_channel *next_chan; |
460 | 460 | ||
461 | /* if no more bands/channels left, complete scan and advance to the idle state */ | ||
462 | if (local->scan_channel_idx >= local->scan_req->n_channels) { | ||
463 | __ieee80211_scan_completed(&local->hw, false); | ||
464 | return 1; | ||
465 | } | ||
466 | |||
467 | /* | 461 | /* |
468 | * check if at least one STA interface is associated, | 462 | * check if at least one STA interface is associated, |
469 | * check if at least one STA interface has pending tx frames | 463 | * check if at least one STA interface has pending tx frames |
@@ -535,7 +529,6 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
535 | } | 529 | } |
536 | 530 | ||
537 | *next_delay = 0; | 531 | *next_delay = 0; |
538 | return 0; | ||
539 | } | 532 | } |
540 | 533 | ||
541 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | 534 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, |
@@ -651,7 +644,7 @@ void ieee80211_scan_work(struct work_struct *work) | |||
651 | container_of(work, struct ieee80211_local, scan_work.work); | 644 | container_of(work, struct ieee80211_local, scan_work.work); |
652 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | 645 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; |
653 | unsigned long next_delay = 0; | 646 | unsigned long next_delay = 0; |
654 | bool aborted; | 647 | bool aborted, hw_scan, finish; |
655 | 648 | ||
656 | mutex_lock(&local->mtx); | 649 | mutex_lock(&local->mtx); |
657 | 650 | ||
@@ -704,8 +697,12 @@ void ieee80211_scan_work(struct work_struct *work) | |||
704 | do { | 697 | do { |
705 | switch (local->next_scan_state) { | 698 | switch (local->next_scan_state) { |
706 | case SCAN_DECISION: | 699 | case SCAN_DECISION: |
707 | if (ieee80211_scan_state_decision(local, &next_delay)) | 700 | /* if no more bands/channels left, complete scan */ |
708 | return; | 701 | if (local->scan_channel_idx >= local->scan_req->n_channels) { |
702 | aborted = false; | ||
703 | goto out_complete; | ||
704 | } | ||
705 | ieee80211_scan_state_decision(local, &next_delay); | ||
709 | break; | 706 | break; |
710 | case SCAN_SET_CHANNEL: | 707 | case SCAN_SET_CHANNEL: |
711 | ieee80211_scan_state_set_channel(local, &next_delay); | 708 | ieee80211_scan_state_set_channel(local, &next_delay); |
@@ -726,8 +723,11 @@ void ieee80211_scan_work(struct work_struct *work) | |||
726 | return; | 723 | return; |
727 | 724 | ||
728 | out_complete: | 725 | out_complete: |
726 | hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | ||
727 | finish = __ieee80211_scan_completed(&local->hw, aborted, hw_scan); | ||
729 | mutex_unlock(&local->mtx); | 728 | mutex_unlock(&local->mtx); |
730 | __ieee80211_scan_completed(&local->hw, aborted); | 729 | if (finish) |
730 | __ieee80211_scan_completed_finish(&local->hw, hw_scan); | ||
731 | return; | 731 | return; |
732 | 732 | ||
733 | out: | 733 | out: |
@@ -796,6 +796,7 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, | |||
796 | void ieee80211_scan_cancel(struct ieee80211_local *local) | 796 | void ieee80211_scan_cancel(struct ieee80211_local *local) |
797 | { | 797 | { |
798 | bool abortscan; | 798 | bool abortscan; |
799 | bool finish = false; | ||
799 | 800 | ||
800 | cancel_delayed_work_sync(&local->scan_work); | 801 | cancel_delayed_work_sync(&local->scan_work); |
801 | 802 | ||
@@ -806,8 +807,10 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) | |||
806 | mutex_lock(&local->mtx); | 807 | mutex_lock(&local->mtx); |
807 | abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) || | 808 | abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) || |
808 | (!local->scanning && local->scan_req); | 809 | (!local->scanning && local->scan_req); |
810 | if (abortscan) | ||
811 | finish = __ieee80211_scan_completed(&local->hw, true, false); | ||
809 | mutex_unlock(&local->mtx); | 812 | mutex_unlock(&local->mtx); |
810 | 813 | ||
811 | if (abortscan) | 814 | if (finish) |
812 | __ieee80211_scan_completed(&local->hw, true); | 815 | __ieee80211_scan_completed_finish(&local->hw, false); |
813 | } | 816 | } |