ath9k: lock reset and PCU start/stopping
authorLuis R. Rodriguez <lrodriguez@atheros.com>
Wed, 20 Oct 2010 23:07:06 +0000 (16:07 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 27 Oct 2010 19:42:58 +0000 (15:42 -0400)
Apart from locking the start and stop PCU we need
to ensure we also content starting and stopping the PCU
between hardware resets.

This is part of a series that will help resolve the bug:

https://bugzilla.kernel.org/show_bug.cgi?id=14624

For more details about this issue refer to:

http://marc.info/?l=linux-wireless&m=128629803703756&w=2

Cc: stable@kernel.org
Cc: Ben Greear <greearb@candelatech.com>
Cc: Kyungwan Nam <kyungwan.nam@atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Tested-by: Ben Greear <greearb@candelatech.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/recv.c

index 47df22b14cf8005c7301063fbac506d91908d9c0..b52f1cf8a603fe4e7f4a61af1ba40c15a9e0c352 100644 (file)
@@ -241,6 +241,9 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
         */
        ath9k_hw_set_interrupts(ah, 0);
        ath_drain_all_txq(sc, false);
+
+       spin_lock_bh(&sc->rx.pcu_lock);
+
        stopped = ath_stoprecv(sc);
 
        /* XXX: do not flush receive queue here. We don't want
@@ -268,6 +271,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
                          "reset status %d\n",
                          channel->center_freq, r);
                spin_unlock_bh(&sc->sc_resetlock);
+               spin_unlock_bh(&sc->rx.pcu_lock);
                goto ps_restore;
        }
        spin_unlock_bh(&sc->sc_resetlock);
@@ -276,9 +280,12 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to restart recv logic\n");
                r = -EIO;
+               spin_unlock_bh(&sc->rx.pcu_lock);
                goto ps_restore;
        }
 
+       spin_unlock_bh(&sc->rx.pcu_lock);
+
        ath_update_txpow(sc);
        ath9k_hw_set_interrupts(ah, ah->imask);
 
@@ -876,6 +883,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
        if (!ah->curchan)
                ah->curchan = ath_get_curchannel(sc, sc->hw);
 
+       spin_lock_bh(&sc->rx.pcu_lock);
        spin_lock_bh(&sc->sc_resetlock);
        r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
        if (r) {
@@ -890,8 +898,10 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
        if (ath_startrecv(sc) != 0) {
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to restart recv logic\n");
+               spin_unlock_bh(&sc->rx.pcu_lock);
                return;
        }
+       spin_unlock_bh(&sc->rx.pcu_lock);
 
        if (sc->sc_flags & SC_OP_BEACONS)
                ath_beacon_config(sc, NULL);    /* restart beacons */
@@ -930,6 +940,9 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
        ath9k_hw_set_interrupts(ah, 0);
 
        ath_drain_all_txq(sc, false);   /* clear pending tx frames */
+
+       spin_lock_bh(&sc->rx.pcu_lock);
+
        ath_stoprecv(sc);               /* turn off frame recv */
        ath_flushrecv(sc);              /* flush recv queue */
 
@@ -947,6 +960,9 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
        spin_unlock_bh(&sc->sc_resetlock);
 
        ath9k_hw_phy_disable(ah);
+
+       spin_unlock_bh(&sc->rx.pcu_lock);
+
        ath9k_hw_configpcipowersave(ah, 1, 1);
        ath9k_ps_restore(sc);
        ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
@@ -966,6 +982,9 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
 
        ath9k_hw_set_interrupts(ah, 0);
        ath_drain_all_txq(sc, retry_tx);
+
+       spin_lock_bh(&sc->rx.pcu_lock);
+
        ath_stoprecv(sc);
        ath_flushrecv(sc);
 
@@ -980,6 +999,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to start recv logic\n");
 
+       spin_unlock_bh(&sc->rx.pcu_lock);
+
        /*
         * We may be doing a reset in response to a request
         * that changes the channel so update any state that
@@ -1142,6 +1163,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
         * be followed by initialization of the appropriate bits
         * and then setup of the interrupt mask.
         */
+       spin_lock_bh(&sc->rx.pcu_lock);
        spin_lock_bh(&sc->sc_resetlock);
        r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
        if (r) {
@@ -1150,6 +1172,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
                          "(freq %u MHz)\n", r,
                          curchan->center_freq);
                spin_unlock_bh(&sc->sc_resetlock);
+               spin_unlock_bh(&sc->rx.pcu_lock);
                goto mutex_unlock;
        }
        spin_unlock_bh(&sc->sc_resetlock);
@@ -1171,8 +1194,10 @@ static int ath9k_start(struct ieee80211_hw *hw)
                ath_print(common, ATH_DBG_FATAL,
                          "Unable to start recv logic\n");
                r = -EIO;
+               spin_unlock_bh(&sc->rx.pcu_lock);
                goto mutex_unlock;
        }
+       spin_unlock_bh(&sc->rx.pcu_lock);
 
        /* Setup our intr mask. */
        ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
@@ -1371,12 +1396,14 @@ static void ath9k_stop(struct ieee80211_hw *hw)
         * before setting the invalid flag. */
        ath9k_hw_set_interrupts(ah, 0);
 
+       spin_lock_bh(&sc->rx.pcu_lock);
        if (!(sc->sc_flags & SC_OP_INVALID)) {
                ath_drain_all_txq(sc, false);
                ath_stoprecv(sc);
                ath9k_hw_phy_disable(ah);
        } else
                sc->rx.rxlink = NULL;
+       spin_unlock_bh(&sc->rx.pcu_lock);
 
        /* disable HAL and put h/w to sleep */
        ath9k_hw_disable(ah);
index 67fe1f8a6439ab0f89b30e5b48611d3b442e7678..fddb0129bb57c43efa1148401a3ebf1e80f38239 100644 (file)
@@ -533,13 +533,11 @@ bool ath_stoprecv(struct ath_softc *sc)
 
 void ath_flushrecv(struct ath_softc *sc)
 {
-       spin_lock_bh(&sc->rx.pcu_lock);
        sc->sc_flags |= SC_OP_RXFLUSH;
        if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                ath_rx_tasklet(sc, 1, true);
        ath_rx_tasklet(sc, 1, false);
        sc->sc_flags &= ~SC_OP_RXFLUSH;
-       spin_unlock_bh(&sc->rx.pcu_lock);
 }
 
 static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)