| 1 | --- a/drivers/net/wireless/ath/ath9k/main.c |
| 2 | +++ b/drivers/net/wireless/ath/ath9k/main.c |
| 3 | @@ -177,7 +177,12 @@ static void ath_update_survey_nf(struct |
| 4 | } |
| 5 | } |
| 6 | |
| 7 | -static void ath_update_survey_stats(struct ath_softc *sc) |
| 8 | +/* |
| 9 | + * Updates the survey statistics and returns the busy time since last |
| 10 | + * update in %, if the measurement duration was long enough for the |
| 11 | + * result to be useful, -1 otherwise. |
| 12 | + */ |
| 13 | +static int ath_update_survey_stats(struct ath_softc *sc) |
| 14 | { |
| 15 | struct ath_hw *ah = sc->sc_ah; |
| 16 | struct ath_common *common = ath9k_hw_common(ah); |
| 17 | @@ -185,9 +190,10 @@ static void ath_update_survey_stats(stru |
| 18 | struct survey_info *survey = &sc->survey[pos]; |
| 19 | struct ath_cycle_counters *cc = &common->cc_survey; |
| 20 | unsigned int div = common->clockrate * 1000; |
| 21 | + int ret = 0; |
| 22 | |
| 23 | if (!ah->curchan) |
| 24 | - return; |
| 25 | + return -1; |
| 26 | |
| 27 | if (ah->power_mode == ATH9K_PM_AWAKE) |
| 28 | ath_hw_cycle_counters_update(common); |
| 29 | @@ -202,9 +208,18 @@ static void ath_update_survey_stats(stru |
| 30 | survey->channel_time_rx += cc->rx_frame / div; |
| 31 | survey->channel_time_tx += cc->tx_frame / div; |
| 32 | } |
| 33 | + |
| 34 | + if (cc->cycles < div) |
| 35 | + return -1; |
| 36 | + |
| 37 | + if (cc->cycles > 0) |
| 38 | + ret = cc->rx_busy * 100 / cc->cycles; |
| 39 | + |
| 40 | memset(cc, 0, sizeof(*cc)); |
| 41 | |
| 42 | ath_update_survey_nf(sc, pos); |
| 43 | + |
| 44 | + return ret; |
| 45 | } |
| 46 | |
| 47 | /* |
| 48 | @@ -226,6 +241,8 @@ int ath_set_channel(struct ath_softc *sc |
| 49 | if (sc->sc_flags & SC_OP_INVALID) |
| 50 | return -EIO; |
| 51 | |
| 52 | + sc->hw_busy_count = 0; |
| 53 | + |
| 54 | del_timer_sync(&common->ani.timer); |
| 55 | cancel_work_sync(&sc->paprd_work); |
| 56 | cancel_work_sync(&sc->hw_check_work); |
| 57 | @@ -584,17 +601,25 @@ static void ath_node_detach(struct ath_s |
| 58 | void ath_hw_check(struct work_struct *work) |
| 59 | { |
| 60 | struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); |
| 61 | - int i; |
| 62 | + struct ath_common *common = ath9k_hw_common(sc->sc_ah); |
| 63 | + unsigned long flags; |
| 64 | + int busy; |
| 65 | |
| 66 | ath9k_ps_wakeup(sc); |
| 67 | + if (ath9k_hw_check_alive(sc->sc_ah)) |
| 68 | + goto out; |
| 69 | |
| 70 | - for (i = 0; i < 3; i++) { |
| 71 | - if (ath9k_hw_check_alive(sc->sc_ah)) |
| 72 | - goto out; |
| 73 | + spin_lock_irqsave(&common->cc_lock, flags); |
| 74 | + busy = ath_update_survey_stats(sc); |
| 75 | + spin_unlock_irqrestore(&common->cc_lock, flags); |
| 76 | |
| 77 | - msleep(1); |
| 78 | - } |
| 79 | - ath_reset(sc, true); |
| 80 | + ath_dbg(common, ATH_DBG_RESET, "Possible baseband hang, " |
| 81 | + "busy=%d (try %d)\n", busy, sc->hw_busy_count + 1); |
| 82 | + if (busy >= 99) { |
| 83 | + if (++sc->hw_busy_count >= 3) |
| 84 | + ath_reset(sc, true); |
| 85 | + } else if (busy >= 0) |
| 86 | + sc->hw_busy_count = 0; |
| 87 | |
| 88 | out: |
| 89 | ath9k_ps_restore(sc); |
| 90 | @@ -988,6 +1013,8 @@ int ath_reset(struct ath_softc *sc, bool |
| 91 | struct ieee80211_hw *hw = sc->hw; |
| 92 | int r; |
| 93 | |
| 94 | + sc->hw_busy_count = 0; |
| 95 | + |
| 96 | /* Stop ANI */ |
| 97 | del_timer_sync(&common->ani.timer); |
| 98 | |
| 99 | --- a/drivers/net/wireless/ath/ath9k/ath9k.h |
| 100 | +++ b/drivers/net/wireless/ath/ath9k/ath9k.h |
| 101 | @@ -598,6 +598,8 @@ struct ath_softc { |
| 102 | struct completion paprd_complete; |
| 103 | bool paprd_pending; |
| 104 | |
| 105 | + unsigned int hw_busy_count; |
| 106 | + |
| 107 | u32 intrstatus; |
| 108 | u32 sc_flags; /* SC_OP_* */ |
| 109 | u16 ps_flags; /* PS_* */ |
| 110 | |