Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+19 −8
Original line number Original line Diff line number Diff line
@@ -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)


	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)
		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)
\********************/
\********************/


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)
	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)
 * 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)
		}
		}
	}
	}
	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)


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,
+2 −1
Original line number Original line Diff line number Diff line
@@ -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 */