140 lines
5.1 KiB
Diff
140 lines
5.1 KiB
Diff
|
commit 88be026490ed89c2ffead81a52531fbac5507e01
|
||
|
Author: Johannes Berg <johannes.berg@intel.com>
|
||
|
Date: Wed Apr 7 00:21:36 2010 -0700
|
||
|
|
||
|
iwlwifi: fix scan races
|
||
|
|
||
|
When an internal scan is started, nothing protects the
|
||
|
is_internal_short_scan variable which can cause crashes,
|
||
|
cf. https://bugzilla.kernel.org/show_bug.cgi?id=15667.
|
||
|
Fix this by making the short scan request use the mutex
|
||
|
for locking, which requires making the request go to a
|
||
|
work struct so that it can sleep.
|
||
|
|
||
|
Reported-by: Peter Zijlstra <peterz@infradead.org>
|
||
|
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||
|
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
|
||
|
|
||
|
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
|
||
|
index e4c2e1e..ba0fdba 100644
|
||
|
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
|
||
|
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
|
||
|
@@ -3330,6 +3330,7 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
|
||
|
|
||
|
cancel_delayed_work_sync(&priv->init_alive_start);
|
||
|
cancel_delayed_work(&priv->scan_check);
|
||
|
+ cancel_work_sync(&priv->start_internal_scan);
|
||
|
cancel_delayed_work(&priv->alive_start);
|
||
|
cancel_work_sync(&priv->beacon_update);
|
||
|
del_timer_sync(&priv->statistics_periodic);
|
||
|
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
|
||
|
index 894bcb8..1459cdb 100644
|
||
|
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
|
||
|
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
|
||
|
@@ -3357,7 +3357,6 @@ static void iwl_force_rf_reset(struct iwl_priv *priv)
|
||
|
*/
|
||
|
IWL_DEBUG_INFO(priv, "perform radio reset.\n");
|
||
|
iwl_internal_short_hw_scan(priv);
|
||
|
- return;
|
||
|
}
|
||
|
|
||
|
|
||
|
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
|
||
|
index 732590f..36940a9 100644
|
||
|
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
|
||
|
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
|
||
|
@@ -506,7 +506,7 @@ void iwl_init_scan_params(struct iwl_priv *priv);
|
||
|
int iwl_scan_cancel(struct iwl_priv *priv);
|
||
|
int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
|
||
|
int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
|
||
|
-int iwl_internal_short_hw_scan(struct iwl_priv *priv);
|
||
|
+void iwl_internal_short_hw_scan(struct iwl_priv *priv);
|
||
|
int iwl_force_reset(struct iwl_priv *priv, int mode);
|
||
|
u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
|
||
|
const u8 *ie, int ie_len, int left);
|
||
|
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
|
||
|
index 6054c5f..ef1720a 100644
|
||
|
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
|
||
|
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
|
||
|
@@ -1296,6 +1296,7 @@ struct iwl_priv {
|
||
|
struct work_struct tt_work;
|
||
|
struct work_struct ct_enter;
|
||
|
struct work_struct ct_exit;
|
||
|
+ struct work_struct start_internal_scan;
|
||
|
|
||
|
struct tasklet_struct irq_tasklet;
|
||
|
|
||
|
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
|
||
|
index bd2f7c4..5062f4e 100644
|
||
|
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
|
||
|
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
|
||
|
@@ -469,6 +469,8 @@ EXPORT_SYMBOL(iwl_init_scan_params);
|
||
|
|
||
|
static int iwl_scan_initiate(struct iwl_priv *priv)
|
||
|
{
|
||
|
+ WARN_ON(!mutex_is_locked(&priv->mutex));
|
||
|
+
|
||
|
IWL_DEBUG_INFO(priv, "Starting scan...\n");
|
||
|
set_bit(STATUS_SCANNING, &priv->status);
|
||
|
priv->is_internal_short_scan = false;
|
||
|
@@ -546,24 +548,31 @@ EXPORT_SYMBOL(iwl_mac_hw_scan);
|
||
|
* internal short scan, this function should only been called while associated.
|
||
|
* It will reset and tune the radio to prevent possible RF related problem
|
||
|
*/
|
||
|
-int iwl_internal_short_hw_scan(struct iwl_priv *priv)
|
||
|
+void iwl_internal_short_hw_scan(struct iwl_priv *priv)
|
||
|
{
|
||
|
- int ret = 0;
|
||
|
+ queue_work(priv->workqueue, &priv->start_internal_scan);
|
||
|
+}
|
||
|
+
|
||
|
+static void iwl_bg_start_internal_scan(struct work_struct *work)
|
||
|
+{
|
||
|
+ struct iwl_priv *priv =
|
||
|
+ container_of(work, struct iwl_priv, start_internal_scan);
|
||
|
+
|
||
|
+ mutex_lock(&priv->mutex);
|
||
|
|
||
|
if (!iwl_is_ready_rf(priv)) {
|
||
|
- ret = -EIO;
|
||
|
IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
|
||
|
- goto out;
|
||
|
+ goto unlock;
|
||
|
}
|
||
|
+
|
||
|
if (test_bit(STATUS_SCANNING, &priv->status)) {
|
||
|
IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
|
||
|
- ret = -EAGAIN;
|
||
|
- goto out;
|
||
|
+ goto unlock;
|
||
|
}
|
||
|
+
|
||
|
if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
|
||
|
IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
|
||
|
- ret = -EAGAIN;
|
||
|
- goto out;
|
||
|
+ goto unlock;
|
||
|
}
|
||
|
|
||
|
priv->scan_bands = 0;
|
||
|
@@ -576,9 +585,8 @@ int iwl_internal_short_hw_scan(struct iwl_priv *priv)
|
||
|
set_bit(STATUS_SCANNING, &priv->status);
|
||
|
priv->is_internal_short_scan = true;
|
||
|
queue_work(priv->workqueue, &priv->request_scan);
|
||
|
-
|
||
|
-out:
|
||
|
- return ret;
|
||
|
+ unlock:
|
||
|
+ mutex_unlock(&priv->mutex);
|
||
|
}
|
||
|
EXPORT_SYMBOL(iwl_internal_short_hw_scan);
|
||
|
|
||
|
@@ -964,6 +972,7 @@ void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
|
||
|
INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed);
|
||
|
INIT_WORK(&priv->request_scan, iwl_bg_request_scan);
|
||
|
INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
|
||
|
+ INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan);
|
||
|
INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
|
||
|
}
|
||
|
EXPORT_SYMBOL(iwl_setup_scan_deferred_work);
|