2014-11-13 17:34:11

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 3.18 1/3] 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 <[email protected]>
---
drivers/net/wireless/ath/ath9k/init.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index c5bcddd..3c6ded9 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -529,10 +529,14 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
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);
@@ -893,9 +897,6 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
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);
--
2.1.2



2014-11-15 22:46:55

by Felix Fietkau

[permalink] [raw]
Subject: Re: [PATCH 3.18 3/3] ath9k: do not access hardware on IRQs during reset

On 2014-11-13 18:34, Felix Fietkau wrote:
> 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: [email protected]
> Signed-off-by: Felix Fietkau <[email protected]>
Please disregard this series for now, it seems to be causing some issues
on a few SoC devices.

- Felix

2014-11-13 17:34:11

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 3.18 3/3] 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: [email protected]
Signed-off-by: Felix Fietkau <[email protected]>
---
drivers/net/wireless/ath/ath9k/main.c | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index ee67956..19cab65 100644
--- 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, struct ath9k_channel *hchan)
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 *sc, enum ath_reset_type type)
#ifdef CONFIG_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);
}
--
2.1.2


2014-11-13 17:34:11

by Felix Fietkau

[permalink] [raw]
Subject: [PATCH 3.18 2/3] ath9k: set ATH_OP_INVALID before disabling hardware

Closes another small IRQ handler race

Signed-off-by: Felix Fietkau <[email protected]>
---
drivers/net/wireless/ath/ath9k/main.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 644552c..ee67956 100644
--- 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_hw *hw)
&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_hw *hw)

ath9k_ps_restore(sc);

- set_bit(ATH_OP_INVALID, &common->op_flags);
sc->ps_idle = prev_idle;

mutex_unlock(&sc->mutex);
--
2.1.2