Commit 8bdd5b9c authored by Bob Copeland's avatar Bob Copeland Committed by John W. Linville

ath5k: fix suspend-related oops on rmmod

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: default avatarToralf Förster <toralf.foerster@gmx.de>
Signed-off-by: default avatarElias Oltmanns <eo@nebensachen.de>
Signed-off-by: default avatarBob Copeland <me@bobcopeland.com>
Cc: stable@kernel.org
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 75e3d8db
...@@ -340,9 +340,9 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) ...@@ -340,9 +340,9 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
} }
/* Interrupt handling */ /* 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_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 irqreturn_t ath5k_intr(int irq, void *dev_id);
static void ath5k_tasklet_reset(unsigned long data); static void ath5k_tasklet_reset(unsigned long data);
...@@ -646,7 +646,7 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -646,7 +646,7 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
ath5k_led_off(sc); ath5k_led_off(sc);
ath5k_stop_hw(sc); ath5k_stop_hw(sc, true);
free_irq(pdev->irq, sc); free_irq(pdev->irq, sc);
pci_save_state(pdev); pci_save_state(pdev);
...@@ -683,7 +683,7 @@ ath5k_pci_resume(struct pci_dev *pdev) ...@@ -683,7 +683,7 @@ ath5k_pci_resume(struct pci_dev *pdev)
goto err_no_irq; goto err_no_irq;
} }
err = ath5k_init(sc); err = ath5k_init(sc, true);
if (err) if (err)
goto err_irq; goto err_irq;
ath5k_led_enable(sc); ath5k_led_enable(sc);
...@@ -2200,12 +2200,17 @@ ath5k_beacon_config(struct ath5k_softc *sc) ...@@ -2200,12 +2200,17 @@ ath5k_beacon_config(struct ath5k_softc *sc)
\********************/ \********************/
static int static int
ath5k_init(struct ath5k_softc *sc) ath5k_init(struct ath5k_softc *sc, bool is_resume)
{ {
int ret; int ret;
mutex_lock(&sc->lock); 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); ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
/* /*
...@@ -2230,12 +2235,15 @@ ath5k_init(struct ath5k_softc *sc) ...@@ -2230,12 +2235,15 @@ ath5k_init(struct ath5k_softc *sc)
if (ret) if (ret)
goto done; goto done;
__set_bit(ATH_STAT_STARTED, sc->status);
/* Set ack to be sent at low bit-rates */ /* Set ack to be sent at low bit-rates */
ath5k_hw_set_ack_bitrate_high(sc->ah, false); ath5k_hw_set_ack_bitrate_high(sc->ah, false);
mod_timer(&sc->calib_tim, round_jiffies(jiffies + mod_timer(&sc->calib_tim, round_jiffies(jiffies +
msecs_to_jiffies(ath5k_calinterval * 1000))); msecs_to_jiffies(ath5k_calinterval * 1000)));
out_ok:
ret = 0; ret = 0;
done: done:
mmiowb(); mmiowb();
...@@ -2290,7 +2298,7 @@ ath5k_stop_locked(struct ath5k_softc *sc) ...@@ -2290,7 +2298,7 @@ ath5k_stop_locked(struct ath5k_softc *sc)
* stop is preempted). * stop is preempted).
*/ */
static int static int
ath5k_stop_hw(struct ath5k_softc *sc) ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend)
{ {
int ret; int ret;
...@@ -2321,6 +2329,9 @@ ath5k_stop_hw(struct ath5k_softc *sc) ...@@ -2321,6 +2329,9 @@ ath5k_stop_hw(struct ath5k_softc *sc)
} }
} }
ath5k_txbuf_free(sc, sc->bbuf); ath5k_txbuf_free(sc, sc->bbuf);
if (!is_suspend)
__clear_bit(ATH_STAT_STARTED, sc->status);
mmiowb(); mmiowb();
mutex_unlock(&sc->lock); mutex_unlock(&sc->lock);
...@@ -2718,12 +2729,12 @@ ath5k_reset_wake(struct ath5k_softc *sc) ...@@ -2718,12 +2729,12 @@ ath5k_reset_wake(struct ath5k_softc *sc)
static int ath5k_start(struct ieee80211_hw *hw) 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) 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, static int ath5k_add_interface(struct ieee80211_hw *hw,
......
...@@ -128,11 +128,12 @@ struct ath5k_softc { ...@@ -128,11 +128,12 @@ struct ath5k_softc {
size_t desc_len; /* size of TX/RX descriptors */ size_t desc_len; /* size of TX/RX descriptors */
u16 cachelsz; /* cache line size */ u16 cachelsz; /* cache line size */
DECLARE_BITMAP(status, 4); DECLARE_BITMAP(status, 5);
#define ATH_STAT_INVALID 0 /* disable hardware accesses */ #define ATH_STAT_INVALID 0 /* disable hardware accesses */
#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ #define ATH_STAT_MRRETRY 1 /* multi-rate retry support */
#define ATH_STAT_PROMISC 2 #define ATH_STAT_PROMISC 2
#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ #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 filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int curmode; /* current phy mode */ unsigned int curmode; /* current phy mode */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment