UPSTREAM: ath9k: cleanup rx stuck detection logic
authorPaul Stewart <pstew@chromium.org>
Thu, 10 May 2012 00:25:29 +0000 (00:25 +0000)
committerOlof Johansson <olof@lixom.net>
Fri, 1 Jun 2012 07:03:04 +0000 (00:03 -0700)
In the following scenario, where the distance b/w STA and AP is ~10m
or in a shield environment by placing an attenuator with reduced AP
txpower, the station started reporting with beacon loss and got
disconnected whenever the chariot endpoint was initiated with BiDi
traffic. In such state, two different stuck cases were observed.

  * rx clear is stuck at low for more than 100ms
  * dcu chain and complete state is stuck.

This patch triggers stuck detection if three consecutive beacons are
missed. If any of the above conditions matches, it triggers a chip
reset to recover. This issue was originally reported in 3.0 kernel with
AR9382 chip by having two stations associated with two different APs in
the same channel and was attenuated/controlled by Azimuth ADEPT-n box.

Cc: Paul Stewart <pstew@google.com>
Cc: Gary Morain <gmorain@google.com>
Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.com>
BUG=chrome-os-partner:4886
TEST=Torture testing Azimuth with hidden nodes; manual over-the-air
testing.

Originally-Reviewed-on: https://gerrit.chromium.org/gerrit/17986
Signed-off-by: Paul Stewart <pstew@chromium.org>
(cherry picked from commit f831f081912a9abe0a8756549c5ae41dc5630918)

Change-Id: I03b0b45444ab8848196e7a99f0a1372204e1688a
Reviewed-on: https://gerrit.chromium.org/gerrit/18629
Reviewed-by: Gary Morain <gmorain@chromium.org>
Commit-Ready: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/recv.c

index 08fe6a9..f2792fc 100644 (file)
@@ -431,6 +431,7 @@ void ath_reset_work(struct work_struct *work);
 void ath_hw_check(struct work_struct *work);
 void ath_hw_pll_work(struct work_struct *work);
 void ath_rx_poll_work(unsigned long data);
+void ath_start_rx_poll(struct ath_softc *sc, u32 nmsec);
 void ath_paprd_calibrate(struct work_struct *work);
 void ath_ani_calibrate(unsigned long data);
 void ath_start_ani(struct ath_common *common);
index c3a3cdb..b9cc4d0 100644 (file)
@@ -3075,35 +3075,49 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len)
 }
 EXPORT_SYMBOL(ath9k_hw_name);
 
