Commit 96148326 authored by Gabor Juhos's avatar Gabor Juhos Committed by John W. Linville

ath9k: fix race with IEEE80211_CONF_PS checks

There is a small window where the mac80211 changes the IEEE80211_CONF_PS
flag, and then informs the driver about the change. We have a race
condition if we are checking the flag in the same time. Avoid it by
introducing a local variable, and using that instead of checking the
IEEE80211_CONF_PS flag directly.

This fix the problem reported by Luis:
http://article.gmane.org/gmane.linux.kernel.wireless.general/34363

Changes-licensed-under: ISC
Signed-off-by: default avatarGabor Juhos <juhosg@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent ff8365ca
...@@ -573,6 +573,7 @@ struct ath_softc { ...@@ -573,6 +573,7 @@ struct ath_softc {
u32 keymax; u32 keymax;
DECLARE_BITMAP(keymap, ATH_KEYMAX); DECLARE_BITMAP(keymap, ATH_KEYMAX);
u8 splitmic; u8 splitmic;
bool ps_enabled;
unsigned long ps_usecount; unsigned long ps_usecount;
enum ath9k_int imask; enum ath9k_int imask;
enum ath9k_ht_extprotspacing ht_extprotspacing; enum ath9k_ht_extprotspacing ht_extprotspacing;
......
...@@ -2863,10 +2863,8 @@ void ath9k_ps_wakeup(struct ath_softc *sc) ...@@ -2863,10 +2863,8 @@ void ath9k_ps_wakeup(struct ath_softc *sc)
if (++sc->ps_usecount != 1) if (++sc->ps_usecount != 1)
goto unlock; goto unlock;
if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE) { if (sc->sc_ah->power_mode != ATH9K_PM_AWAKE)
sc->sc_ah->restore_mode = sc->sc_ah->power_mode;
ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_AWAKE); ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_AWAKE);
}
unlock: unlock:
spin_unlock_irqrestore(&sc->sc_pm_lock, flags); spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
...@@ -2880,13 +2878,12 @@ void ath9k_ps_restore(struct ath_softc *sc) ...@@ -2880,13 +2878,12 @@ void ath9k_ps_restore(struct ath_softc *sc)
if (--sc->ps_usecount != 0) if (--sc->ps_usecount != 0)
goto unlock; goto unlock;
if ((sc->hw->conf.flags & IEEE80211_CONF_PS) && if (sc->ps_enabled &&
!(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON |
SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_CAB |
SC_OP_WAIT_FOR_PSPOLL_DATA | SC_OP_WAIT_FOR_PSPOLL_DATA |
SC_OP_WAIT_FOR_TX_ACK))) SC_OP_WAIT_FOR_TX_ACK)))
ath9k_hw_setpower_nolock(sc->sc_ah, ath9k_hw_setpower_nolock(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP);
sc->sc_ah->restore_mode);
unlock: unlock:
spin_unlock_irqrestore(&sc->sc_pm_lock, flags); spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
......
...@@ -421,7 +421,6 @@ struct ath_hw { ...@@ -421,7 +421,6 @@ struct ath_hw {
enum nl80211_iftype opmode; enum nl80211_iftype opmode;
enum ath9k_power_mode power_mode; enum ath9k_power_mode power_mode;
enum ath9k_power_mode restore_mode;
struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS];
struct ar5416Stats stats; struct ar5416Stats stats;
......
...@@ -499,8 +499,7 @@ static void ath9k_tasklet(unsigned long data) ...@@ -499,8 +499,7 @@ static void ath9k_tasklet(unsigned long data)
if (status & ATH9K_INT_TX) if (status & ATH9K_INT_TX)
ath_tx_tasklet(sc); ath_tx_tasklet(sc);
if ((status & ATH9K_INT_TSFOOR) && if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
(sc->hw->conf.flags & IEEE80211_CONF_PS)) {
/* /*
* TSF sync does not look correct; remain awake to sync with * TSF sync does not look correct; remain awake to sync with
* the next Beacon. * the next Beacon.
...@@ -2001,7 +2000,7 @@ static int ath9k_tx(struct ieee80211_hw *hw, ...@@ -2001,7 +2000,7 @@ static int ath9k_tx(struct ieee80211_hw *hw,
goto exit; goto exit;
} }
if (sc->hw->conf.flags & IEEE80211_CONF_PS) { if (sc->ps_enabled) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
/* /*
* mac80211 does not set PM field for normal data frames, so we * mac80211 does not set PM field for normal data frames, so we
...@@ -2289,8 +2288,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) ...@@ -2289,8 +2288,9 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
} }
ath9k_hw_setrxabort(sc->sc_ah, 1); ath9k_hw_setrxabort(sc->sc_ah, 1);
} }
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); sc->ps_enabled = true;
} else { } else {
sc->ps_enabled = false;
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE);
if (!(ah->caps.hw_caps & if (!(ah->caps.hw_caps &
ATH9K_HW_CAP_AUTOSLEEP)) { ATH9K_HW_CAP_AUTOSLEEP)) {
......
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