diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-04-07 03:21:36 -0400 |
---|---|---|
committer | Reinette Chatre <reinette.chatre@intel.com> | 2010-04-16 16:27:10 -0400 |
commit | 88be026490ed89c2ffead81a52531fbac5507e01 (patch) | |
tree | 94f8727e5027a09f24623bd4dcecbbbe1be6922a /drivers/net | |
parent | 8b9fce77737ae9983f61ec56cd53f52fb738b2c7 (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.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-dev.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-scan.c | 31 |
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); | |||
506 | int iwl_scan_cancel(struct iwl_priv *priv); | 506 | int iwl_scan_cancel(struct iwl_priv *priv); |
507 | int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); | 507 | int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); |
508 | int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); | 508 | int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req); |
509 | int iwl_internal_short_hw_scan(struct iwl_priv *priv); | 509 | void iwl_internal_short_hw_scan(struct iwl_priv *priv); |
510 | int iwl_force_reset(struct iwl_priv *priv, int mode); | 510 | int iwl_force_reset(struct iwl_priv *priv, int mode); |
511 | u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, | 511 | u16 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 | ||
470 | static int iwl_scan_initiate(struct iwl_priv *priv) | 470 | static 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 | */ |
549 | int iwl_internal_short_hw_scan(struct iwl_priv *priv) | 551 | void 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 | |||
556 | static 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: | |
580 | out: | 589 | mutex_unlock(&priv->mutex); |
581 | return ret; | ||
582 | } | 590 | } |
583 | EXPORT_SYMBOL(iwl_internal_short_hw_scan); | 591 | EXPORT_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 | } |
969 | EXPORT_SYMBOL(iwl_setup_scan_deferred_work); | 978 | EXPORT_SYMBOL(iwl_setup_scan_deferred_work); |