[openwrt/openwrt] mac80211: ath9k: Add RX inactivity detection and reset chip
LEDE Commits
lede-commits at lists.infradead.org
Sun Jan 18 16:17:07 PST 2026
hauke pushed a commit to openwrt/openwrt.git, branch openwrt-24.10:
https://git.openwrt.org/6a11ba0fd69213139e753373931006e7cf382b50
commit 6a11ba0fd69213139e753373931006e7cf382b50
Author: Florian Maurer <f.maurer at outlook.de>
AuthorDate: Sat Jan 10 14:05:57 2026 +0100
mac80211: ath9k: Add RX inactivity detection and reset chip
Some ath9k chips can, seemingly at random, end up in a state which can
be described as "deaf". No or nearly no interrupts are generated anymore
for incoming packets. Existing links either break down after a while and
new links will not be established.
This is merged upstream in
https://patchwork.kernel.org/project/linux-wireless/patch/20241106-ath9k-deaf-detection-v1-1-736a150d2425@redhat.com/
Signed-off-by: Florian Maurer <f.maurer at outlook.de>
Link: https://github.com/openwrt/openwrt/pull/21487
Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
---
...th9k-add_rx_inactivity_for_deaf_detection.patch | 163 +++++++++++++++++++++
1 file changed, 163 insertions(+)
diff --git a/package/kernel/mac80211/patches/ath9k/554-ath9k-add_rx_inactivity_for_deaf_detection.patch b/package/kernel/mac80211/patches/ath9k/554-ath9k-add_rx_inactivity_for_deaf_detection.patch
new file mode 100644
index 0000000000..9b01bcd5bd
--- /dev/null
+++ b/package/kernel/mac80211/patches/ath9k/554-ath9k-add_rx_inactivity_for_deaf_detection.patch
@@ -0,0 +1,163 @@
+From: =?utf-8?q?Toke_H=C3=B8iland-J=C3=B8rgensen?= <toke at toke.dk>
+Date: Wed, 06 Nov 2024 13:41:44 +0100
+Subject: [PATCH] ath9k: Add RX inactivity detection and reset chip when it
+ occurs
+
+Some ath9k chips can, seemingly at random, end up in a state which can
+be described as "deaf". No or nearly no interrupts are generated anymore
+for incoming packets. Existing links either break down after a while and
+new links will not be established.
+
+The circumstances leading to this "deafness" is still unclear, but some
+particular chips (especially 2-stream 11n SoCs, but also others) can go
+'deaf' when running AP or mesh (or both) after some time. It's probably
+a hardware issue, and doing a channel scan to trigger a chip
+reset (which one normally can't do on an AP interface) recovers the
+hardware.
+
+The only way the driver can detect this state, is by detecting if there
+has been no RX activity for a while. In this case we can proactively
+reset the chip (which only takes a small number of milliseconds, so
+shouldn't interrupt things too much if it has been idle for several
+seconds), which functions as a workaround.
+
+OpenWrt, and various derivatives, have been carrying versions of this
+workaround for years, that were never upstreamed. One version[0],
+written by Felix Fietkau, used a simple counter and only reset if there
+was precisely zero RX activity for a long period of time. This had the
+problem that in some cases a small number of interrupts would appear
+even if the device was otherwise not responsive. For this reason,
+another version[1], written by Simon Wunderlich and Sven Eckelmann, used
+a time-based approach to calculate the average number of RX interrupts
+over a longer (four-second) interval, and reset the chip when seeing
+less than one interrupt per second over this period. However, that
+version relied on debugfs counters to keep track of the number of
+interrupts, which means it didn't work at all if debugfs was not
+enabled.
+
+This patch unifies the two versions: it uses the same approach as Felix'
+patch to count the number of RX handler invocations, but uses the same
+time-based windowing approach as Simon and Sven's patch to still handle
+the case where occasional interrupts appear but the device is otherwise
+deaf.
+
+Since this is based on ideas by all three people, but not actually
+directly derived from any of the patches, I'm including Suggested-by
+tags from Simon, Sven and Felix below, which should hopefully serve as
+proper credit.
+
+[0] https://patchwork.kernel.org/project/linux-wireless/patch/20170125163654.66431-3-nbd@nbd.name/
+[1] https://patchwork.kernel.org/project/linux-wireless/patch/20161117083614.19188-2-sven.eckelmann@open-mesh.com/
+
+Suggested-by: Simon Wunderlich <sw at simonwunderlich.de>
+Suggested-by: Sven Eckelmann <se at simonwunderlich.de>
+Suggested-by: Felix Fietkau <nbd at nbd.name>
+Signed-off-by: Toke Høiland-Jørgensen <toke at toke.dk>
+Reviewed-by: Sven Eckelmann <se at simonwunderlich.de>
+Acked-by: Simon Wunderlich <sw at simonwunderlich.de>
+Tested-by: Issam Hamdi <ih at simonwunderlich.de>
+Tested-by: Sven Eckelmann <se at simonwunderlich.de>
+---
+ drivers/net/wireless/ath/ath9k/ath9k.h | 2 ++
+ drivers/net/wireless/ath/ath9k/debug.c | 1 +
+ drivers/net/wireless/ath/ath9k/debug.h | 1 +
+ drivers/net/wireless/ath/ath9k/link.c | 33 +++++++++++++++++++++++++++++++--
+ drivers/net/wireless/ath/ath9k/main.c | 1 +
+ 5 files changed, 36 insertions(+), 2 deletions(-)
+
+
+---
+base-commit: c33f9c2728d0ccc7472e6239346c0fb3de556e0f
+change-id: 20241105-ath9k-deaf-detection-0e26bb3243a6
+
+--- a/drivers/net/wireless/ath/ath9k/ath9k.h
++++ b/drivers/net/wireless/ath/ath9k/ath9k.h
+@@ -1041,6 +1041,8 @@ struct ath_softc {
+
+ u8 gtt_cnt;
+ u32 intrstatus;
++ u32 rx_active_check_time;
++ u32 rx_active_count;
+ u16 ps_flags; /* PS_* */
+ bool ps_enabled;
+ bool ps_idle;
+--- a/drivers/net/wireless/ath/ath9k/debug.c
++++ b/drivers/net/wireless/ath/ath9k/debug.c
+@@ -805,6 +805,7 @@ static int read_file_reset(struct seq_fi
+ [RESET_TYPE_CALIBRATION] = "Calibration error",
+ [RESET_TX_DMA_ERROR] = "Tx DMA stop error",
+ [RESET_RX_DMA_ERROR] = "Rx DMA stop error",
++ [RESET_TYPE_RX_INACTIVE] = "Rx path inactive",
+ };
+ int i;
+
+--- a/drivers/net/wireless/ath/ath9k/debug.h
++++ b/drivers/net/wireless/ath/ath9k/debug.h
+@@ -53,6 +53,7 @@ enum ath_reset_type {
+ RESET_TYPE_CALIBRATION,
+ RESET_TX_DMA_ERROR,
+ RESET_RX_DMA_ERROR,
++ RESET_TYPE_RX_INACTIVE,
+ __RESET_TYPE_MAX
+ };
+
+--- a/drivers/net/wireless/ath/ath9k/link.c
++++ b/drivers/net/wireless/ath/ath9k/link.c
+@@ -50,7 +50,36 @@ reset:
+ "tx hung, resetting the chip\n");
+ ath9k_queue_reset(sc, RESET_TYPE_TX_HANG);
+ return false;
++}
++
++#define RX_INACTIVE_CHECK_INTERVAL (4 * MSEC_PER_SEC)
++
++static bool ath_hw_rx_inactive_check(struct ath_softc *sc)
++{
++ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
++ u32 interval, count;
++
++ interval = jiffies_to_msecs(jiffies - sc->rx_active_check_time);
++ count = sc->rx_active_count;
+
++ if (interval < RX_INACTIVE_CHECK_INTERVAL)
++ return true; /* too soon to check */
++
++ sc->rx_active_count = 0;
++ sc->rx_active_check_time = jiffies;
++
++ /* Need at least one interrupt per second, and we should only react if
++ * we are within a factor two of the expected interval
++ */
++ if (interval > RX_INACTIVE_CHECK_INTERVAL * 2 ||
++ count >= interval / MSEC_PER_SEC)
++ return true;
++
++ ath_dbg(common, RESET,
++ "RX inactivity detected. Schedule chip reset\n");
++ ath9k_queue_reset(sc, RESET_TYPE_RX_INACTIVE);
++
++ return false;
+ }
+
+ void ath_hw_check_work(struct work_struct *work)
+@@ -58,8 +87,8 @@ void ath_hw_check_work(struct work_struc
+ struct ath_softc *sc = container_of(work, struct ath_softc,
+ hw_check_work.work);
+
+- if (!ath_hw_check(sc) ||
+- !ath_tx_complete_check(sc))
++ if (!ath_hw_check(sc) || !ath_tx_complete_check(sc) ||
++ !ath_hw_rx_inactive_check(sc))
+ return;
+
+ ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work,
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -454,6 +454,7 @@ void ath9k_tasklet(struct tasklet_struct
+ ath_rx_tasklet(sc, 0, true);
+
+ ath_rx_tasklet(sc, 0, false);
++ sc->rx_active_count++;
+ }
+
+ if (status & ATH9K_INT_TX) {
More information about the lede-commits
mailing list