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

Reply via email to