aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2010-04-07 03:21:36 -0400
committerReinette Chatre <reinette.chatre@intel.com>2010-04-16 16:27:10 -0400
commit88be026490ed89c2ffead81a52531fbac5507e01 (patch)
tree94f8727e5027a09f24623bd4dcecbbbe1be6922a /drivers/net
parent8b9fce77737ae9983f61ec56cd53f52fb738b2c7 (diff)
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>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-dev.h1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c31
5 files changed, 23 insertions, 13 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index e4c2e1e448ad..ba0fdba602cd 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)
3330 3330
3331 cancel_delayed_work_sync(&priv->init_alive_start); 3331 cancel_delayed_work_sync(&priv->init_alive_start);
3332 cancel_delayed_work(&priv->scan_check); 3332 cancel_delayed_work(&priv->scan_check);
3333 cancel_work_sync(&priv->start_internal_scan);
3333 cancel_delayed_work(&priv->alive_start); 3334 cancel_delayed_work(&priv->alive_start);
3334 cancel_work_sync(&priv->beacon_update); 3335 cancel_work_sync(&priv->beacon_update);
3335 del_timer_sync(&priv->statistics_periodic); 3336 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 894bcb8b8b37..1459cdb26f8d 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)
3357 */ 3357 */
3358 IWL_DEBUG_INFO(priv, "perform radio reset.\n"); 3358 IWL_DEBUG_INFO(priv, "perform radio reset.\n");
3359 iwl_internal_short_hw_scan(priv); 3359 iwl_internal_short_hw_scan(priv);
3360 return;
3361} 3360}
3362 3361
3363 3362
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 732590f5fe30..36940a9ec6b9 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);
506int iwl_scan_cancel(struct iwl_priv *priv); 506int iwl_scan_cancel(struct iwl_priv *priv);
507int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); 507int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
508int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); 508int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
509int iwl_internal_short_hw_scan(struct iwl_priv *priv); 509void iwl_internal_short_hw_scan(struct iwl_priv *priv);
510int iwl_force_reset(struct iwl_priv *priv, int mode); 510int iwl_force_reset(struct iwl_priv *priv, int mode);
511u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, 511u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
512 const u8 *ie, int ie_len, int left); 512 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 6054c5fba0c1..ef1720a852e9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -1296,6 +1296,7 @@ struct iwl_priv {
1296 struct work_struct tt_work; 1296 struct work_struct tt_work;
1297 struct work_struct ct_enter; 1297 struct work_struct ct_enter;
1298 struct work_struct ct_exit; 1298 struct work_struct ct_exit;
1299 struct work_struct start_internal_scan;
1299 1300
1300 struct tasklet_struct irq_tasklet; 1301 struct tasklet_struct irq_tasklet;
1301 1302
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index bd2f7c420563..5062f4ebb6a9 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);
469 469
470static int iwl_scan_initiate(struct iwl_priv *priv) 470static int iwl_scan_initiate(struct iwl_priv *priv)
471{ 471{
472 WARN_ON(!mutex_is_locked(&priv->mutex));
473
472 IWL_DEBUG_INFO(priv, "Starting scan...\n"); 474 IWL_DEBUG_INFO(priv, "Starting scan...\n");
473 set_bit(STATUS_SCANNING, &priv->status); 475 set_bit(STATUS_SCANNING, &priv->status);
474 priv->is_internal_short_scan = false; 476 priv->is_internal_short_scan = false;
@@ -546,24 +548,31 @@ EXPORT_SYMBOL(iwl_mac_hw_scan);
546 * internal short scan, this function should only been called while associated. 548 * internal short scan, this function should only been called while associated.
547 * It will reset and tune the radio to prevent possible RF related problem 549 * It will reset and tune the radio to prevent possible RF related problem
548 */ 550 */
549int iwl_internal_short_hw_scan(struct iwl_priv *priv) 551void iwl_internal_short_hw_scan(struct iwl_priv *priv)
550{ 552{
551 int ret = 0; 553 queue_work(priv->workqueue, &priv->start_internal_scan);
554}
555
556static void iwl_bg_start_internal_scan(struct work_struct *work)
557{
558 struct iwl_priv *priv =
559 container_of(work, struct iwl_priv, start_internal_scan);
560
561 mutex_lock(&priv->mutex);
552 562
553 if (!iwl_is_ready_rf(priv)) { 563 if (!iwl_is_ready_rf(priv)) {
554 ret = -EIO;
555 IWL_DEBUG_SCAN(priv, "not ready or exit pending\n"); 564 IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
556 goto out; 565 goto unlock;
557 } 566 }
567
558 if (test_bit(STATUS_SCANNING, &priv->status)) { 568 if (test_bit(STATUS_SCANNING, &priv->status)) {
559 IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); 569 IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
560 ret = -EAGAIN; 570 goto unlock;
561 goto out;
562 } 571 }
572
563 if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { 573 if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
564 IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n"); 574 IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
565 ret = -EAGAIN; 575 goto unlock;
566 goto out;
567 } 576 }
568 577
569 priv->scan_bands = 0; 578 priv->scan_bands = 0;
@@ -576,9 +585,8 @@ int iwl_internal_short_hw_scan(struct iwl_priv *priv)
576 set_bit(STATUS_SCANNING, &priv->status); 585 set_bit(STATUS_SCANNING, &priv->status);
577 priv->is_internal_short_scan = true; 586 priv->is_internal_short_scan = true;
578 queue_work(priv->workqueue, &priv->request_scan); 587 queue_work(priv->workqueue, &priv->request_scan);
579 588 unlock:
580out: 589 mutex_unlock(&priv->mutex);
581 return ret;
582} 590}
583EXPORT_SYMBOL(iwl_internal_short_hw_scan); 591EXPORT_SYMBOL(iwl_internal_short_hw_scan);
584 592
@@ -964,6 +972,7 @@ void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
964 INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed); 972 INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed);
965 INIT_WORK(&priv->request_scan, iwl_bg_request_scan); 973 INIT_WORK(&priv->request_scan, iwl_bg_request_scan);
966 INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan); 974 INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
975 INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan);
967 INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); 976 INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
968} 977}
969EXPORT_SYMBOL(iwl_setup_scan_deferred_work); 978EXPORT_SYMBOL(iwl_setup_scan_deferred_work);