ath9k: fix crashes when using shared IRQs
authorFelix Fietkau <nbd@openwrt.org>
Thu, 13 Nov 2014 17:38:22 +0000 (17:38 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Thu, 13 Nov 2014 17:38:22 +0000 (17:38 +0000)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 43239

package/kernel/mac80211/patches/309-ath9k-prevent-early-IRQs-from-accessing-hardware.patch [new file with mode: 0644]
package/kernel/mac80211/patches/310-ath9k-set-ATH_OP_INVALID-before-disabling-hardware.patch [new file with mode: 0644]
package/kernel/mac80211/patches/311-ath9k-do-not-access-hardware-on-IRQs-during-reset.patch [new file with mode: 0644]
package/kernel/mac80211/patches/410-ath9k_allow_adhoc_and_ap.patch
package/kernel/mac80211/patches/501-ath9k_ahb_init.patch
package/kernel/mac80211/patches/530-ath9k_extra_leds.patch
package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
package/kernel/mac80211/patches/543-ath9k_entropy_from_adc.patch

diff --git a/package/kernel/mac80211/patches/309-ath9k-prevent-early-IRQs-from-accessing-hardware.patch b/package/kernel/mac80211/patches/309-ath9k-prevent-early-IRQs-from-accessing-hardware.patch
new file mode 100644 (file)
index 0000000..6af94c8
--- /dev/null
@@ -0,0 +1,38 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Thu, 13 Nov 2014 18:27:47 +0100
+Subject: [PATCH] ath9k: prevent early IRQs from accessing hardware
+
+IRQs are suppressed if ah == NULL and ATH_OP_INVALID being set in
+common->op_flags. Close a short time window between those two.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -513,10 +513,14 @@ static int ath9k_init_softc(u16 devid, s
+       ah->reg_ops.read = ath9k_ioread32;
+       ah->reg_ops.write = ath9k_iowrite32;
+       ah->reg_ops.rmw = ath9k_reg_rmw;
+-      sc->sc_ah = ah;
+       pCap = &ah->caps;
+       common = ath9k_hw_common(ah);
++
++      /* Will be cleared in ath9k_start() */
++      set_bit(ATH_OP_INVALID, &common->op_flags);
++
++      sc->sc_ah = ah;
+       sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
+       sc->tx99_power = MAX_RATE_POWER + 1;
+       init_waitqueue_head(&sc->tx_wait);
+@@ -876,9 +880,6 @@ int ath9k_init_device(u16 devid, struct 
+       common = ath9k_hw_common(ah);
+       ath9k_set_hw_capab(sc, hw);
+-      /* Will be cleared in ath9k_start() */
+-      set_bit(ATH_OP_INVALID, &common->op_flags);
+-
+       /* Initialize regulatory */
+       error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
+                             ath9k_reg_notifier);
diff --git a/package/kernel/mac80211/patches/310-ath9k-set-ATH_OP_INVALID-before-disabling-hardware.patch b/package/kernel/mac80211/patches/310-ath9k-set-ATH_OP_INVALID-before-disabling-hardware.patch
new file mode 100644 (file)
index 0000000..c382e5d
--- /dev/null
@@ -0,0 +1,29 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Thu, 13 Nov 2014 18:29:00 +0100
+Subject: [PATCH] ath9k: set ATH_OP_INVALID before disabling hardware
+
+Closes another small IRQ handler race
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -885,6 +885,9 @@ static void ath9k_stop(struct ieee80211_
+                                                   &sc->cur_chan->chandef);
+       ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
++
++      set_bit(ATH_OP_INVALID, &common->op_flags);
++
+       ath9k_hw_phy_disable(ah);
+       ath9k_hw_configpcipowersave(ah, true);
+@@ -893,7 +896,6 @@ static void ath9k_stop(struct ieee80211_
+       ath9k_ps_restore(sc);
+-      set_bit(ATH_OP_INVALID, &common->op_flags);
+       sc->ps_idle = prev_idle;
+       mutex_unlock(&sc->mutex);
diff --git a/package/kernel/mac80211/patches/311-ath9k-do-not-access-hardware-on-IRQs-during-reset.patch b/package/kernel/mac80211/patches/311-ath9k-do-not-access-hardware-on-IRQs-during-reset.patch
new file mode 100644 (file)
index 0000000..7996725
--- /dev/null
@@ -0,0 +1,51 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Thu, 13 Nov 2014 18:29:26 +0100
+Subject: [PATCH] ath9k: do not access hardware on IRQs during reset
+
+Instead of killing interrupts during reset when the first one happens,
+kill them before issuing the reset.
+This fixes an easy to reproduce crash with multiple cards sharing the
+same IRQ.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -512,16 +512,13 @@ irqreturn_t ath_isr(int irq, void *dev)
+       if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags))
+               return IRQ_NONE;
+-      /* shared irq, not for us */
++      if (test_bit(ATH_OP_HW_RESET, &common->op_flags))
++              return IRQ_NONE;
++      /* shared irq, not for us */
+       if (!ath9k_hw_intrpend(ah))
+               return IRQ_NONE;
+-      if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) {
+-              ath9k_hw_kill_interrupts(ah);
+-              return IRQ_HANDLED;
+-      }
+-
+       /*
+        * Figure out the reason(s) for the interrupt.  Note
+        * that the hal returns a pseudo-ISR that may include
+@@ -613,6 +610,7 @@ int ath_reset(struct ath_softc *sc, stru
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       int r;
++      ath9k_hw_kill_interrupts(sc->sc_ah);
+       set_bit(ATH_OP_HW_RESET, &common->op_flags);
+       ath9k_ps_wakeup(sc);
+@@ -633,6 +631,7 @@ void ath9k_queue_reset(struct ath_softc 
+ #ifdef CPTCFG_ATH9K_DEBUGFS
+       RESET_STAT_INC(sc, type);
+ #endif
++      ath9k_hw_kill_interrupts(sc->sc_ah);
+       set_bit(ATH_OP_HW_RESET, &common->op_flags);
+       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ }
index ebcb34c3811245581eb77b50786fb514fa0e7fe9..16bf144ecf0d045cba1a0b0771915f2cbbf3dcd6 100644 (file)
@@ -1,6 +1,6 @@
 --- a/drivers/net/wireless/ath/ath9k/init.c
 +++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -680,6 +680,7 @@ static const struct ieee80211_iface_limi
+@@ -684,6 +684,7 @@ static const struct ieee80211_iface_limi
                                 BIT(NL80211_IFTYPE_AP) },
        { .max = 1,     .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
                                 BIT(NL80211_IFTYPE_P2P_GO) },
index eaae68d40bbe803da1863f2d9ca45a17a843db42..965cb6b0880187ce0c24c0fe5e7bacc2b6b368a8 100644 (file)
@@ -1,6 +1,6 @@
 --- a/drivers/net/wireless/ath/ath9k/init.c
 +++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -989,23 +989,23 @@ static int __init ath9k_init(void)
+@@ -990,23 +990,23 @@ static int __init ath9k_init(void)
  {
        int error;
  
index d7dd4314fdc1e16af5e807527e32bc7b4020e03d..cb6c477f3e6acca388f8192cecaa3713f117e641 100644 (file)
  void ath_fill_led_pin(struct ath_softc *sc)
 --- a/drivers/net/wireless/ath/ath9k/init.c
 +++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -902,7 +902,7 @@ int ath9k_init_device(u16 devid, struct 
+@@ -903,7 +903,7 @@ int ath9k_init_device(u16 devid, struct 
  
  #ifdef CPTCFG_MAC80211_LEDS
        /* must be initialized before ieee80211_register_hw */
