diff options
author | Felix Fietkau <nbd@openwrt.org> | 2010-10-20 00:46:37 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2010-10-20 00:46:37 +0000 |
commit | 1798277ca394a205c2bdf4e0499cd7f7fba9473f (patch) | |
tree | fe0bdf46a00565e3724072c77d145f278b21fda0 | |
parent | 7784723cda5f018ac332ee0f5c999e726a774ace (diff) | |
download | upstream-1798277ca394a205c2bdf4e0499cd7f7fba9473f.tar.gz upstream-1798277ca394a205c2bdf4e0499cd7f7fba9473f.tar.bz2 upstream-1798277ca394a205c2bdf4e0499cd7f7fba9473f.zip |
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.patch | 237 |
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 0000000000..fef36ecccd --- /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) |