aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/316-ath9k-fix-race-condition-in-enabling-disabling-IRQs.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/316-ath9k-fix-race-condition-in-enabling-disabling-IRQs.patch')
-rw-r--r--package/kernel/mac80211/patches/316-ath9k-fix-race-condition-in-enabling-disabling-IRQs.patch197
1 files changed, 0 insertions, 197 deletions
diff --git a/package/kernel/mac80211/patches/316-ath9k-fix-race-condition-in-enabling-disabling-IRQs.patch b/package/kernel/mac80211/patches/316-ath9k-fix-race-condition-in-enabling-disabling-IRQs.patch
deleted file mode 100644
index 7a41206132..0000000000
--- a/package/kernel/mac80211/patches/316-ath9k-fix-race-condition-in-enabling-disabling-IRQs.patch
+++ /dev/null
@@ -1,197 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 25 Jan 2017 15:10:37 +0100
-Subject: [PATCH] ath9k: fix race condition in enabling/disabling IRQs
-
-The code currently relies on refcounting to disable IRQs from within the
-IRQ handler and re-enabling them again after the tasklet has run.
-
-However, due to race conditions sometimes the IRQ handler might be
-called twice, or the tasklet may not run at all (if interrupted in the
-middle of a reset).
-
-This can cause nasty imbalances in the irq-disable refcount which will
-get the driver permanently stuck until the entire radio has been stopped
-and started again (ath_reset will not recover from this).
-
-Instead of using this fragile logic, change the code to ensure that
-running the irq handler during tasklet processing is safe, and leave the
-refcount untouched.
-
-Cc: stable@vger.kernel.org
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/drivers/net/wireless/ath/ath9k/ath9k.h
-+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
-@@ -998,6 +998,7 @@ struct ath_softc {
- struct survey_info *cur_survey;
- struct survey_info survey[ATH9K_NUM_CHANNELS];
-
-+ spinlock_t intr_lock;
- struct tasklet_struct intr_tq;
- struct tasklet_struct bcon_tasklet;
- struct ath_hw *sc_ah;
---- a/drivers/net/wireless/ath/ath9k/init.c
-+++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -669,6 +669,7 @@ static int ath9k_init_softc(u16 devid, s
- common->bt_ant_diversity = 1;
-
- spin_lock_init(&common->cc_lock);
-+ spin_lock_init(&sc->intr_lock);
- spin_lock_init(&sc->sc_serial_rw);
- spin_lock_init(&sc->sc_pm_lock);
- spin_lock_init(&sc->chan_lock);
---- a/drivers/net/wireless/ath/ath9k/mac.c
-+++ b/drivers/net/wireless/ath/ath9k/mac.c
-@@ -810,21 +810,12 @@ void ath9k_hw_disable_interrupts(struct
- }
- EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
-
--void ath9k_hw_enable_interrupts(struct ath_hw *ah)
-+static void __ath9k_hw_enable_interrupts(struct ath_hw *ah)
- {
- struct ath_common *common = ath9k_hw_common(ah);
- u32 sync_default = AR_INTR_SYNC_DEFAULT;
- u32 async_mask;
-
-- if (!(ah->imask & ATH9K_INT_GLOBAL))
-- return;
--
-- if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
-- ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
-- atomic_read(&ah->intr_ref_cnt));
-- return;
-- }
--
- if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) ||
- AR_SREV_9561(ah))
- sync_default &= ~AR_INTR_SYNC_HOST1_FATAL;
-@@ -846,6 +837,39 @@ void ath9k_hw_enable_interrupts(struct a
- ath_dbg(common, INTERRUPT, "AR_IMR 0x%x IER 0x%x\n",
- REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER));
- }
-+
-+void ath9k_hw_resume_interrupts(struct ath_hw *ah)
-+{
-+ struct ath_common *common = ath9k_hw_common(ah);
-+
-+ if (!(ah->imask & ATH9K_INT_GLOBAL))
-+ return;
-+
-+ if (atomic_read(&ah->intr_ref_cnt) != 0) {
-+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
-+ atomic_read(&ah->intr_ref_cnt));
-+ return;
-+ }
-+
-+ __ath9k_hw_enable_interrupts(ah);
-+}
-+EXPORT_SYMBOL(ath9k_hw_resume_interrupts);
-+
-+void ath9k_hw_enable_interrupts(struct ath_hw *ah)
-+{
-+ struct ath_common *common = ath9k_hw_common(ah);
-+
-+ if (!(ah->imask & ATH9K_INT_GLOBAL))
-+ return;
-+
-+ if (!atomic_inc_and_test(&ah->intr_ref_cnt)) {
-+ ath_dbg(common, INTERRUPT, "Do not enable IER ref count %d\n",
-+ atomic_read(&ah->intr_ref_cnt));
-+ return;
-+ }
-+
-+ __ath9k_hw_enable_interrupts(ah);
-+}
- EXPORT_SYMBOL(ath9k_hw_enable_interrupts);
-
- void ath9k_hw_set_interrupts(struct ath_hw *ah)
---- a/drivers/net/wireless/ath/ath9k/mac.h
-+++ b/drivers/net/wireless/ath/ath9k/mac.h
-@@ -744,6 +744,7 @@ void ath9k_hw_set_interrupts(struct ath_
- void ath9k_hw_enable_interrupts(struct ath_hw *ah);
- void ath9k_hw_disable_interrupts(struct ath_hw *ah);
- void ath9k_hw_kill_interrupts(struct ath_hw *ah);
-+void ath9k_hw_resume_interrupts(struct ath_hw *ah);
-
- void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
-
---- a/drivers/net/wireless/ath/ath9k/main.c
-+++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -374,21 +374,20 @@ void ath9k_tasklet(unsigned long data)
- struct ath_common *common = ath9k_hw_common(ah);
- enum ath_reset_type type;
- unsigned long flags;
-- u32 status = sc->intrstatus;
-+ u32 status;
- u32 rxmask;
-
-+ spin_lock_irqsave(&sc->intr_lock, flags);
-+ status = sc->intrstatus;
-+ sc->intrstatus = 0;
-+ spin_unlock_irqrestore(&sc->intr_lock, flags);
-+
- ath9k_ps_wakeup(sc);
- spin_lock(&sc->sc_pcu_lock);
-
- if (status & ATH9K_INT_FATAL) {
- type = RESET_TYPE_FATAL_INT;
- ath9k_queue_reset(sc, type);
--
-- /*
-- * Increment the ref. counter here so that
-- * interrupts are enabled in the reset routine.
-- */
-- atomic_inc(&ah->intr_ref_cnt);
- ath_dbg(common, RESET, "FATAL: Skipping interrupts\n");
- goto out;
- }
-@@ -404,11 +403,6 @@ void ath9k_tasklet(unsigned long data)
- type = RESET_TYPE_BB_WATCHDOG;
- ath9k_queue_reset(sc, type);
-
-- /*
-- * Increment the ref. counter here so that
-- * interrupts are enabled in the reset routine.
-- */
-- atomic_inc(&ah->intr_ref_cnt);
- ath_dbg(common, RESET,
- "BB_WATCHDOG: Skipping interrupts\n");
- goto out;
-@@ -421,7 +415,6 @@ void ath9k_tasklet(unsigned long data)
- if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) {
- type = RESET_TYPE_TX_GTT;
- ath9k_queue_reset(sc, type);
-- atomic_inc(&ah->intr_ref_cnt);
- ath_dbg(common, RESET,
- "GTT: Skipping interrupts\n");
- goto out;
-@@ -478,7 +471,7 @@ void ath9k_tasklet(unsigned long data)
- ath9k_btcoex_handle_interrupt(sc, status);
-
- /* re-enable hardware interrupt */
-- ath9k_hw_enable_interrupts(ah);
-+ ath9k_hw_resume_interrupts(ah);
- out:
- spin_unlock(&sc->sc_pcu_lock);
- ath9k_ps_restore(sc);
-@@ -542,7 +535,9 @@ irqreturn_t ath_isr(int irq, void *dev)
- return IRQ_NONE;
-
- /* Cache the status */
-- sc->intrstatus = status;
-+ spin_lock(&sc->intr_lock);
-+ sc->intrstatus |= status;
-+ spin_unlock(&sc->intr_lock);
-
- if (status & SCHED_INTR)
- sched = true;
-@@ -588,7 +583,7 @@ chip_reset:
-
- if (sched) {
- /* turn off every interrupt */
-- ath9k_hw_disable_interrupts(ah);
-+ ath9k_hw_kill_interrupts(ah);
- tasklet_schedule(&sc->intr_tq);
- }
-