index 38c0c6352918c7079ad14fbd17003be724df0ea9..e1359704517bcd80ef57b3c540c295f4a1568cb8 100644 (file)
                REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
 --- a/drivers/net/wireless/ath/ath9k/main.c
 +++ b/drivers/net/wireless/ath/ath9k/main.c
-@@ -532,6 +532,11 @@ irqreturn_t ath_isr(int irq, void *dev)
+@@ -529,6 +529,11 @@ irqreturn_t ath_isr(int irq, void *dev)
        ath9k_debug_sync_cause(sc, sync_cause);
        status &= ah->imask;    /* discard unasked-for bits */
  
index 9d789b40a7d61b4ea6871201631383c80e48ba2a..89f89ece0ec6c9f4511a0d2f8c4d6727c8c18124 100644 (file)
@@ -55,7 +55,7 @@
        ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
 --- a/drivers/net/wireless/ath/ath9k/init.c
 +++ b/drivers/net/wireless/ath/ath9k/init.c
-@@ -668,7 +668,8 @@ static void ath9k_init_txpower_limits(st
+@@ -672,7 +672,8 @@ static void ath9k_init_txpower_limits(st
        if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ)
                ath9k_init_band_txpower(sc, IEEE80211_BAND_5GHZ);
  
@@ -65,7 +65,7 @@
  }
  
  static const struct ieee80211_iface_limit if_limits[] = {
-@@ -859,6 +860,18 @@ static void ath9k_set_hw_capab(struct at
+@@ -863,6 +864,18 @@ static void ath9k_set_hw_capab(struct at
        SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
  }
  
@@ -84,7 +84,7 @@
  int ath9k_init_device(u16 devid, struct ath_softc *sc,
                    const struct ath_bus_ops *bus_ops)
  {
-@@ -907,6 +920,8 @@ int ath9k_init_device(u16 devid, struct 
+@@ -908,6 +921,8 @@ int ath9k_init_device(u16 devid, struct 
                ARRAY_SIZE(ath9k_tpt_blink));
  #endif