--- /dev/null
+From 7fc00a3054b70b1794c2d64db703eb467ad0365c Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Wed, 9 Jan 2013 16:16:55 +0100
+Subject: ath9k: add a better fix for the rx tasklet vs rx flush race
+
+From: Felix Fietkau <nbd@openwrt.org>
+
+commit 7fc00a3054b70b1794c2d64db703eb467ad0365c upstream.
+
+Ensure that the rx tasklet is no longer running when entering the reset path.
+Also remove the distinction between flush and no-flush frame processing.
+If a frame has been received and ACKed by the hardware, the stack needs to see
+it, so that the BA receive window does not go out of sync.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/ath/ath9k/ath9k.h | 1 -
+ drivers/net/wireless/ath/ath9k/debug.c | 1 -
+ drivers/net/wireless/ath/ath9k/debug.h | 2 --
+ drivers/net/wireless/ath/ath9k/main.c | 4 ++++
+ drivers/net/wireless/ath/ath9k/recv.c | 15 ---------------
+ 5 files changed, 4 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -630,7 +630,6 @@ void ath_ant_comb_update(struct ath_soft
+ enum sc_op_flags {
+ SC_OP_INVALID,
+ SC_OP_BEACONS,
+- SC_OP_RXFLUSH,
+ SC_OP_ANI_RUN,
+ SC_OP_PRIM_STA_VIF,
+ SC_OP_HW_RESET,
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -973,7 +973,6 @@ static ssize_t read_file_recv(struct fil
+ RXS_ERR("RX-LENGTH-ERR", rx_len_err);
+ RXS_ERR("RX-OOM-ERR", rx_oom_err);
+ RXS_ERR("RX-RATE-ERR", rx_rate_err);
+- RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush);
+ RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err);
+
+ PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN);
+--- a/drivers/net/wireless/ath/ath9k/debug.h
++++ b/drivers/net/wireless/ath/ath9k/debug.h
+@@ -200,7 +200,6 @@ struct ath_tx_stats {
+ * @rx_oom_err: No. of frames dropped due to OOM issues.
+ * @rx_rate_err: No. of frames dropped due to rate errors.
+ * @rx_too_many_frags_err: Frames dropped due to too-many-frags received.
+- * @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH.
+ * @rx_beacons: No. of beacons received.
+ * @rx_frags: No. of rx-fragements received.
+ */
+@@ -219,7 +218,6 @@ struct ath_rx_stats {
+ u32 rx_oom_err;
+ u32 rx_rate_err;
+ u32 rx_too_many_frags_err;
+- u32 rx_drop_rxflush;
+ u32 rx_beacons;
+ u32 rx_frags;
+ };
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -195,6 +195,8 @@ static bool ath_prepare_reset(struct ath
+ ath9k_debug_samp_bb_mac(sc);
+ ath9k_hw_disable_interrupts(ah);
+
++ tasklet_disable(&sc->intr_tq);
++
+ if (!ath_stoprecv(sc))
+ ret = false;
+
+@@ -209,6 +211,8 @@ static bool ath_prepare_reset(struct ath
+ ath_flushrecv(sc);
+ }
+
++ tasklet_enable(&sc->intr_tq);
++
+ return ret;
+ }
+
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -286,7 +286,6 @@ int ath_rx_init(struct ath_softc *sc, in
+
+ spin_lock_init(&sc->sc_pcu_lock);
+ spin_lock_init(&sc->rx.rxbuflock);
+- clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
+
+ common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
+ sc->sc_ah->caps.rx_status_len;
+@@ -501,11 +500,9 @@ bool ath_stoprecv(struct ath_softc *sc)
+
+ void ath_flushrecv(struct ath_softc *sc)
+ {
+- set_bit(SC_OP_RXFLUSH, &sc->sc_flags);
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
+ ath_rx_tasklet(sc, 1, true);
+ ath_rx_tasklet(sc, 1, false);
+- clear_bit(SC_OP_RXFLUSH, &sc->sc_flags);
+ }
+
+ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
+@@ -1067,9 +1064,6 @@ int ath_rx_tasklet(struct ath_softc *sc,
+
+ do {
+ bool decrypt_error = false;
+- /* If handling rx interrupt and flush is in progress => exit */
+- if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
+- break;
+
+ memset(&rs, 0, sizeof(rs));
+ if (edma)
+@@ -1109,15 +1103,6 @@ int ath_rx_tasklet(struct ath_softc *sc,
+ sc->rx.num_pkts++;
+ ath_debug_stat_rx(sc, &rs);
+
+- /*
+- * If we're asked to flush receive queue, directly
+- * chain it back at the queue without processing it.
+- */
+- if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags)) {
+- RX_STAT_INC(rx_drop_rxflush);
+- goto requeue_drop_frag;
+- }
+-
+ memset(rxs, 0, sizeof(struct ieee80211_rx_status));
+
+ rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
--- /dev/null
+From 4668cce527acb3bd048c5e6c99b157a14b214671 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Mon, 14 Jan 2013 16:56:46 +0100
+Subject: ath9k: disable the tasklet before taking the PCU lock
+
+From: Felix Fietkau <nbd@openwrt.org>
+
+commit 4668cce527acb3bd048c5e6c99b157a14b214671 upstream.
+
+Fixes a reported CPU soft lockup where the tasklet tries to acquire the
+lock and blocks while ath_prepare_reset (holding the lock) waits for it
+to complete.
+
+Reported-by: Robert Shade <robert.shade@gmail.com>
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/ath/ath9k/main.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -195,16 +195,12 @@ static bool ath_prepare_reset(struct ath
+ ath9k_debug_samp_bb_mac(sc);
+ ath9k_hw_disable_interrupts(ah);
+
+- tasklet_disable(&sc->intr_tq);
+-
+ if (!ath_stoprecv(sc))
+ ret = false;
+
+ if (!ath_drain_all_txq(sc, retry_tx))
+ ret = false;
+
+- tasklet_enable(&sc->intr_tq);
+-
+ return ret;
+ }
+
+@@ -261,6 +257,7 @@ static int ath_reset_internal(struct ath
+
+ __ath_cancel_work(sc);
+
++ tasklet_disable(&sc->intr_tq);
+ spin_lock_bh(&sc->sc_pcu_lock);
+
+ if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+@@ -291,6 +288,8 @@ static int ath_reset_internal(struct ath
+
+ out:
+ spin_unlock_bh(&sc->sc_pcu_lock);
++ tasklet_enable(&sc->intr_tq);
++
+ return r;
+ }
+
--- /dev/null
+From 4b883f021b9ccf2df3d14425e6e610281fb6a35e Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Wed, 9 Jan 2013 16:16:56 +0100
+Subject: ath9k: fix rx flush handling
+
+From: Felix Fietkau <nbd@openwrt.org>
+
+commit 4b883f021b9ccf2df3d14425e6e610281fb6a35e upstream.
+
+Right now the rx flush is not doing anything useful on AR9003+, as it only
+works if the buffers in the rx FIFO have not been purged yet, as is done
+by ath_stoprecv.
+
+To fix this, always call ath_flushrecv from within ath_stoprecv before
+the FIFO is emptied, but still after the hw receive path has been stopped.
+
+This ensures that frames received (and ACKed by the hardware) shortly before
+a reset will be seen by the software, which should improve A-MPDU session
+stability.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/ath/ath9k/ath9k.h | 1 -
+ drivers/net/wireless/ath/ath9k/main.c | 16 +++-------------
+ drivers/net/wireless/ath/ath9k/recv.c | 16 +++++++++-------
+ 3 files changed, 12 insertions(+), 21 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -326,7 +326,6 @@ struct ath_rx {
+
+ int ath_startrecv(struct ath_softc *sc);
+ bool ath_stoprecv(struct ath_softc *sc);
+-void ath_flushrecv(struct ath_softc *sc);
+ u32 ath_calcrxfilter(struct ath_softc *sc);
+ int ath_rx_init(struct ath_softc *sc, int nbufs);
+ void ath_rx_cleanup(struct ath_softc *sc);
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -181,7 +181,7 @@ static void ath_restart_work(struct ath_
+ ath_start_ani(sc);
+ }
+
+-static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
++static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx)
+ {
+ struct ath_hw *ah = sc->sc_ah;
+ bool ret = true;
+@@ -203,14 +203,6 @@ static bool ath_prepare_reset(struct ath
+ if (!ath_drain_all_txq(sc, retry_tx))
+ ret = false;
+
+- if (!flush) {
+- if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
+- ath_rx_tasklet(sc, 1, true);
+- ath_rx_tasklet(sc, 1, false);
+- } else {
+- ath_flushrecv(sc);
+- }
+-
+ tasklet_enable(&sc->intr_tq);
+
+ return ret;
+@@ -265,7 +257,6 @@ static int ath_reset_internal(struct ath
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_hw_cal_data *caldata = NULL;
+ bool fastcc = true;
+- bool flush = false;
+ int r;
+
+ __ath_cancel_work(sc);
+@@ -279,11 +270,10 @@ static int ath_reset_internal(struct ath
+
+ if (!hchan) {
+ fastcc = false;
+- flush = true;
+ hchan = ah->curchan;
+ }
+
+- if (!ath_prepare_reset(sc, retry_tx, flush))
++ if (!ath_prepare_reset(sc, retry_tx))
+ fastcc = false;
+
+ ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
+@@ -820,7 +810,7 @@ static void ath9k_stop(struct ieee80211_
+ ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
+ }
+
+- ath_prepare_reset(sc, false, true);
++ ath_prepare_reset(sc, false);
+
+ if (sc->rx.frag) {
+ dev_kfree_skb_any(sc->rx.frag);
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -472,6 +472,13 @@ start_recv:
+ return 0;
+ }
+
++static void ath_flushrecv(struct ath_softc *sc)
++{
++ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
++ ath_rx_tasklet(sc, 1, true);
++ ath_rx_tasklet(sc, 1, false);
++}
++
+ bool ath_stoprecv(struct ath_softc *sc)
+ {
+ struct ath_hw *ah = sc->sc_ah;
+@@ -482,6 +489,8 @@ bool ath_stoprecv(struct ath_softc *sc)
+ ath9k_hw_setrxfilter(ah, 0);
+ stopped = ath9k_hw_stopdmarecv(ah, &reset);
+
++ ath_flushrecv(sc);
++
+ if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
+ ath_edma_stop_recv(sc);
+ else
+@@ -498,13 +507,6 @@ bool ath_stoprecv(struct ath_softc *sc)
+ return stopped && !reset;
+ }
+
+-void ath_flushrecv(struct ath_softc *sc)
+-{
+- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
+- ath_rx_tasklet(sc, 1, true);
+- ath_rx_tasklet(sc, 1, false);
+-}
+-
+ static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
+ {
+ /* Check whether the Beacon frame has DTIM indicating buffered bc/mc */
--- /dev/null
+From 463e3ed3eacc8f47866e5d612bd8ee0bcee5e2f0 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Mon, 14 Jan 2013 10:50:15 +0100
+Subject: ath9k: remove sc->rx.rxbuflock to fix a deadlock
+
+From: Felix Fietkau <nbd@openwrt.org>
+
+commit 463e3ed3eacc8f47866e5d612bd8ee0bcee5e2f0 upstream.
+
+The commit "ath9k: fix rx flush handling" added a deadlock that happens
+because ath_rx_tasklet is called in a section that has already taken the
+rx buffer lock.
+
+It seems that the only purpose of the rxbuflock was a band-aid fix to the
+reset vs rx tasklet race, which has been properly fixed in the commit
+"ath9k: add a better fix for the rx tasklet vs rx flush race".
+
+Now that the fix is in, we can safely remove the lock to avoid such issues.
+
+Reported-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/ath/ath9k/ath9k.h | 1 -
+ drivers/net/wireless/ath/ath9k/recv.c | 13 -------------
+ 2 files changed, 14 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -315,7 +315,6 @@ struct ath_rx {
+ u32 *rxlink;
+ u32 num_pkts;
+ unsigned int rxfilter;
+- spinlock_t rxbuflock;
+ struct list_head rxbuf;
+ struct ath_descdma rxdma;
+ struct ath_buf *rx_bufptr;
+--- a/drivers/net/wireless/ath/ath9k/recv.c
++++ b/drivers/net/wireless/ath/ath9k/recv.c
+@@ -254,8 +254,6 @@ rx_init_fail:
+
+ static void ath_edma_start_recv(struct ath_softc *sc)
+ {
+- spin_lock_bh(&sc->rx.rxbuflock);
+-
+ ath9k_hw_rxena(sc->sc_ah);
+
+ ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP,
+@@ -267,8 +265,6 @@ static void ath_edma_start_recv(struct a
+ ath_opmode_init(sc);
+
+ ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+-
+- spin_unlock_bh(&sc->rx.rxbuflock);
+ }
+
+ static void ath_edma_stop_recv(struct ath_softc *sc)
+@@ -285,7 +281,6 @@ int ath_rx_init(struct ath_softc *sc, in
+ int error = 0;
+
+ spin_lock_init(&sc->sc_pcu_lock);
+- spin_lock_init(&sc->rx.rxbuflock);
+
+ common->rx_bufsize = IEEE80211_MAX_MPDU_LEN / 2 +
+ sc->sc_ah->caps.rx_status_len;
+@@ -446,7 +441,6 @@ int ath_startrecv(struct ath_softc *sc)
+ return 0;
+ }
+
+- spin_lock_bh(&sc->rx.rxbuflock);
+ if (list_empty(&sc->rx.rxbuf))
+ goto start_recv;
+
+@@ -467,8 +461,6 @@ start_recv:
+ ath_opmode_init(sc);
+ ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+
+- spin_unlock_bh(&sc->rx.rxbuflock);
+-
+ return 0;
+ }
+
+@@ -484,7 +476,6 @@ bool ath_stoprecv(struct ath_softc *sc)
+ struct ath_hw *ah = sc->sc_ah;
+ bool stopped, reset = false;
+
+- spin_lock_bh(&sc->rx.rxbuflock);
+ ath9k_hw_abortpcurecv(ah);
+ ath9k_hw_setrxfilter(ah, 0);
+ stopped = ath9k_hw_stopdmarecv(ah, &reset);
+@@ -495,7 +486,6 @@ bool ath_stoprecv(struct ath_softc *sc)
+ ath_edma_stop_recv(sc);
+ else
+ sc->rx.rxlink = NULL;
+- spin_unlock_bh(&sc->rx.rxbuflock);
+
+ if (!(ah->ah_flags & AH_UNPLUGGED) &&
+ unlikely(!stopped)) {
+@@ -1059,7 +1049,6 @@ int ath_rx_tasklet(struct ath_softc *sc,
+ dma_type = DMA_FROM_DEVICE;
+
+ qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP;
+- spin_lock_bh(&sc->rx.rxbuflock);
+
+ tsf = ath9k_hw_gettsf64(ah);
+ tsf_lower = tsf & 0xffffffff;
+@@ -1251,8 +1240,6 @@ requeue:
+ }
+ } while (1);
+
+- spin_unlock_bh(&sc->rx.rxbuflock);
+-
+ if (!(ah->imask & ATH9K_INT_RXEOL)) {
+ ah->imask |= (ATH9K_INT_RXEOL | ATH9K_INT_RXORN);
+ ath9k_hw_set_interrupts(ah);
--- /dev/null
+From 3adcf20afb585993ffee24de36d1975f6b26b120 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Wed, 9 Jan 2013 16:16:54 +0100
+Subject: ath9k: remove the WARN_ON that triggers if generating a beacon fails
+
+From: Felix Fietkau <nbd@openwrt.org>
+
+commit 3adcf20afb585993ffee24de36d1975f6b26b120 upstream.
+
+During teardown, mac80211 will not return a new beacon. This is normal and
+handled properly in the driver, so there's no need to spam the user with a kernel
+warning here.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/net/wireless/ath/ath9k/beacon.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/net/wireless/ath/ath9k/beacon.c
++++ b/drivers/net/wireless/ath/ath9k/beacon.c
+@@ -360,7 +360,6 @@ void ath9k_beacon_tasklet(unsigned long
+ return;
+
+ bf = ath9k_beacon_generate(sc->hw, vif);
+- WARN_ON(!bf);
+
+ if (sc->beacon.bmisscnt != 0) {
+ ath_dbg(common, BSTUCK, "resume beacon xmit after %u misses\n",
ath9k_htc-fix-memory-leak.patch
ath9k-do-not-link-receive-buffers-during-flush.patch
ath9k-fix-double-free-bug-on-beacon-generate-failure.patch
+ath9k-remove-the-warn_on-that-triggers-if-generating-a-beacon-fails.patch
+ath9k-add-a-better-fix-for-the-rx-tasklet-vs-rx-flush-race.patch
+ath9k-fix-rx-flush-handling.patch
+ath9k-remove-sc-rx.rxbuflock-to-fix-a-deadlock.patch
+ath9k-disable-the-tasklet-before-taking-the-pcu-lock.patch