diff options
author | Bob Copeland <me@bobcopeland.com> | 2008-10-16 11:02:06 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-10-22 18:09:32 -0400 |
commit | 8bdd5b9c6bd53add260756b6673a0545fbdbba21 (patch) | |
tree | a35a59e657422a8577103fff8a34f8b2d8f79440 | |
parent | 75e3d8db531b462b875c1adb13eeb6b0be7374c0 (diff) |
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: Toralf Förster <toralf.foerster@gmx.de>
Signed-off-by: Elias Oltmanns <eo@nebensachen.de>
Signed-off-by: Bob Copeland <me@bobcopeland.com>
Cc: stable@kernel.org
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath5k/base.c | 27 | ||||
-rw-r--r-- | drivers/net/wireless/ath5k/base.h | 3 |
2 files changed, 21 insertions, 9 deletions
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c index 9b95c4049b31..0f1d6bdd51a2 100644 --- a/drivers/net/wireless/ath5k/base.c +++ b/drivers/net/wireless/ath5k/base.c | |||
@@ -340,9 +340,9 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp) | |||
340 | } | 340 | } |
341 | 341 | ||
342 | /* Interrupt handling */ | 342 | /* Interrupt handling */ |
343 | static int ath5k_init(struct ath5k_softc *sc); | 343 | static int ath5k_init(struct ath5k_softc *sc, bool is_resume); |
344 | static int ath5k_stop_locked(struct ath5k_softc *sc); | 344 | static int ath5k_stop_locked(struct ath5k_softc *sc); |
345 | static int ath5k_stop_hw(struct ath5k_softc *sc); | 345 | static int ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend); |
346 | static irqreturn_t ath5k_intr(int irq, void *dev_id); | 346 | static irqreturn_t ath5k_intr(int irq, void *dev_id); |
347 | static void ath5k_tasklet_reset(unsigned long data); | 347 | static void ath5k_tasklet_reset(unsigned long data); |
348 | 348 | ||
@@ -646,7 +646,7 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
646 | 646 | ||
647 | ath5k_led_off(sc); | 647 | ath5k_led_off(sc); |
648 | 648 | ||
649 | ath5k_stop_hw(sc); | 649 | ath5k_stop_hw(sc, true); |
650 | 650 | ||
651 | free_irq(pdev->irq, sc); | 651 | free_irq(pdev->irq, sc); |
652 | pci_save_state(pdev); | 652 | pci_save_state(pdev); |
@@ -683,7 +683,7 @@ ath5k_pci_resume(struct pci_dev *pdev) | |||
683 | goto err_no_irq; | 683 | goto err_no_irq; |
684 | } | 684 | } |
685 | 685 | ||
686 | err = ath5k_init(sc); | 686 | err = ath5k_init(sc, true); |
687 | if (err) | 687 | if (err) |
688 | goto err_irq; | 688 | goto err_irq; |
689 | ath5k_led_enable(sc); | 689 | ath5k_led_enable(sc); |
@@ -2200,12 +2200,17 @@ ath5k_beacon_config(struct ath5k_softc *sc) | |||
2200 | \********************/ | 2200 | \********************/ |
2201 | 2201 | ||
2202 | static int | 2202 | static int |
2203 | ath5k_init(struct ath5k_softc *sc) | 2203 | ath5k_init(struct ath5k_softc *sc, bool is_resume) |
2204 | { | 2204 | { |
2205 | int ret; | 2205 | int ret; |
2206 | 2206 | ||
2207 | mutex_lock(&sc->lock); | 2207 | mutex_lock(&sc->lock); |
2208 | 2208 | ||
2209 | if (is_resume && !test_bit(ATH_STAT_STARTED, sc->status)) | ||
2210 | goto out_ok; | ||
2211 | |||
2212 | __clear_bit(ATH_STAT_STARTED, sc->status); | ||
2213 | |||
2209 | ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); | 2214 | ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode); |
2210 | 2215 | ||
2211 | /* | 2216 | /* |
@@ -2230,12 +2235,15 @@ ath5k_init(struct ath5k_softc *sc) | |||
2230 | if (ret) | 2235 | if (ret) |
2231 | goto done; | 2236 | goto done; |
2232 | 2237 | ||
2238 | __set_bit(ATH_STAT_STARTED, sc->status); | ||
2239 | |||
2233 | /* Set ack to be sent at low bit-rates */ | 2240 | /* Set ack to be sent at low bit-rates */ |
2234 | ath5k_hw_set_ack_bitrate_high(sc->ah, false); | 2241 | ath5k_hw_set_ack_bitrate_high(sc->ah, false); |
2235 | 2242 | ||
2236 | mod_timer(&sc->calib_tim, round_jiffies(jiffies + | 2243 | mod_timer(&sc->calib_tim, round_jiffies(jiffies + |
2237 | msecs_to_jiffies(ath5k_calinterval * 1000))); | 2244 | msecs_to_jiffies(ath5k_calinterval * 1000))); |
2238 | 2245 | ||
2246 | out_ok: | ||
2239 | ret = 0; | 2247 | ret = 0; |
2240 | done: | 2248 | done: |
2241 | mmiowb(); | 2249 | mmiowb(); |
@@ -2290,7 +2298,7 @@ ath5k_stop_locked(struct ath5k_softc *sc) | |||
2290 | * stop is preempted). | 2298 | * stop is preempted). |
2291 | */ | 2299 | */ |
2292 | static int | 2300 | static int |
2293 | ath5k_stop_hw(struct ath5k_softc *sc) | 2301 | ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend) |
2294 | { | 2302 | { |
2295 | int ret; | 2303 | int ret; |
2296 | 2304 | ||
@@ -2321,6 +2329,9 @@ ath5k_stop_hw(struct ath5k_softc *sc) | |||
2321 | } | 2329 | } |
2322 | } | 2330 | } |
2323 | ath5k_txbuf_free(sc, sc->bbuf); | 2331 | ath5k_txbuf_free(sc, sc->bbuf); |
2332 | if (!is_suspend) | ||
2333 | __clear_bit(ATH_STAT_STARTED, sc->status); | ||
2334 | |||
2324 | mmiowb(); | 2335 | mmiowb(); |
2325 | mutex_unlock(&sc->lock); | 2336 | mutex_unlock(&sc->lock); |
2326 | 2337 | ||
@@ -2718,12 +2729,12 @@ ath5k_reset_wake(struct ath5k_softc *sc) | |||
2718 | 2729 | ||
2719 | static int ath5k_start(struct ieee80211_hw *hw) | 2730 | static int ath5k_start(struct ieee80211_hw *hw) |
2720 | { | 2731 | { |
2721 | return ath5k_init(hw->priv); | 2732 | return ath5k_init(hw->priv, false); |
2722 | } | 2733 | } |
2723 | 2734 | ||
2724 | static void ath5k_stop(struct ieee80211_hw *hw) | 2735 | static void ath5k_stop(struct ieee80211_hw *hw) |
2725 | { | 2736 | { |
2726 | ath5k_stop_hw(hw->priv); | 2737 | ath5k_stop_hw(hw->priv, false); |
2727 | } | 2738 | } |
2728 | 2739 | ||
2729 | static int ath5k_add_interface(struct ieee80211_hw *hw, | 2740 | static int ath5k_add_interface(struct ieee80211_hw *hw, |
diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h index 9d0b728928e3..06d1054ca94b 100644 --- a/drivers/net/wireless/ath5k/base.h +++ b/drivers/net/wireless/ath5k/base.h | |||
@@ -128,11 +128,12 @@ struct ath5k_softc { | |||
128 | size_t desc_len; /* size of TX/RX descriptors */ | 128 | size_t desc_len; /* size of TX/RX descriptors */ |
129 | u16 cachelsz; /* cache line size */ | 129 | u16 cachelsz; /* cache line size */ |
130 | 130 | ||
131 | DECLARE_BITMAP(status, 4); | 131 | DECLARE_BITMAP(status, 5); |
132 | #define ATH_STAT_INVALID 0 /* disable hardware accesses */ | 132 | #define ATH_STAT_INVALID 0 /* disable hardware accesses */ |
133 | #define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ | 133 | #define ATH_STAT_MRRETRY 1 /* multi-rate retry support */ |
134 | #define ATH_STAT_PROMISC 2 | 134 | #define ATH_STAT_PROMISC 2 |
135 | #define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ | 135 | #define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */ |
136 | #define ATH_STAT_STARTED 4 /* opened & irqs enabled */ | ||
136 | 137 | ||
137 | unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ | 138 | unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */ |
138 | unsigned int curmode; /* current phy mode */ | 139 | unsigned int curmode; /* current phy mode */ |