git: fb3edd4f3ff8 - main - [ath] do a cold reset if TSFOOR triggers

Adrian Chadd adrian at FreeBSD.org
Sat Mar 13 07:35:43 UTC 2021


The branch main has been updated by adrian:

URL: https://cgit.FreeBSD.org/src/commit/?id=fb3edd4f3ff89f5883b8309ff71ff4a426279c85

commit fb3edd4f3ff89f5883b8309ff71ff4a426279c85
Author:     Adrian Chadd <adrian at FreeBSD.org>
AuthorDate: 2021-03-13 07:30:25 +0000
Commit:     Adrian Chadd <adrian at FreeBSD.org>
CommitDate: 2021-03-13 07:30:25 +0000

    [ath] do a cold reset if TSFOOR triggers
    
    TSFOOR happens if a beacon with a given TSF isn't received within the
    programmed/expected TSF value, plus/minus a fudge range. (OOR == out of range.)
    
    If this happens then it could be because the baseband/mac is stuck, or
    the baseband is deaf.  So, do a cold reset and resync the beacon to
    try and unstick the hardware.
    
    It also happens when a bad AP decides to err, slew its TSF because they
    themselves are resetting and they don't preserve the TSF "well."
    
    This has fixed a bunch of weird corner cases on my 2GHz AP radio upstairs
    here where it occasionally goes deaf due to how much 2GHz noise is up
    here (and ANI gets a little sideways) and this unsticks the station
    VAP.
    
    For AP modes a hung baseband/mac usually ends up as a stuck beacon
    and those have been addressed for a long time by just resetting the
    hardware.  But similar hangs in station mode didn't have a similar
    recovery mechanism.
    
    Tested:
    
    * AR9380, STA mode, 2GHz/5GHz
    * AR9580, STA mode, 5GHz
    * QCA9344 SoC w/ on-board wifi (TL-WDR4300/3600 devices); 2GHz
      STA mode
---
 sys/dev/ath/if_ath.c      | 50 ++++++++++++++++++++++++++++++++++++++++++-----
 sys/dev/ath/if_athioctl.h |  1 +
 sys/dev/ath/if_athvar.h   |  1 +
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c
index 0ef10e529dd5..698b8d1a2853 100644
--- a/sys/dev/ath/if_ath.c
+++ b/sys/dev/ath/if_ath.c
@@ -165,6 +165,7 @@ static void	ath_parent(struct ieee80211com *);
 static void	ath_fatal_proc(void *, int);
 static void	ath_bmiss_vap(struct ieee80211vap *);
 static void	ath_bmiss_proc(void *, int);
+static void	ath_tsfoor_proc(void *, int);
 static void	ath_key_update_begin(struct ieee80211vap *);
 static void	ath_key_update_end(struct ieee80211vap *);
 static void	ath_update_mcast_hw(struct ath_softc *);
@@ -761,6 +762,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
 
 	TASK_INIT(&sc->sc_rxtask, 0, sc->sc_rx.recv_tasklet, sc);
 	TASK_INIT(&sc->sc_bmisstask, 0, ath_bmiss_proc, sc);
+	TASK_INIT(&sc->sc_tsfoortask, 0, ath_tsfoor_proc, sc);
 	TASK_INIT(&sc->sc_bstucktask,0, ath_bstuck_proc, sc);
 	TASK_INIT(&sc->sc_resettask,0, ath_reset_proc, sc);
 	TASK_INIT(&sc->sc_txqtask, 0, ath_txq_sched_tasklet, sc);
@@ -2333,13 +2335,18 @@ ath_intr(void *arg)
 			sc->sc_stats.ast_rxorn++;
 		}
 		if (status & HAL_INT_TSFOOR) {
-			/* out of range beacon - wake the chip up,
-			 * but don't modify self-gen frame config */
-			device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__);
-			sc->sc_syncbeacon = 1;
+			/*
+			 * out of range beacon - wake the chip up,
+			 * but don't modify self-gen frame config.
+			 * Do a full reset to clear any potential stuck
+			 * PHY/MAC that generated this condition.
+			 */
+			sc->sc_stats.ast_tsfoor++;
 			ATH_LOCK(sc);
 			ath_power_setpower(sc, HAL_PM_AWAKE, 0);
 			ATH_UNLOCK(sc);
+			taskqueue_enqueue(sc->sc_tq, &sc->sc_tsfoortask);
+			device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__);
 		}
 		if (status & HAL_INT_MCI) {
 			ath_btcoex_mci_intr(sc);
@@ -2483,7 +2490,7 @@ ath_bmiss_proc(void *arg, int pending)
 	ath_beacon_miss(sc);
 
 	/*
-	 * Do a reset upon any becaon miss event.
+	 * Do a reset upon any beacon miss event.
 	 *
 	 * It may be a non-recognised RX clear hang which needs a reset
 	 * to clear.
@@ -2505,6 +2512,39 @@ ath_bmiss_proc(void *arg, int pending)
 	ATH_UNLOCK(sc);
 }
 
+/*
+ * Handle a TSF out of range interrupt in STA mode.
+ *
+ * This may be due to a partially deaf looking radio, so
+ * do a full reset just in case it is indeed deaf and
+ * resync the beacon.
+ */
+static void
+ath_tsfoor_proc(void *arg, int pending)
+{
+	struct ath_softc *sc = arg;
+
+	DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending);
+
+	ATH_LOCK(sc);
+	ath_power_set_power_state(sc, HAL_PM_AWAKE);
+	ATH_UNLOCK(sc);
+
+	/*
+	 * Do a full reset after any TSFOOR.  It's possible that
+	 * we've gone deaf or partially deaf (eg due to calibration
+	 * failures) and this should clean things up a bit.
+	 */
+	ath_reset(sc, ATH_RESET_NOLOSS, HAL_RESET_FORCE_COLD);
+
+	/* Force a beacon resync, in case they've drifted */
+	sc->sc_syncbeacon = 1;
+
+	ATH_LOCK(sc);
+	ath_power_restore_power_state(sc);
+	ATH_UNLOCK(sc);
+}
+
 /*
  * Handle TKIP MIC setup to deal hardware that doesn't do MIC
  * calcs together with WME.  If necessary disable the crypto
diff --git a/sys/dev/ath/if_athioctl.h b/sys/dev/ath/if_athioctl.h
index fec48bb4c890..5043cd8ab467 100644
--- a/sys/dev/ath/if_athioctl.h
+++ b/sys/dev/ath/if_athioctl.h
@@ -177,6 +177,7 @@ struct ath_stats {
 	u_int32_t	ast_tx_nodeq_overflow;	/* node sw queue overflow */
 	u_int32_t	ast_tx_ldpc;		/* TX LDPC frame */
 	u_int32_t	ast_tx_stbc;		/* TX STBC frame */
+	u_int32_t	ast_tsfoor;		/* TSFOOR interrupts */
 	u_int32_t	ast_pad[10];
 };
 
diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h
index 7817fe667281..1d2f842197cd 100644
--- a/sys/dev/ath/if_athvar.h
+++ b/sys/dev/ath/if_athvar.h
@@ -784,6 +784,7 @@ struct ath_softc {
 						/* recent tx frames/antenna */
 	struct ath_txq		*sc_cabq;	/* tx q for cab frames */
 	struct task		sc_bmisstask;	/* bmiss int processing */
+	struct task		sc_tsfoortask;	/* TSFOOR int processing */
 	struct task		sc_bstucktask;	/* stuck beacon processing */
 	struct task		sc_resettask;	/* interface reset task */
 	struct task		sc_fataltask;	/* fatal task */


More information about the dev-commits-src-main mailing list