ath5k: fix suspend-related oops on rmmod
authorBob Copeland <me@bobcopeland.com>
Thu, 16 Oct 2008 15:02:06 +0000 (11:02 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 22 Oct 2008 22:09:32 +0000 (18:09 -0400)
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>
drivers/net/wireless/ath5k/base.c
drivers/net/wireless/ath5k/base.h

index 9b95c4049b313492055198077de7d3f6458c2fbc..0f1d6bdd51a268f4b28ec1e79bc2c93e8fb84810 100644 (file)
@@ -340,9 +340,9 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
 }
 
 /* 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);
 
@@ -646,7 +646,7 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
 
        ath5k_led_off(sc);
 
-       ath5k_stop_hw(sc);
+       ath5k_stop_hw(sc, true);
 
        free_irq(pdev->irq, sc);
        pci_save_state(pdev);
@@ -683,7 +683,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);
@@ -2200,12 +2200,17 @@ ath5k_beacon_config(struct ath5k_softc *sc)
 \********************/
 
 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);
 
        /*
@@ -2230,12 +2235,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();
@@ -2290,7 +2298,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;
 
@@ -2321,6 +2329,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);
 
@@ -2718,12 +2729,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 9d0b728928e305ab982deb0edd7448eb821cfa93..06d1054ca94b9cc525ee4f9516c5363b26d39fa2 100644 (file)
@@ -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 */