diff options
author | Reinette Chatre <reinette.chatre@intel.com> | 2009-05-15 19:13:46 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-05-20 14:29:53 -0400 |
commit | fbc9f97bbf5e1eaee562eba93dc60faaff3f3bfa (patch) | |
tree | b9ee621a651efb7f8daeed55210106f2466daae5 /drivers/net/wireless/iwlwifi/iwl-scan.c | |
parent | a54be5d43aa2d6febc5a4f8dd3b87b9429b60437 (diff) |
iwlwifi: do not cancel delayed work inside spin_lock_irqsave
Calling cancel_delayed_work() from inside
spin_lock_irqsave, introduces a potential deadlock.
As explained by Johannes Berg <johannes@sipsolutions.net>
A - lock
T - timer
phase CPU 1 CPU 2
---------------------------------------------
some place that calls
cancel_timer_sync()
(which is the | code)
lock-irq(A)
| "lock-irq"(T)
| "unlock"(T)
| wait(T)
unlock(A)
timer softirq
"lock"(T)
run(T)
"unlock"(T)
irq handler
lock(A)
unlock(A)
Now all that again, interleaved, leading to deadlock:
lock-irq(A)
"lock"(T)
run(T)
IRQ during or maybe
before run(T) --> lock(A)
"lock-irq"(T)
wait(T)
We fix this by moving the call to cancel_delayed_work() into workqueue.
There are cases where the work may not actually be queued or running
at the time we are trying to cancel it, but cancel_delayed_work() is
able to deal with this.
Also cleanup iwl_set_mode related to this call. This function
(iwl_set_mode) is only called when bringing interface up and there will
thus not be any scanning done. No need to try to cancel scanning.
Fixes http://bugzilla.kernel.org/show_bug.cgi?id=13224, which was also
reported at http://marc.info/?l=linux-wireless&m=124081921903223&w=2 .
Tested-by: Miles Lane <miles.lane@gmail.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Acked-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-scan.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-scan.c | 7 |
1 files changed, 4 insertions, 3 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index e7c65c4f741b..6330b91e37ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c | |||
@@ -227,9 +227,6 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, | |||
227 | /* The HW is no longer scanning */ | 227 | /* The HW is no longer scanning */ |
228 | clear_bit(STATUS_SCAN_HW, &priv->status); | 228 | clear_bit(STATUS_SCAN_HW, &priv->status); |
229 | 229 | ||
230 | /* The scan completion notification came in, so kill that timer... */ | ||
231 | cancel_delayed_work(&priv->scan_check); | ||
232 | |||
233 | IWL_DEBUG_INFO(priv, "Scan pass on %sGHz took %dms\n", | 230 | IWL_DEBUG_INFO(priv, "Scan pass on %sGHz took %dms\n", |
234 | (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ? | 231 | (priv->scan_bands & BIT(IEEE80211_BAND_2GHZ)) ? |
235 | "2.4" : "5.2", | 232 | "2.4" : "5.2", |
@@ -712,6 +709,8 @@ static void iwl_bg_request_scan(struct work_struct *data) | |||
712 | 709 | ||
713 | mutex_lock(&priv->mutex); | 710 | mutex_lock(&priv->mutex); |
714 | 711 | ||
712 | cancel_delayed_work(&priv->scan_check); | ||
713 | |||
715 | if (!iwl_is_ready(priv)) { | 714 | if (!iwl_is_ready(priv)) { |
716 | IWL_WARN(priv, "request scan called when driver not ready.\n"); | 715 | IWL_WARN(priv, "request scan called when driver not ready.\n"); |
717 | goto done; | 716 | goto done; |
@@ -925,6 +924,8 @@ void iwl_bg_scan_completed(struct work_struct *work) | |||
925 | 924 | ||
926 | IWL_DEBUG_SCAN(priv, "SCAN complete scan\n"); | 925 | IWL_DEBUG_SCAN(priv, "SCAN complete scan\n"); |
927 | 926 | ||
927 | cancel_delayed_work(&priv->scan_check); | ||
928 | |||
928 | ieee80211_scan_completed(priv->hw, false); | 929 | ieee80211_scan_completed(priv->hw, false); |
929 | 930 | ||
930 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) | 931 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |