summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuciano Coelho <luciano.coelho@intel.com>2015-01-24 10:30:02 +0200
committerJohannes Berg <johannes.berg@intel.com>2015-01-27 09:58:46 +0100
commit9120d94e8f9abd3eb9f00a5aaa6eca85cdf4f439 (patch)
treeb141172aa32ce2fa5f9a531efae302f5c04ac112
parent225b818982403120ce1f5e7d4b3e5245e0399775 (diff)
mac80211: handle potential race between suspend and scan completion
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>
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/util.c12
2 files changed, 14 insertions, 1 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index bdb3e3bc750..3afe3682470 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -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));
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index c65d03f3c16..8428f4a9547 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -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);