mac80211: handle potential race between suspend and scan completion
authorLuciano Coelho <luciano.coelho@intel.com>
Sat, 24 Jan 2015 08:30:02 +0000 (10:30 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 27 Jan 2015 08:58:46 +0000 (09:58 +0100)
If suspend starts while ieee80211_scan_completed() is running, between
the point where SCAN_COMPLETED is set and the work is queued,
ieee80211_scan_cancel() will not catch the work and we may finish
suspending before the work is actually executed, leaving the scan
running while suspended.

To fix this race, queue the scan work during resume if the
SCAN_COMPLETED flag is set and flush it immediately.

Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/ieee80211_i.h
net/mac80211/util.c

index bdb3e3bc7503acff60dd97d34ceec227d09b9a9c..3afe36824703f49dcfe374e103b6372a9851b8aa 100644 (file)
@@ -1752,7 +1752,8 @@ static inline int __ieee80211_resume(struct ieee80211_hw *hw)
 {
        struct ieee80211_local *local = hw_to_local(hw);
 
-       WARN(test_bit(SCAN_HW_SCANNING, &local->scanning),
+       WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) &&
+            !test_bit(SCAN_COMPLETED, &local->scanning),
                "%s: resume with hardware scan still in progress\n",
                wiphy_name(hw->wiphy));
 
index c65d03f3c1676507f3359a36892afc499bc91022..8428f4a954795657a32a24f77a0f9c9ae6591b7e 100644 (file)
@@ -2060,6 +2060,18 @@ int ieee80211_reconfig(struct ieee80211_local *local)
        mb();
        local->resuming = false;
 
+       /* It's possible that we don't handle the scan completion in
+        * time during suspend, so if it's still marked as completed
+        * here, queue the work and flush it to clean things up.
+        * Instead of calling the worker function directly here, we
+        * really queue it to avoid potential races with other flows
+        * scheduling the same work.
+        */
+       if (test_bit(SCAN_COMPLETED, &local->scanning)) {
+               ieee80211_queue_delayed_work(&local->hw, &local->scan_work, 0);
+               flush_delayed_work(&local->scan_work);
+       }
+
        if (local->open_count && !reconfig_due_to_wowlan)
                drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);