ath9k: add a locking fix that might prevent random memory corruption during hardware...
authorFelix Fietkau <nbd@openwrt.org>
Wed, 20 Oct 2010 00:46:37 +0000 (00:46 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Wed, 20 Oct 2010 00:46:37 +0000 (00:46 +0000)
SVN-Revision: 23542

package/mac80211/patches/530-ath9k_locking_fix.patch [new file with mode: 0644]

diff --git a/package/mac80211/patches/530-ath9k_locking_fix.patch b/package/mac80211/patches/530-ath9k_locking_fix.patch
new file mode 100644 (file)
index 0000000..fef36ec
--- /dev/null
@@ -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)