Based on a patch by Elias Oltmanns, we call ath5k_init in resume even if we didn't previously open the device. Besides starting up the device unnecessarily, this also causes an oops on rmmod because mac80211 will not invoke ath5k_stop and softirqs are left running after the module has been unloaded. Add a new state bit, ATH_STAT_STARTED, to indicate that we have been started up.
Reported-by: Toralf Förster <[EMAIL PROTECTED]> Signed-off-by: Elias Oltmanns <[EMAIL PROTECTED]> Signed-off-by: Bob Copeland <[EMAIL PROTECTED]> Cc: [EMAIL PROTECTED] --- Hi John, I know this hasn't been in wireless-testing yet, but it would be nice if this could make it for v2.6.28 since it fixes an oops on rmmod. The fix is a bit of a stop-gap until mac80211 suspend callbacks, which I hope to start on next week for 2.6.29. I've been running with it for the past few days with no problems, though it could still use review/ack by other ath5k devs. Here's the original thread: http://marc.info/?l=linux-wireless&m=122288739002737 drivers/net/wireless/ath5k/base.c | 27 +++++++++++++++++++-------- drivers/net/wireless/ath5k/base.h | 3 ++- 2 files changed, 21 insertions(+), 9 deletions(-) Index: foo/drivers/net/wireless/ath5k/base.c =================================================================== --- foo.orig/drivers/net/wireless/ath5k/base.c 2008-10-16 10:22:58.000000000 -0400 +++ foo/drivers/net/wireless/ath5k/base.c 2008-10-16 10:41:29.000000000 -0400 @@ -339,9 +339,9 @@ static inline u64 ath5k_extend_tsf(struc } /* Interrupt handling */ -static int ath5k_init(struct ath5k_softc *sc); +static int ath5k_init(struct ath5k_softc *sc, bool is_resume); static int ath5k_stop_locked(struct ath5k_softc *sc); -static int ath5k_stop_hw(struct ath5k_softc *sc); +static int ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend); static irqreturn_t ath5k_intr(int irq, void *dev_id); static void ath5k_tasklet_reset(unsigned long data); @@ -645,7 +645,7 @@ ath5k_pci_suspend(struct pci_dev *pdev, ath5k_led_off(sc); - ath5k_stop_hw(sc); + ath5k_stop_hw(sc, true); free_irq(pdev->irq, sc); pci_save_state(pdev); @@ -682,7 +682,7 @@ ath5k_pci_resume(struct pci_dev *pdev) goto err_no_irq; } - err = ath5k_init(sc); + err = ath5k_init(sc, true); if (err) goto err_irq; ath5k_led_enable(sc); @@ -2199,12 +2199,17 @@ ath5k_beacon_config(struct ath5k_softc * \********************/ static int -ath5k_init(struct ath5k_softc *sc) +ath5k_init(struct ath5k_softc *sc, bool is_resume) { int ret; mutex_lock(&sc->lock); + if (is_resume && !test_bit(ATH_STAT_STARTED, sc->status)) + goto out_ok; + + __clear_bit(ATH_STAT_STARTED, sc->status); + ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); /* @@ -2229,12 +2234,15 @@ ath5k_init(struct ath5k_softc *sc) if (ret) goto done; + __set_bit(ATH_STAT_STARTED, sc->status); + /* Set ack to be sent at low bit-rates */ ath5k_hw_set_ack_bitrate_high(sc->ah, false); mod_timer(&sc->calib_tim, round_jiffies(jiffies + msecs_to_jiffies(ath5k_calinterval * 1000))); +out_ok: ret = 0; done: mmiowb(); @@ -2289,7 +2297,7 @@ ath5k_stop_locked(struct ath5k_softc *sc * stop is preempted). */ static int -ath5k_stop_hw(struct ath5k_softc *sc) +ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend) { int ret; @@ -2320,6 +2328,9 @@ ath5k_stop_hw(struct ath5k_softc *sc) } } ath5k_txbuf_free(sc, sc->bbuf); + if (!is_suspend) + __clear_bit(ATH_STAT_STARTED, sc->status); + mmiowb(); mutex_unlock(&sc->lock); @@ -2717,12 +2728,12 @@ ath5k_reset_wake(struct ath5k_softc *sc) static int ath5k_start(struct ieee80211_hw *hw) { - return ath5k_init(hw->priv); + return ath5k_init(hw->priv, false); } static void ath5k_stop(struct ieee80211_hw *hw) { - ath5k_stop_hw(hw->priv); + ath5k_stop_hw(hw->priv, false); } static int ath5k_add_interface(struct ieee80211_hw *hw, Index: foo/drivers/net/wireless/ath5k/base.h =================================================================== --- foo.orig/drivers/net/wireless/ath5k/base.h 2008-10-16 10:22:58.000000000 -0400 +++ foo/drivers/net/wireless/ath5k/base.h 2008-10-16 10:41:29.000000000 -0400 @@ -128,11 +128,12 @@ struct ath5k_softc { size_t desc_len; /* size of TX/RX descriptors */ u16 cachelsz; /* cache line size */ - DECLARE_BITMAP(status, 4); + DECLARE_BITMAP(status, 5); #define ATH_STAT_INVALID 0 /* disable hardware accesses */ #define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ #define ATH_STAT_PROMISC 2 #define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ +#define ATH_STAT_STARTED 4 /* opened & irqs enabled */ unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ unsigned int curmode; /* current phy mode */ _______________________________________________ ath5k-devel mailing list ath5k-devel@lists.ath5k.org https://lists.ath5k.org/mailman/listinfo/ath5k-devel