diff options
author | Rajkumar Manoharan <rmanohar@qca.qualcomm.com> | 2011-06-24 08:08:13 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-06-27 15:09:42 -0400 |
commit | f6b4e4d476b890e1ddebbed8ec4924f9c2750a31 (patch) | |
tree | c7b8beed1a1967722f8ce73b072057f7abe82c11 /drivers/net/wireless/ath | |
parent | 428bc8c3960d2b18cb9f0d90cfe197ec9a822a54 (diff) |
ath9k: Fix locking issue during tx completion
The received tx status of aggregated frame without BlockAck may
cause deaf state in AR5416 cards. So the driver does a reset to
recover. When this happens, we release the pcu_lock before doing
a reset as ath_rest acquires pcu_lock. This is ugly and also not
atomic. Fixing this addresses the TX DMA failure also.
ath_tx_complete_aggr can be called from different paths which
takes different variants of spin_lock. This patch also addresses
the following warning.
WARNING: at kernel/timer.c:1011 del_timer_sync+0x4e/0x50()
Call Trace:
<IRQ> [<ffffffff8104be3a>] warn_slowpath_common+0x7a/0xb0
[<ffffffff8104be85>] warn_slowpath_null+0x15/0x20
[<ffffffff8105915e>] del_timer_sync+0x4e/0x50
[<ffffffffa03726be>] ath_reset+0x3e/0x210 [ath9k]
[<ffffffff8135cdaf>] ? _raw_spin_unlock_bh+0x1f/0x30
[<ffffffffa037760a>] ath_tx_complete_aggr.isra.26+0x54a/0xa40 [ath9k]
Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/beacon.c | 2 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 13 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 7 |
3 files changed, 14 insertions, 8 deletions
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 0b6e3b65bb0b..e19a230dfff9 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c | |||
@@ -385,7 +385,9 @@ void ath_beacon_tasklet(unsigned long data) | |||
385 | ath_dbg(common, ATH_DBG_BSTUCK, | 385 | ath_dbg(common, ATH_DBG_BSTUCK, |
386 | "beacon is officially stuck\n"); | 386 | "beacon is officially stuck\n"); |
387 | sc->sc_flags |= SC_OP_TSF_RESET; | 387 | sc->sc_flags |= SC_OP_TSF_RESET; |
388 | spin_lock(&sc->sc_pcu_lock); | ||
388 | ath_reset(sc, true); | 389 | ath_reset(sc, true); |
390 | spin_unlock(&sc->sc_pcu_lock); | ||
389 | } | 391 | } |
390 | 392 | ||
391 | return; | 393 | return; |
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5ae303b11e68..9098aaad97a9 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -617,8 +617,11 @@ void ath_hw_check(struct work_struct *work) | |||
617 | ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, " | 617 | ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, " |
618 | "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1); | 618 | "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1); |
619 | if (busy >= 99) { | 619 | if (busy >= 99) { |
620 | if (++sc->hw_busy_count >= 3) | 620 | if (++sc->hw_busy_count >= 3) { |
621 | spin_lock_bh(&sc->sc_pcu_lock); | ||
621 | ath_reset(sc, true); | 622 | ath_reset(sc, true); |
623 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
624 | } | ||
622 | } else if (busy >= 0) | 625 | } else if (busy >= 0) |
623 | sc->hw_busy_count = 0; | 626 | sc->hw_busy_count = 0; |
624 | 627 | ||
@@ -637,7 +640,9 @@ static void ath_hw_pll_rx_hang_check(struct ath_softc *sc, u32 pll_sqsum) | |||
637 | /* Rx is hung for more than 500ms. Reset it */ | 640 | /* Rx is hung for more than 500ms. Reset it */ |
638 | ath_dbg(common, ATH_DBG_RESET, | 641 | ath_dbg(common, ATH_DBG_RESET, |
639 | "Possible RX hang, resetting"); | 642 | "Possible RX hang, resetting"); |
643 | spin_lock_bh(&sc->sc_pcu_lock); | ||
640 | ath_reset(sc, true); | 644 | ath_reset(sc, true); |
645 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
641 | count = 0; | 646 | count = 0; |
642 | } | 647 | } |
643 | } else | 648 | } else |
@@ -674,7 +679,9 @@ void ath9k_tasklet(unsigned long data) | |||
674 | 679 | ||
675 | if ((status & ATH9K_INT_FATAL) || | 680 | if ((status & ATH9K_INT_FATAL) || |
676 | (status & ATH9K_INT_BB_WATCHDOG)) { | 681 | (status & ATH9K_INT_BB_WATCHDOG)) { |
682 | spin_lock(&sc->sc_pcu_lock); | ||
677 | ath_reset(sc, true); | 683 | ath_reset(sc, true); |
684 | spin_unlock(&sc->sc_pcu_lock); | ||
678 | return; | 685 | return; |
679 | } | 686 | } |
680 | 687 | ||
@@ -980,7 +987,6 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) | |||
980 | del_timer_sync(&common->ani.timer); | 987 | del_timer_sync(&common->ani.timer); |
981 | 988 | ||
982 | ath9k_ps_wakeup(sc); | 989 | ath9k_ps_wakeup(sc); |
983 | spin_lock_bh(&sc->sc_pcu_lock); | ||
984 | 990 | ||
985 | ieee80211_stop_queues(hw); | 991 | ieee80211_stop_queues(hw); |
986 | 992 | ||
@@ -1023,7 +1029,6 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) | |||
1023 | } | 1029 | } |
1024 | 1030 | ||
1025 | ieee80211_wake_queues(hw); | 1031 | ieee80211_wake_queues(hw); |
1026 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
1027 | 1032 | ||
1028 | /* Start ANI */ | 1033 | /* Start ANI */ |
1029 | if (!common->disable_ani) | 1034 | if (!common->disable_ani) |
@@ -2326,9 +2331,9 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop) | |||
2326 | ath9k_ps_wakeup(sc); | 2331 | ath9k_ps_wakeup(sc); |
2327 | spin_lock_bh(&sc->sc_pcu_lock); | 2332 | spin_lock_bh(&sc->sc_pcu_lock); |
2328 | drain_txq = ath_drain_all_txq(sc, false); | 2333 | drain_txq = ath_drain_all_txq(sc, false); |
2329 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
2330 | if (!drain_txq) | 2334 | if (!drain_txq) |
2331 | ath_reset(sc, false); | 2335 | ath_reset(sc, false); |
2336 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
2332 | ath9k_ps_restore(sc); | 2337 | ath9k_ps_restore(sc); |
2333 | ieee80211_wake_queues(hw); | 2338 | ieee80211_wake_queues(hw); |
2334 | 2339 | ||
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index ec012b4317af..a1fed6c8ff95 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c | |||
@@ -565,11 +565,8 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq, | |||
565 | 565 | ||
566 | rcu_read_unlock(); | 566 | rcu_read_unlock(); |
567 | 567 | ||
568 | if (needreset) { | 568 | if (needreset) |
569 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
570 | ath_reset(sc, false); | 569 | ath_reset(sc, false); |
571 | spin_lock_bh(&sc->sc_pcu_lock); | ||
572 | } | ||
573 | } | 570 | } |
574 | 571 | ||
575 | static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, | 572 | static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, |
@@ -2169,7 +2166,9 @@ static void ath_tx_complete_poll_work(struct work_struct *work) | |||
2169 | if (needreset) { | 2166 | if (needreset) { |
2170 | ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET, | 2167 | ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_RESET, |
2171 | "tx hung, resetting the chip\n"); | 2168 | "tx hung, resetting the chip\n"); |
2169 | spin_lock_bh(&sc->sc_pcu_lock); | ||
2172 | ath_reset(sc, true); | 2170 | ath_reset(sc, true); |
2171 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
2173 | } | 2172 | } |
2174 | 2173 | ||
2175 | ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, | 2174 | ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, |