Chuck Ebbert b5010d0
From reinette.chatre@intel.com Thu May 13 17:49:59 2010
Chuck Ebbert b5010d0
Return-path: <reinette.chatre@intel.com>
Chuck Ebbert b5010d0
Envelope-to: linville@tuxdriver.com
Chuck Ebbert b5010d0
Delivery-date: Thu, 13 May 2010 17:49:59 -0400
Chuck Ebbert b5010d0
Received: from mga09.intel.com ([134.134.136.24])
Chuck Ebbert b5010d0
	by smtp.tuxdriver.com with esmtp (Exim 4.63)
Chuck Ebbert b5010d0
	(envelope-from <reinette.chatre@intel.com>)
Chuck Ebbert b5010d0
	id 1OCgI1-0007H3-Eg
Chuck Ebbert b5010d0
	for linville@tuxdriver.com; Thu, 13 May 2010 17:49:59 -0400
Chuck Ebbert b5010d0
Received: from orsmga002.jf.intel.com ([10.7.209.21])
Chuck Ebbert b5010d0
  by orsmga102.jf.intel.com with ESMTP; 13 May 2010 14:48:04 -0700
Chuck Ebbert b5010d0
X-ExtLoop1: 1
Chuck Ebbert b5010d0
X-IronPort-AV: E=Sophos;i="4.53,224,1272870000"; 
Chuck Ebbert b5010d0
   d="scan'208";a="517743256"
Chuck Ebbert b5010d0
Received: from rchatre-desk.amr.corp.intel.com.jf.intel.com (HELO localhost.localdomain) ([134.134.15.94])
Chuck Ebbert b5010d0
  by orsmga002.jf.intel.com with ESMTP; 13 May 2010 14:49:12 -0700
Chuck Ebbert b5010d0
From: Reinette Chatre <reinette.chatre@intel.com>
Chuck Ebbert b5010d0
To: linville@tuxdriver.com
Chuck Ebbert b5010d0
Cc: linux-wireless@vger.kernel.org, ipw3945-devel@lists.sourceforge.net, Reinette Chatre <reinette.chatre@intel.com>
Chuck Ebbert b5010d0
Subject: [PATCH 1/2] iwlwifi: fix internal scan race
Chuck Ebbert b5010d0
Date: Thu, 13 May 2010 14:49:44 -0700
Chuck Ebbert b5010d0
Message-Id: <1273787385-9248-2-git-send-email-reinette.chatre@intel.com>
Chuck Ebbert b5010d0
X-Mailer: git-send-email 1.6.3.3
Chuck Ebbert b5010d0
In-Reply-To: <1273787385-9248-1-git-send-email-reinette.chatre@intel.com>
Chuck Ebbert b5010d0
References: <1273787385-9248-1-git-send-email-reinette.chatre@intel.com>
Chuck Ebbert b5010d0
X-Spam-Score: -4.2 (----)
Chuck Ebbert b5010d0
X-Spam-Status: No
Chuck Ebbert b5010d0
Status: RO
Chuck Ebbert b5010d0
Content-Length: 3370
Chuck Ebbert b5010d0
Lines: 91
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
From: Reinette Chatre <reinette.chatre@intel.com>
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
It is possible for internal scan to race against itself if the device is
Chuck Ebbert b5010d0
not returning the scan results from first requests. What happens in this
Chuck Ebbert b5010d0
case is the cleanup done during the abort of the first internal scan also
Chuck Ebbert b5010d0
cleans up part of the new scan, causing it to access memory it shouldn't.
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
Here are details:
Chuck Ebbert b5010d0
* First internal scan is triggered and scan command sent to device.
Chuck Ebbert b5010d0
* After seven seconds there is no scan results so the watchdog timer
Chuck Ebbert b5010d0
  triggers a scan abort.
Chuck Ebbert b5010d0
* The scan abort succeeds and a SCAN_COMPLETE_NOTIFICATION is received for
Chuck Ebbert b5010d0
 failed scan.
Chuck Ebbert b5010d0
* During processing of SCAN_COMPLETE_NOTIFICATION we clear STATUS_SCANNING
Chuck Ebbert b5010d0
  and queue the "scan_completed" work.
Chuck Ebbert b5010d0
** At this time, since the problem that caused the internal scan in first
Chuck Ebbert b5010d0
   place is still present, a new internal scan is triggered.
