Commit cb8d61de authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville

ath9k: add additional checks for the baseband hang detection

Since even with the latest changes the false positive issue of the baseband
hang check is not fully solved yet, additional checks are needed.
If the baseband hang occurs, the rx_clear signal will be stuck to high, so
we can use the cycle counters to confirm it.
With this patch, a hardware reset is only triggered if the baseband hang
check returned true three times in a row, with a beacon interval between
each check and if the busy time was also 99% or more during the check
intervals.
Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent b1f93314
...@@ -602,6 +602,8 @@ struct ath_softc { ...@@ -602,6 +602,8 @@ struct ath_softc {
struct completion paprd_complete; struct completion paprd_complete;
bool paprd_pending; bool paprd_pending;
unsigned int hw_busy_count;
u32 intrstatus; u32 intrstatus;
u32 sc_flags; /* SC_OP_* */ u32 sc_flags; /* SC_OP_* */
u16 ps_flags; /* PS_* */ u16 ps_flags; /* PS_* */
......
...@@ -153,7 +153,12 @@ static void ath_update_survey_nf(struct ath_softc *sc, int channel) ...@@ -153,7 +153,12 @@ static void ath_update_survey_nf(struct ath_softc *sc, int channel)
} }
} }
static void ath_update_survey_stats(struct ath_softc *sc) /*
* Updates the survey statistics and returns the busy time since last
* update in %, if the measurement duration was long enough for the
* result to be useful, -1 otherwise.
*/
static int ath_update_survey_stats(struct ath_softc *sc)
{ {
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
...@@ -161,9 +166,10 @@ static void ath_update_survey_stats(struct ath_softc *sc) ...@@ -161,9 +166,10 @@ static void ath_update_survey_stats(struct ath_softc *sc)
struct survey_info *survey = &sc->survey[pos]; struct survey_info *survey = &sc->survey[pos];
struct ath_cycle_counters *cc = &common->cc_survey; struct ath_cycle_counters *cc = &common->cc_survey;
unsigned int div = common->clockrate * 1000; unsigned int div = common->clockrate * 1000;
int ret = 0;
if (!ah->curchan) if (!ah->curchan)
return; return -1;
if (ah->power_mode == ATH9K_PM_AWAKE) if (ah->power_mode == ATH9K_PM_AWAKE)
ath_hw_cycle_counters_update(common); ath_hw_cycle_counters_update(common);
...@@ -178,9 +184,18 @@ static void ath_update_survey_stats(struct ath_softc *sc) ...@@ -178,9 +184,18 @@ static void ath_update_survey_stats(struct ath_softc *sc)
survey->channel_time_rx += cc->rx_frame / div; survey->channel_time_rx += cc->rx_frame / div;
survey->channel_time_tx += cc->tx_frame / div; survey->channel_time_tx += cc->tx_frame / div;
} }
if (cc->cycles < div)
return -1;
if (cc->cycles > 0)
ret = cc->rx_busy * 100 / cc->cycles;
memset(cc, 0, sizeof(*cc)); memset(cc, 0, sizeof(*cc));
ath_update_survey_nf(sc, pos); ath_update_survey_nf(sc, pos);
return ret;
} }
/* /*
...@@ -202,6 +217,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, ...@@ -202,6 +217,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
if (sc->sc_flags & SC_OP_INVALID) if (sc->sc_flags & SC_OP_INVALID)
return -EIO; return -EIO;
sc->hw_busy_count = 0;
del_timer_sync(&common->ani.timer); del_timer_sync(&common->ani.timer);
cancel_work_sync(&sc->paprd_work); cancel_work_sync(&sc->paprd_work);
cancel_work_sync(&sc->hw_check_work); cancel_work_sync(&sc->hw_check_work);
...@@ -569,17 +586,25 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) ...@@ -569,17 +586,25 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
void ath_hw_check(struct work_struct *work) void ath_hw_check(struct work_struct *work)
{ {
struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
int i; struct ath_common *common = ath9k_hw_common(sc->sc_ah);
unsigned long flags;
int busy;
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
if (ath9k_hw_check_alive(sc->sc_ah))
goto out;
for (i = 0; i < 3; i++) { spin_lock_irqsave(&common->cc_lock, flags);
if (ath9k_hw_check_alive(sc->sc_ah)) busy = ath_update_survey_stats(sc);
goto out; spin_unlock_irqrestore(&common->cc_lock, flags);
msleep(1); ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, "
} "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1);
ath_reset(sc, true); if (busy >= 99) {
if (++sc->hw_busy_count >= 3)
ath_reset(sc, true);
} else if (busy >= 0)
sc->hw_busy_count = 0;
out: out:
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
...@@ -930,6 +955,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) ...@@ -930,6 +955,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
struct ieee80211_hw *hw = sc->hw; struct ieee80211_hw *hw = sc->hw;
int r; int r;
sc->hw_busy_count = 0;
/* Stop ANI */ /* Stop ANI */
del_timer_sync(&common->ani.timer); del_timer_sync(&common->ani.timer);
......
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