diff options
author | Johannes Berg <johannes.berg@intel.com> | 2011-03-07 09:48:41 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-03-07 13:51:04 -0500 |
commit | d07bfd8b6f20a81d7ec65c50f35b053d9e3aa740 (patch) | |
tree | 3eaa4381dde301226625782d467778b32ee62c80 /net/mac80211 | |
parent | 2a6672f2c425e6d1da2ef7f3169e417cd1f5a6cd (diff) |
mac80211: fix scan race, simplify code
The scan code has a race that Michael reported
he ran into, but it's easy to fix while at the
same time simplifying the code.
The race resulted in the following warning:
------------[ cut here ]------------
WARNING: at net/mac80211/scan.c:310 ieee80211_rx_bss_free+0x20c/0x4b8 [mac80211]()
Modules linked in: [...]
[<c0033edc>] (unwind_backtrace+0x0/0xe0) from [<c004f2a4>] (warn_slowpath_common+0x4c/0x64)
[... backtrace wasn't useful ...]
Reported-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/scan.c | 64 |
1 files changed, 24 insertions, 40 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 842954509925..489b6ad200d4 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -258,10 +258,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
258 | return true; | 258 | return true; |
259 | } | 259 | } |
260 | 260 | ||
261 | static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | 261 | static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, |
262 | bool was_hw_scan) | 262 | bool was_hw_scan) |
263 | { | 263 | { |
264 | struct ieee80211_local *local = hw_to_local(hw); | 264 | struct ieee80211_local *local = hw_to_local(hw); |
265 | bool on_oper_chan; | ||
266 | bool enable_beacons = false; | ||
265 | 267 | ||
266 | lockdep_assert_held(&local->mtx); | 268 | lockdep_assert_held(&local->mtx); |
267 | 269 | ||
@@ -275,12 +277,12 @@ static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
275 | aborted = true; | 277 | aborted = true; |
276 | 278 | ||
277 | if (WARN_ON(!local->scan_req)) | 279 | if (WARN_ON(!local->scan_req)) |
278 | return false; | 280 | return; |
279 | 281 | ||
280 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | 282 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { |
281 | int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req); | 283 | int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req); |
282 | if (rc == 0) | 284 | if (rc == 0) |
283 | return false; | 285 | return; |
284 | } | 286 | } |
285 | 287 | ||
286 | kfree(local->hw_scan_req); | 288 | kfree(local->hw_scan_req); |
@@ -294,26 +296,11 @@ static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
294 | local->scanning = 0; | 296 | local->scanning = 0; |
295 | local->scan_channel = NULL; | 297 | local->scan_channel = NULL; |
296 | 298 | ||
297 | return true; | ||
298 | } | ||
299 | |||
300 | static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, | ||
301 | bool was_hw_scan) | ||
302 | { | ||
303 | struct ieee80211_local *local = hw_to_local(hw); | ||
304 | bool on_oper_chan; | ||
305 | bool enable_beacons = false; | ||
306 | |||
307 | mutex_lock(&local->mtx); | ||
308 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); | 299 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); |
309 | 300 | ||
310 | WARN_ON(local->scanning & (SCAN_SW_SCANNING | SCAN_HW_SCANNING)); | 301 | if (was_hw_scan || !on_oper_chan) |
311 | |||
312 | if (was_hw_scan || !on_oper_chan) { | ||
313 | if (WARN_ON(local->scan_channel)) | ||
314 | local->scan_channel = NULL; | ||
315 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 302 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
316 | } else | 303 | else |
317 | /* Set power back to normal operating levels. */ | 304 | /* Set power back to normal operating levels. */ |
318 | ieee80211_hw_config(local, 0); | 305 | ieee80211_hw_config(local, 0); |
319 | 306 | ||
@@ -331,7 +318,6 @@ static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, | |||
331 | } | 318 | } |
332 | 319 | ||
333 | ieee80211_recalc_idle(local); | 320 | ieee80211_recalc_idle(local); |
334 | mutex_unlock(&local->mtx); | ||
335 | 321 | ||
336 | ieee80211_mlme_notify_scan_completed(local); | 322 | ieee80211_mlme_notify_scan_completed(local); |
337 | ieee80211_ibss_notify_scan_completed(local); | 323 | ieee80211_ibss_notify_scan_completed(local); |
@@ -686,12 +672,14 @@ void ieee80211_scan_work(struct work_struct *work) | |||
686 | { | 672 | { |
687 | struct ieee80211_local *local = | 673 | struct ieee80211_local *local = |
688 | container_of(work, struct ieee80211_local, scan_work.work); | 674 | container_of(work, struct ieee80211_local, scan_work.work); |
689 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | 675 | struct ieee80211_sub_if_data *sdata; |
690 | unsigned long next_delay = 0; | 676 | unsigned long next_delay = 0; |
691 | bool aborted, hw_scan, finish; | 677 | bool aborted, hw_scan; |
692 | 678 | ||
693 | mutex_lock(&local->mtx); | 679 | mutex_lock(&local->mtx); |
694 | 680 | ||
681 | sdata = local->scan_sdata; | ||
682 | |||
695 | if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { | 683 | if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { |
696 | aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); | 684 | aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); |
697 | goto out_complete; | 685 | goto out_complete; |
@@ -755,17 +743,11 @@ void ieee80211_scan_work(struct work_struct *work) | |||
755 | } while (next_delay == 0); | 743 | } while (next_delay == 0); |
756 | 744 | ||
757 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); | 745 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); |
758 | mutex_unlock(&local->mtx); | 746 | goto out; |
759 | return; | ||
760 | 747 | ||
761 | out_complete: | 748 | out_complete: |
762 | hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | 749 | hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); |
763 | finish = __ieee80211_scan_completed(&local->hw, aborted, hw_scan); | 750 | __ieee80211_scan_completed(&local->hw, aborted, hw_scan); |
764 | mutex_unlock(&local->mtx); | ||
765 | if (finish) | ||
766 | __ieee80211_scan_completed_finish(&local->hw, hw_scan); | ||
767 | return; | ||
768 | |||
769 | out: | 751 | out: |
770 | mutex_unlock(&local->mtx); | 752 | mutex_unlock(&local->mtx); |
771 | } | 753 | } |
@@ -835,7 +817,6 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, | |||
835 | void ieee80211_scan_cancel(struct ieee80211_local *local) | 817 | void ieee80211_scan_cancel(struct ieee80211_local *local) |
836 | { | 818 | { |
837 | bool abortscan; | 819 | bool abortscan; |
838 | bool finish = false; | ||
839 | 820 | ||
840 | /* | 821 | /* |
841 | * We are only canceling software scan, or deferred scan that was not | 822 | * We are only canceling software scan, or deferred scan that was not |
@@ -855,14 +836,17 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) | |||
855 | 836 | ||
856 | mutex_lock(&local->mtx); | 837 | mutex_lock(&local->mtx); |
857 | abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); | 838 | abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); |
858 | if (abortscan) | ||
859 | finish = __ieee80211_scan_completed(&local->hw, true, false); | ||
860 | mutex_unlock(&local->mtx); | ||
861 | |||
862 | if (abortscan) { | 839 | if (abortscan) { |
863 | /* The scan is canceled, but stop work from being pending */ | 840 | /* |
864 | cancel_delayed_work_sync(&local->scan_work); | 841 | * The scan is canceled, but stop work from being pending. |
842 | * | ||
843 | * If the work is currently running, it must be blocked on | ||
844 | * the mutex, but we'll set scan_sdata = NULL and it'll | ||
845 | * simply exit once it acquires the mutex. | ||
846 | */ | ||
847 | cancel_delayed_work(&local->scan_work); | ||
848 | /* and clean up */ | ||
849 | __ieee80211_scan_completed(&local->hw, true, false); | ||
865 | } | 850 | } |
866 | if (finish) | 851 | mutex_unlock(&local->mtx); |
867 | __ieee80211_scan_completed_finish(&local->hw, false); | ||
868 | } | 852 | } |