diff options
Diffstat (limited to 'package/mac80211/patches/530-ath9k_locking_fix.patch')
| -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 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)  | 