Chuck Ebbert b5010d0
The behavior at this point is a bit different between 2.6.34 and 2.6.35
Chuck Ebbert b5010d0
since 2.6.35 has a lot of this synchronized. The rest of the race
Chuck Ebbert b5010d0
description will thus be generalized.
Chuck Ebbert b5010d0
** As part of preparing for the scan "is_internal_short_scan" is set to
Chuck Ebbert b5010d0
true.
Chuck Ebbert b5010d0
* At this point the completion work for fist scan is run. As part of this
Chuck Ebbert b5010d0
  there is some locking missing around the "is_internal_short_scan"
Chuck Ebbert b5010d0
  variable and it is set to "false".
Chuck Ebbert b5010d0
** Now the second scan runs and it considers itself a real (not internal0
Chuck Ebbert b5010d0
   scan and thus causes problems with wrong memory being accessed.
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
The fix is twofold.
Chuck Ebbert b5010d0
* Since "is_internal_short_scan" should be protected by mutex, fix this in
Chuck Ebbert b5010d0
  scan completion work so that changes to it can be serialized.
Chuck Ebbert b5010d0
* Do not queue a new internal scan if one is in progress.
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
This fixes https://bugzilla.kernel.org/show_bug.cgi?id=15824
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Chuck Ebbert b5010d0
---
Chuck Ebbert b5010d0
 drivers/net/wireless/iwlwifi/iwl-scan.c |   21 ++++++++++++++++++---
Chuck Ebbert b5010d0
 1 files changed, 18 insertions(+), 3 deletions(-)
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
Chuck Ebbert b5010d0
index 2367286..a2c4855 100644
Chuck Ebbert b5010d0
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
Chuck Ebbert b5010d0
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
Chuck Ebbert b5010d0
@@ -560,6 +560,11 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
Chuck Ebbert b5010d0
 
Chuck Ebbert b5010d0
 	mutex_lock(&priv->mutex);
Chuck Ebbert b5010d0
 
Chuck Ebbert b5010d0
+	if (priv->is_internal_short_scan == true) {
Chuck Ebbert b5010d0
+		IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
Chuck Ebbert b5010d0
+		goto unlock;
Chuck Ebbert b5010d0
+	}
Chuck Ebbert b5010d0
+
Chuck Ebbert b5010d0
 	if (!iwl_is_ready_rf(priv)) {
Chuck Ebbert b5010d0
 		IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
Chuck Ebbert b5010d0
 		goto unlock;
Chuck Ebbert b5010d0
@@ -957,17 +962,27 @@ void iwl_bg_scan_completed(struct work_struct *work)
Chuck Ebbert b5010d0
 {
Chuck Ebbert b5010d0
 	struct iwl_priv *priv =
Chuck Ebbert b5010d0
 	    container_of(work, struct iwl_priv, scan_completed);
Chuck Ebbert b5010d0
+	bool internal = false;
Chuck Ebbert b5010d0
 
Chuck Ebbert b5010d0
 	IWL_DEBUG_SCAN(priv, "SCAN complete scan\n");
Chuck Ebbert b5010d0
 
Chuck Ebbert b5010d0
 	cancel_delayed_work(&priv->scan_check);
Chuck Ebbert b5010d0
 
Chuck Ebbert b5010d0
-	if (!priv->is_internal_short_scan)
Chuck Ebbert b5010d0
-		ieee80211_scan_completed(priv->hw, false);
Chuck Ebbert b5010d0
-	else {
Chuck Ebbert b5010d0
+	mutex_lock(&priv->mutex);
Chuck Ebbert b5010d0
+	if (priv->is_internal_short_scan) {
Chuck Ebbert b5010d0
 		priv->is_internal_short_scan = false;
Chuck Ebbert b5010d0
 		IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
Chuck Ebbert b5010d0
+		internal = true;
Chuck Ebbert b5010d0
 	}
Chuck Ebbert b5010d0
+	mutex_unlock(&priv->mutex);
Chuck Ebbert b5010d0
+
Chuck Ebbert b5010d0
+	/*
Chuck Ebbert b5010d0
+	 * Do not hold mutex here since this will cause mac80211 to call
Chuck Ebbert b5010d0
+	 * into driver again into functions that will attempt to take
Chuck Ebbert b5010d0
+	 * mutex.
Chuck Ebbert b5010d0
+	 */
Chuck Ebbert b5010d0
+	if (!internal)
Chuck Ebbert b5010d0
+		ieee80211_scan_completed(priv->hw, false);
Chuck Ebbert b5010d0
 
Chuck Ebbert b5010d0
 	if (test_bit(STATUS_EXIT_PENDING, &priv->status))
Chuck Ebbert b5010d0
 		return;
Chuck Ebbert b5010d0
-- 
Chuck Ebbert b5010d0
1.6.3.3
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0
Chuck Ebbert b5010d0