-#define DCU_COMPLETE_STATE  1
-#define NUM_STATUS_READS    50
+static bool ath9k_hw_check_dcs(u32 dma_dbg, int num_dcu_states,
+                              int *hang_state, int *hang_pos)
+{
+       static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */
+       u32 chain_state, dcs_pos, i;
+
+       for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) {
+               chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f;
+               for (i = 0; i < 3; i++) {
+                       if (chain_state == dcu_chain_state[i]) {
+                               *hang_state = chain_state;
+                               *hang_pos = dcs_pos;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+#define DCU_COMPLETE_STATE        1
+#define DCU_COMPLETE_STATE_MASK 0x3
+#define NUM_STATUS_READS         50
 bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
 {
-       u32 dma_dbg_4 = 0, dma_dbg_5 = 0, dma_dbg_6;
-       u32 cur_chain_state = 0;
-       int i, hang_pos, hang_state = 0;
+       u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4;
+       u32 i, hang_pos, hang_state;
 
-       dma_dbg_6 = REG_READ(ah, AR_DMADBG_6);
+       comp_state = REG_READ(ah, AR_DMADBG_6);
 
-       if ((dma_dbg_6 & 0x3) != DCU_COMPLETE_STATE) {
+       if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) {
                ath_dbg(ath9k_hw_common(ah), RX_STUCK,
                        "MAC Hang signature not found at DCU complete\n");
                return false;
        }
 
-       dma_dbg_5 = REG_READ(ah, AR_DMADBG_5);
-       for (hang_pos = 3; hang_pos >= 0; hang_pos--) {
-               hang_state = (dma_dbg_5 >> (5 * hang_pos)) & 0x1f;
-               if (hang_state)
-                       goto hang_check_iter;
-       }
+       chain_state = REG_READ(ah, dcs_reg);
+       if (ath9k_hw_check_dcs(chain_state, 6, &hang_state, &hang_pos))
+               goto hang_check_iter;
 
-       dma_dbg_4 = REG_READ(ah, AR_DMADBG_4);
-       for (hang_pos = 5; hang_pos >= 0; hang_pos--) {
-               hang_state = (dma_dbg_4 >> (5 * hang_pos)) & 0x1f;
-               if (hang_state)
-                       goto hang_check_iter;
-       }
+       dcs_reg = AR_DMADBG_5;
+       chain_state = REG_READ(ah, dcs_reg);
+       if (ath9k_hw_check_dcs(chain_state, 4, &hang_state, &hang_pos))
+               goto hang_check_iter;
 
        ath_dbg(ath9k_hw_common(ah), RX_STUCK,
                "MAC Hang signature 1 not found\n");
@@ -3111,22 +3125,17 @@ bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
 
 hang_check_iter:
        ath_dbg(ath9k_hw_common(ah), RX_STUCK,
-               "DMA Registers: %x %x %x Hang pos: %d\n",
-               dma_dbg_4, dma_dbg_5, dma_dbg_6, hang_pos);
-
+               "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n",
+               chain_state, comp_state, hang_state, hang_pos);
 
        for (i = 0; i < NUM_STATUS_READS; i++) {
-               if (dma_dbg_4) {
-                       dma_dbg_4 = REG_READ(ah, AR_DMADBG_4);
-                       cur_chain_state = (dma_dbg_4 >> (5 * hang_pos)) & 0x1f;
-               } else {
-                       dma_dbg_5 = REG_READ(ah, AR_DMADBG_5);
-                       cur_chain_state = (dma_dbg_5 >> (5 * hang_pos)) & 0x1f;
-               }
-               dma_dbg_6 = REG_READ(ah, AR_DMADBG_6);
+               chain_state = REG_READ(ah, dcs_reg);
+               chain_state = (chain_state >> (5 * hang_pos)) & 0x1f;
+               comp_state = REG_READ(ah, AR_DMADBG_6);
 
-               if (((dma_dbg_6 & 0x3) != DCU_COMPLETE_STATE) ||
-                   (cur_chain_state != hang_state))
+               if (((comp_state & DCU_COMPLETE_STATE_MASK) !=
+                     DCU_COMPLETE_STATE) ||
+                   (chain_state != hang_state))
                        return false;
        }
 
index ab24137..074e3c2 100644 (file)
@@ -1027,7 +1027,6 @@ void ar9002_hw_attach_ops(struct ath_hw *ah);
 void ar9003_hw_attach_ops(struct ath_hw *ah);
 
 void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan);
-void ar9003_hw_dump_txdesc(struct ath_hw *ah);
 bool ath9k_hw_detect_mac_hang(struct ath_hw *ah);
 /*
  * ANI work can be shared between all families but a next
index 6293597..1ae599a 100644 (file)
@@ -290,13 +290,11 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
                if (sc->sc_flags & SC_OP_BEACONS)
                        ath_set_beacon(sc);
 
-               if (sc->sc_flags & SC_OP_PRIM_STA_VIF)
-                       mod_timer(&sc->rx_poll_timer,
-                                 jiffies + msecs_to_jiffies(10));
                ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
                ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
                if (!common->disable_ani)
                        ath_start_ani(common);
+               ath_start_rx_poll(sc, 100);
        }
 
        if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3) {
@@ -674,6 +672,17 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
                ath_tx_node_cleanup(sc, an);
 }
 
+void ath_start_rx_poll(struct ath_softc *sc, u32 nmsec)
+{
+       if (!AR_SREV_9300(sc->sc_ah))
+               return;
+
+       if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
+               return;
+
+       mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies(nmsec));
+}
+
 void ath_rx_poll_work(unsigned long data)
 {
        struct ath_softc *sc = (struct ath_softc *)data;
@@ -684,21 +693,16 @@ void ath_rx_poll_work(unsigned long data)
        static u32 iter, match_count;
        static u64 last_run;
        unsigned long flags;
-       u32 rx_clear, rx, tx, delay = 10, reg;
-       int i, j;
+       u32 rx_clear, rx, tx, reg;
+       int i, j, len;
        u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
-       u8 nread;
+       u8 nread, nmsec = 10, buf[200];
 
        if (jiffies_to_msecs(jiffies - last_run) > 30)
                iter = match_count = 0;
-       else {
-               if (atomic_read(&sc->stop_rx_poll) && iter) {
-                       iter = match_count = 0;
-                       return;
-               }
+       else
                iter += 1;
-       }
-       sc->ps_flags |= PS_WAIT_FOR_BEACON;
+
        ath9k_ps_wakeup(sc);
 
        spin_lock_irqsave(&common->cc_lock, flags);
@@ -708,7 +712,6 @@ void ath_rx_poll_work(unsigned long data)
        rx = common->cc_rxpoll.rx_frame * 100 / common->cc_rxpoll.cycles;
        tx = common->cc_rxpoll.tx_frame * 100 / common->cc_rxpoll.cycles;
        memset(&common->cc_rxpoll, 0, sizeof(common->cc_rxpoll));
-
        spin_unlock_irqrestore(&common->cc_lock, flags);
 
        ath_dbg(common, RX_STUCK,
@@ -723,7 +726,7 @@ void ath_rx_poll_work(unsigned long data)
                REG_READ(ah, AR_IMR), REG_READ(ah, AR_IER),
                atomic_read(&ah->intr_ref_cnt));
 
-       ar9003_hw_dump_txdesc(ah);
+       ath_dbg(common, RX_STUCK, "ts tail %d\n", ah->ts_tail);
 
        REG_SET_BIT(ah, AR_DIAG_SW, 0x8080000);
        for (i = 0; i < 5; i++) {
@@ -902,42 +905,40 @@ void ath_rx_poll_work(unsigned long data)
                "Chain | privNF | # Readings | NF Readings\n");
        for (i = 0; i < 6; i++) {
                if (!(chainmask & (1 << i)) ||
-                               ((i >= 3) && !conf_is_ht40(conf)))
+                   ((i >= 3) && !conf_is_ht40(conf)))
                        continue;
 
+               memset(buf, 0, sizeof(buf));
+               len = 0;
                nread = 5 - h[i].invalidNFcount;
-               ath_dbg(common, RX_STUCK,
-               " %d\t %d\t %d\t\t", i, h[i].privNF, nread);
                for (j = 0; j < nread; j++)
-                       ath_dbg(common, RX_STUCK,
-                               " %d", h[i].nfCalBuffer[j]);
-               ath_dbg(common, RX_STUCK, "\n");
+                       len += snprintf(buf + len, sizeof(buf) - len,
+                                       "%d ", h[i].nfCalBuffer[j]);
+               ath_dbg(common, RX_STUCK, " %d\t %d\t %d\t\t %s",
+                       i, h[i].privNF, nread, buf);
        }
 
        last_run = jiffies;
        if (rx_clear > 98) {
-               ath_dbg(common, RX_STUCK,
-                       "rx clear %d tx %d matched count %d\n",
-                       rx_clear, tx, match_count);
-               if (match_count++ > 9) {
-                       ath9k_ps_restore(sc);
-                       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-                       iter = match_count = 0;
-                       return;
-               }
-       } else if (ath9k_hw_detect_mac_hang(ah)) {
-               ath_dbg(common, RX_STUCK, "MAC hang signature found\n");
-               ath9k_ps_restore(sc);
-               ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
-               iter = match_count = 0;
-               return;
-       } else if (iter >= 15) {
+               ath_dbg(common, RESET,
+                       "rx clear %d match count %d iteration %d\n",
+                       rx_clear, match_count, iter);
+               if (match_count++ > 15)
+                       goto queue_reset_work;
+       } else if (ath9k_hw_detect_mac_hang(ah))
+               goto queue_reset_work;
+       else if (iter >= 9) {
                iter = match_count = 0;
-               delay = 200;
+               nmsec = 200;
        }
        ath9k_ps_restore(sc);
-       atomic_set(&sc->stop_rx_poll, 0);
-       mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies(delay));
+       ath_start_rx_poll(sc, nmsec);
+       return;
+
+queue_reset_work:
+       ath9k_ps_restore(sc);
+       ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+       iter = match_count = 0;
 }
 
 void ath9k_tasklet(unsigned long data)
@@ -1195,9 +1196,7 @@ static int ath_reset(struct ath_softc *sc, bool retry_tx)
        if ((sc->sc_flags & SC_OP_BEACONS) || !(sc->sc_flags & (SC_OP_OFFCHANNEL)))
                ath_set_beacon(sc);     /* restart beacons */
 
-       if (sc->sc_flags & SC_OP_PRIM_STA_VIF)
-               mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies(300));
-
+       ath_start_rx_poll(sc, 300);
        ath9k_hw_set_interrupts(ah);
        ath9k_hw_enable_interrupts(ah);
 
@@ -2232,11 +2231,8 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
                if (!common->disable_ani) {
                        sc->sc_flags |= SC_OP_ANI_RUN;
                        ath_start_ani(common);
-                       atomic_set(&sc->stop_rx_poll, 0);
-                       mod_timer(&sc->rx_poll_timer,
-                                 jiffies + msecs_to_jiffies(300));
                }
-
+               ath_start_rx_poll(sc, 300);
        }
 }
 
index e5d2fb0..a15ec8d 100644 (file)
@@ -1841,11 +1841,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
 
                memset(rxs, 0, sizeof(struct ieee80211_rx_status));
 
-               if (rs.is_mybeacon) {
-                       atomic_set(&sc->stop_rx_poll, 1);
-                       mod_timer(&sc->rx_poll_timer,
-                                 jiffies + msecs_to_jiffies(300));
-               }
+               if (rs.is_mybeacon)
+                       ath_start_rx_poll(sc, 300);
 
                rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
                if (rs.rs_tstamp > tsf_lower &&