summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/iwlwifi/iwl-scan.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2011-09-23 02:15:00 +0400
committerJohn W. Linville <linville@tuxdriver.com>2011-09-27 22:34:09 +0400
commit84b1bec6d716fc8c289e2530cab109a6e097455b (patch)
tree91659c23350ca7d93fa383ba9ea439d360140b8b /drivers/net/wireless/iwlwifi/iwl-scan.c
parenta2fa2462f05115722beb2443d081a72f4f4450ea (diff)
downloadlinux-84b1bec6d716fc8c289e2530cab109a6e097455b.tar.xz
iwlagn: fix scan complete processing
When we cancel a scan, the completion runs only from the workqueue. This can cause the remain-on-channel scan to fail when another one was just canceled, because we're still aborting it. To fix this, run the completion inline with the lock still held before returning from iwl_scan_cancel_timeout(). Also, to avoid the scan complete work from completing a new scan prematurely, add a new STATUS_SCAN_COMPLETE bit. Reported-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Tested-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-scan.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c36
1 files changed, 32 insertions, 4 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index e50338b47593..c5c95d5319b1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -118,6 +118,11 @@ static void iwl_process_scan_complete(struct iwl_priv *priv)
{
bool aborted;
+ lockdep_assert_held(&priv->shrd->mutex);
+
+ if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status))
+ return;
+
IWL_DEBUG_SCAN(priv, "Completed scan.\n");
cancel_delayed_work(&priv->scan_check);
@@ -181,6 +186,7 @@ void iwl_force_scan_end(struct iwl_priv *priv)
clear_bit(STATUS_SCANNING, &priv->shrd->status);
clear_bit(STATUS_SCAN_HW, &priv->shrd->status);
clear_bit(STATUS_SCAN_ABORTING, &priv->shrd->status);
+ clear_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status);
iwl_complete_scan(priv, true);
}
@@ -235,9 +241,24 @@ void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms)
while (time_before_eq(jiffies, timeout)) {
if (!test_bit(STATUS_SCAN_HW, &priv->shrd->status))
- break;
+ goto finished;
msleep(20);
}
+
+ return;
+
+ finished:
+ /*
+ * Now STATUS_SCAN_HW is clear. This means that the
+ * device finished, but the background work is going
+ * to execute at best as soon as we release the mutex.
+ * Since we need to be able to issue a new scan right
+ * after this function returns, run the complete here.
+ * The STATUS_SCAN_COMPLETE bit will then be cleared
+ * and prevent the background work from "completing"
+ * a possible new scan.
+ */
+ iwl_process_scan_complete(priv);
}
/* Service response to REPLY_SCAN_CMD (0x80) */
@@ -321,13 +342,20 @@ static int iwl_rx_scan_complete_notif(struct iwl_priv *priv,
scan_notif->tsf_low,
scan_notif->tsf_high, scan_notif->status);
- /* The HW is no longer scanning */
- clear_bit(STATUS_SCAN_HW, &priv->shrd->status);
-
IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n",
(priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2",
jiffies_to_msecs(jiffies - priv->scan_start));
+ /*
+ * When aborting, we run the scan completed background work inline
+ * and the background work must then do nothing. The SCAN_COMPLETE
+ * bit helps implement that logic and thus needs to be set before
+ * queueing the work. Also, since the scan abort waits for SCAN_HW
+ * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW
+ * to avoid a race there.
+ */
+ set_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status);
+ clear_bit(STATUS_SCAN_HW, &priv->shrd->status);
queue_work(priv->shrd->workqueue, &priv->scan_completed);
if (priv->iw_mode != NL80211_IFTYPE_ADHOC &&