summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2010-10-20 00:46:37 +0000
committernbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2010-10-20 00:46:37 +0000
commit0457e48f9d223ff8a735d149eae210ed277d6119 (patch)
treefe0bdf46a00565e3724072c77d145f278b21fda0
parent24750ea64efc60431fef5f190cfb5f8bdbc6371b (diff)
ath9k: add a locking fix that might prevent random memory corruption during hardware resets
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@23542 3c298f89-4303-0410-b956-a3cf2f4a3e73
-rw-r--r--package/mac80211/patches/530-ath9k_locking_fix.patch237
1 files changed, 237 insertions, 0 deletions
diff --git a/package/mac80211/patches/530-ath9k_locking_fix.patch b/package/mac80211/patches/530-ath9k_locking_fix.patch
new file mode 100644
index 000000000..fef36eccc
--- /dev/null
+++ b/package/mac80211/patches/530-ath9k_locking_fix.patch
@@ -0,0 +1,237 @@
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -309,8 +309,8 @@ struct ath_rx {
+ u8 rxotherant;
+ u32 *rxlink;
+ unsigned int rxfilter;
+- spinlock_t rxflushlock;
+ spinlock_t rxbuflock;
++ spinlock_t pcu_lock;
+ struct list_head rxbuf;
+ struct ath_descdma rxdma;
+ struct ath_buf *rx_bufptr;
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -239,6 +239,9 @@ int ath_set_channel(struct ath_softc *sc
+ */
+ ath9k_hw_disable_interrupts(ah);
+ 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
+@@ -266,6 +269,7 @@ int ath_set_channel(struct ath_softc *sc
+ "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);
+@@ -274,9 +278,12 @@ int ath_set_channel(struct ath_softc *sc
+ 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);
+
+@@ -610,7 +617,7 @@ void ath9k_tasklet(unsigned long data)
+ rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
+
+ if (status & rxmask) {
+- spin_lock_bh(&sc->rx.rxflushlock);
++ spin_lock_bh(&sc->rx.pcu_lock);
+
+ /* Check for high priority Rx first */
+ if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
+@@ -618,7 +625,7 @@ void ath9k_tasklet(unsigned long data)
+ ath_rx_tasklet(sc, 0, true);
+
+ ath_rx_tasklet(sc, 0, false);
+- spin_unlock_bh(&sc->rx.rxflushlock);
++ spin_unlock_bh(&sc->rx.pcu_lock);
+ }
+
+ if (status & ATH9K_INT_TX) {
+@@ -876,6 +883,7 @@ void ath_radio_enable(struct ath_softc *
+ 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 *
+ 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
+ ath9k_hw_disable_interrupts(ah);
+
+ 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
+ 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
+
+ ath9k_hw_disable_interrupts(ah);
+ 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
+ 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_
+ * 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_
+ "(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_
+ 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_
+ * before setting the invalid flag. */
+ ath9k_hw_disable_interrupts(ah);
+
++ 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);
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -297,19 +297,17 @@ static void ath_edma_start_recv(struct a
+ ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP,
+ sc->rx.rx_edma[ATH9K_RX_QUEUE_LP].rx_fifo_hwsize);
+
+- spin_unlock_bh(&sc->rx.rxbuflock);
+-
+ ath_opmode_init(sc);
+
+ ath9k_hw_startpcureceive(sc->sc_ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
++
++ spin_unlock_bh(&sc->rx.rxbuflock);
+ }
+
+ static void ath_edma_stop_recv(struct ath_softc *sc)
+ {
+- spin_lock_bh(&sc->rx.rxbuflock);
+ ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
+ ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
+- spin_unlock_bh(&sc->rx.rxbuflock);
+ }
+
+ int ath_rx_init(struct ath_softc *sc, int nbufs)
+@@ -319,8 +317,8 @@ int ath_rx_init(struct ath_softc *sc, in
+ struct ath_buf *bf;
+ int error = 0;
+
+- spin_lock_init(&sc->rx.rxflushlock);
+ sc->sc_flags &= ~SC_OP_RXFLUSH;
++ spin_lock_init(&sc->rx.pcu_lock);
+ spin_lock_init(&sc->rx.rxbuflock);
+
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
+@@ -506,9 +504,9 @@ int ath_startrecv(struct ath_softc *sc)
+ ath9k_hw_rxena(ah);
+
+ start_recv:
+- spin_unlock_bh(&sc->rx.rxbuflock);
+ ath_opmode_init(sc);
+ ath9k_hw_startpcureceive(ah, (sc->sc_flags & SC_OP_OFFCHANNEL));
++ spin_unlock_bh(&sc->rx.rxbuflock);
+
+ return 0;
+ }
+@@ -518,6 +516,7 @@ bool ath_stoprecv(struct ath_softc *sc)
+ struct ath_hw *ah = sc->sc_ah;
+ bool stopped;
+
++ spin_lock_bh(&sc->rx.rxbuflock);
+ ath9k_hw_stoppcurecv(ah);
+ ath9k_hw_setrxfilter(ah, 0);
+ stopped = ath9k_hw_stopdmarecv(ah);
+@@ -526,19 +525,18 @@ bool ath_stoprecv(struct ath_softc *sc)
+ ath_edma_stop_recv(sc);
+ else
+ sc->rx.rxlink = NULL;
++ spin_unlock_bh(&sc->rx.rxbuflock);
+
+ return stopped;
+ }
+
+ void ath_flushrecv(struct ath_softc *sc)
+ {
+- spin_lock_bh(&sc->rx.rxflushlock);
+ 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.rxflushlock);
+ }
+
+ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)