From 5a05cb40ce04154772ab333bc1587beaad68717d Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
Date: Thu, 13 Nov 2014 17:38:22 +0000
Subject: [PATCH] ath9k: fix crashes when using shared IRQs

Signed-off-by: Felix Fietkau <nbd@openwrt.org>

SVN-Revision: 43239
---
 ...t-early-IRQs-from-accessing-hardware.patch | 38 ++++++++++++++
 ...OP_INVALID-before-disabling-hardware.patch | 29 +++++++++++
 ...access-hardware-on-IRQs-during-reset.patch | 51 +++++++++++++++++++
 .../410-ath9k_allow_adhoc_and_ap.patch        |  2 +-
 .../mac80211/patches/501-ath9k_ahb_init.patch |  2 +-
 .../patches/530-ath9k_extra_leds.patch        |  2 +-
 .../patches/542-ath9k_debugfs_diag.patch      |  2 +-
 .../patches/543-ath9k_entropy_from_adc.patch  |  6 +--
 8 files changed, 125 insertions(+), 7 deletions(-)
 create mode 100644 package/kernel/mac80211/patches/309-ath9k-prevent-early-IRQs-from-accessing-hardware.patch
 create mode 100644 package/kernel/mac80211/patches/310-ath9k-set-ATH_OP_INVALID-before-disabling-hardware.patch
 create mode 100644 package/kernel/mac80211/patches/311-ath9k-do-not-access-hardware-on-IRQs-during-reset.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
index 0000000000..6af94c8ea6
--- /dev/null
+++ b/package/kernel/mac80211/patches/309-ath9k-prevent-early-IRQs-from-accessing-hardware.patch
@@ -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
index 0000000000..c382e5d103
--- /dev/null
+++ b/package/kernel/mac80211/patches/310-ath9k-set-ATH_OP_INVALID-before-disabling-hardware.patch
@@ -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
index 0000000000..7996725025
--- /dev/null
+++ b/package/kernel/mac80211/patches/311-ath9k-do-not-access-hardware-on-IRQs-during-reset.patch
@@ -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);
+ }
diff --git a/package/kernel/mac80211/patches/410-ath9k_allow_adhoc_and_ap.patch b/package/kernel/mac80211/patches/410-ath9k_allow_adhoc_and_ap.patch
index ebcb34c381..16bf144ecf 100644
--- a/package/kernel/mac80211/patches/410-ath9k_allow_adhoc_and_ap.patch
+++ b/package/kernel/mac80211/patches/410-ath9k_allow_adhoc_and_ap.patch
@@ -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) },
diff --git a/package/kernel/mac80211/patches/501-ath9k_ahb_init.patch b/package/kernel/mac80211/patches/501-ath9k_ahb_init.patch
index eaae68d40b..965cb6b088 100644
--- a/package/kernel/mac80211/patches/501-ath9k_ahb_init.patch
+++ b/package/kernel/mac80211/patches/501-ath9k_ahb_init.patch
@@ -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;
  
diff --git a/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch b/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch
index d7dd4314fd..cb6c477f3e 100644
--- a/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch
+++ b/package/kernel/mac80211/patches/530-ath9k_extra_leds.patch
@@ -162,7 +162,7 @@
  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 */
diff --git a/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch b/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
index 38c0c63529..e135970451 100644
--- a/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
+++ b/package/kernel/mac80211/patches/542-ath9k_debugfs_diag.patch
@@ -125,7 +125,7 @@
  		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 */
  
diff --git a/package/kernel/mac80211/patches/543-ath9k_entropy_from_adc.patch b/package/kernel/mac80211/patches/543-ath9k_entropy_from_adc.patch
index 9d789b40a7..89f89ece0e 100644
--- a/package/kernel/mac80211/patches/543-ath9k_entropy_from_adc.patch
+++ b/package/kernel/mac80211/patches/543-ath9k_entropy_from_adc.patch
@@ -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
  
-- 
GitLab