iwlagn: fix scan complete processing
authorJohannes Berg <johannes.berg@intel.com>
Thu, 22 Sep 2011 22:15:00 +0000 (15:15 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 27 Sep 2011 18:34:09 +0000 (14:34 -0400)
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>
drivers/net/wireless/iwlwifi/iwl-scan.c
drivers/net/wireless/iwlwifi/iwl-shared.h

index e50338b47593bd1f9a44c1fb7453e791256c956c..c5c95d5319b19f172deb0b806e764e10d1181477 100644 (file)
@@ -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 &&
index 7abafe16de9a41527b4f2d6213938b26ad90c8cb..8747bbdf898311cfc2a9f75b3111dfb0329577eb 100644 (file)
@@ -489,6 +489,7 @@ static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv,
 #define STATUS_FW_ERROR                17
 #define STATUS_DEVICE_ENABLED  18
 #define STATUS_CHANNEL_SWITCH_PENDING 19
+#define STATUS_SCAN_COMPLETE   20
 
 static inline int iwl_is_ready(struct iwl_shared *shrd)
 {