Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH 0/2] wifi: ath9k_htc: cope with lost WMI reads
@ 2026-09-03 17:00 Nerijus Bendžiūnas
  2026-09-03 17:00 ` [PATCH 1/2] wifi: ath9k_htc: derive the PHY error filter bits from software state Nerijus Bendžiūnas
  2026-09-03 17:00 ` [PATCH 2/2] wifi: ath9k_htc: count WMI timeouts and length-discarded frames Nerijus Bendžiūnas
  0 siblings, 2 replies; 3+ messages in thread
From: Nerijus Bendžiūnas @ 2026-09-03 17:00 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, linux-wireless; +Cc: linux-kernel

On the USB devices every register read is a WMI round trip that can
time out, and ath9k_regread() reports a timeout as -1, indistinguishable
from an all-ones register. Two patches already on the list deal with
the reads that did the most damage: the spectral trigger no longer reads
the filter back, and a timed-out multi-read reports all ones like a
single read does. These two finish the job on the receive side.

Patch 1 stops ath9k_htc_calcrxfilter() reading AR_RX_FILTER back to
find out whether a spectral scan set the PHY error bits. The driver
knows that already, so it sets them from spec_priv, one round trip less
per filter recalculation, and a timed-out read can no longer switch PHY
error forwarding on by itself.

Patch 2 adds the two counters that were missing while all of this was
being tracked down on a stock kernel: WMI commands sent and timed out,
in a new debugfs file, and frames the receive path discards for their
length, in the LENGTH-ERR line that recv already prints for ath9k.


Nerijus Bendžiūnas (2):
  wifi: ath9k_htc: derive the PHY error filter bits from software state
  wifi: ath9k_htc: count WMI timeouts and length-discarded frames

 drivers/net/wireless/ath/ath9k/htc.h          |  2 ++
 .../net/wireless/ath/ath9k/htc_drv_debug.c    | 25 +++++++++++++++++++
 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 15 +++++------
 drivers/net/wireless/ath/ath9k/wmi.c          |  2 ++
 drivers/net/wireless/ath/ath9k/wmi.h          |  2 ++
 5 files changed, 39 insertions(+), 7 deletions(-)


base-commit: ca800a9302764c445de0da0e84d2252400a770ee
-- 
2.55.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH 1/2] wifi: ath9k_htc: derive the PHY error filter bits from software state
  2026-09-03 17:00 [PATCH 0/2] wifi: ath9k_htc: cope with lost WMI reads Nerijus Bendžiūnas
@ 2026-09-03 17:00 ` Nerijus Bendžiūnas
  2026-09-03 17:00 ` [PATCH 2/2] wifi: ath9k_htc: count WMI timeouts and length-discarded frames Nerijus Bendžiūnas
  1 sibling, 0 replies; 3+ messages in thread
From: Nerijus Bendžiūnas @ 2026-09-03 17:00 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, linux-wireless; +Cc: linux-kernel

ath9k_htc_calcrxfilter() reads AR_RX_FILTER back from the device to keep
the PHY error bits the spectral trigger may have set. On USB that read is
a WMI round trip, and when it times out ath9k_regread() returns -1, so
the recalculated filter turns PHY error forwarding on with no scan
running and the device streams every PHY error frame at the host until
the next recalculation. The driver already knows whether a scan is
active, so set the two bits from spec_priv instead, the way ath9k
derives its filter from software state. This drops one round trip from
every filter recalculation, and the bits now go away when the scan is
disabled rather than lingering until the next reset.

Signed-off-by: Nerijus Bendžiūnas <nerijus.bendziunas@gmail.com>
---
 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index bed7ea2425a0..299064a7fa49 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -868,14 +868,14 @@ int ath9k_htc_cabq_setup(struct ath9k_htc_priv *priv)
  */
 u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
 {
-#define	RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR)
-
 	struct ath_hw *ah = priv->ah;
 	u32 rfilt;
 
-	rfilt = (ath9k_hw_getrxfilter(ah) & RX_FILTER_PRESERVE)
-		| ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
-		| ATH9K_RX_FILTER_MCAST;
+	rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST |
+		ATH9K_RX_FILTER_MCAST;
+
+	if (priv->spec_priv.spectral_mode != SPECTRAL_DISABLED)
+		rfilt |= ATH9K_RX_FILTER_PHYRADAR | ATH9K_RX_FILTER_PHYERR;
 
 	if (priv->rxfilter & FIF_PROBE_REQ)
 		rfilt |= ATH9K_RX_FILTER_PROBEREQ;
@@ -906,8 +906,6 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv)
 		rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
 
 	return rfilt;
-
-#undef RX_FILTER_PRESERVE
 }
 
 /*
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] wifi: ath9k_htc: count WMI timeouts and length-discarded frames
  2026-09-03 17:00 [PATCH 0/2] wifi: ath9k_htc: cope with lost WMI reads Nerijus Bendžiūnas
  2026-09-03 17:00 ` [PATCH 1/2] wifi: ath9k_htc: derive the PHY error filter bits from software state Nerijus Bendžiūnas
@ 2026-09-03 17:00 ` Nerijus Bendžiūnas
  1 sibling, 0 replies; 3+ messages in thread
From: Nerijus Bendžiūnas @ 2026-09-03 17:00 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, linux-wireless; +Cc: linux-kernel

A WMI command that times out is invisible unless CONFIG_ATH_DEBUG is on
and the WMI debug bit is set, yet it is the event behind every register
read that came back as -1. Likewise the receive path discards short and
zero-length frames with nothing but a debug message, although a burst of
them is what strong interference looks like from the host. Keep a count
of commands sent and timed out in a new debugfs file, wmi, and count the
length discards in the existing LENGTH-ERR line of recv, so both can be
watched on a stock kernel.

Signed-off-by: Nerijus Bendžiūnas <nerijus.bendziunas@gmail.com>
---
 drivers/net/wireless/ath/ath9k/htc.h          |  2 ++
 .../net/wireless/ath/ath9k/htc_drv_debug.c    | 25 +++++++++++++++++++
 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c |  3 +++
 drivers/net/wireless/ath/ath9k/wmi.c          |  2 ++
 drivers/net/wireless/ath/ath9k/wmi.h          |  2 ++
 5 files changed, 34 insertions(+)

diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 6c33e898b300..0bdb300f0ad7 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -328,6 +328,7 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
 #ifdef CONFIG_ATH9K_HTC_DEBUGFS
 #define __STAT_SAFE(hif_dev, expr)	do { ((hif_dev)->htc_handle->drv_priv ? (expr) : 0); } while (0)
 #define CAB_STAT_INC(priv)		do { ((priv)->debug.tx_stats.cab_queued++); } while (0)
+#define RX_LEN_ERR_INC(priv)		((priv)->debug.rx_stats.rx_len_err++)
 #define TX_QSTAT_INC(priv, q)		do { ((priv)->debug.tx_stats.queue_stats[q]++); } while (0)
 
 #define TX_STAT_INC(hif_dev, c) \
@@ -383,6 +384,7 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
 #define RX_STAT_ADD(hif_dev, c, a)	do { } while (0)
 
 #define CAB_STAT_INC(priv)
+#define RX_LEN_ERR_INC(priv)
 #define TX_QSTAT_INC(priv, c)
 
 static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 9437d69877cc..f1bf8cc90d3a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -310,6 +310,29 @@ static const struct file_operations fops_slot = {
 	.llseek = default_llseek,
 };
 
+static ssize_t read_file_wmi(struct file *file, char __user *user_buf,
+			     size_t count, loff_t *ppos)
+{
+	struct ath9k_htc_priv *priv = file->private_data;
+	char buf[128];
+	unsigned int len;
+
+	len = scnprintf(buf, sizeof(buf),
+			"%20s : %10u\n"
+			"%20s : %10u\n",
+			"Commands", priv->wmi->cmds_sent,
+			"Timeouts", priv->wmi->cmds_timed_out);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_wmi = {
+	.read = read_file_wmi,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 static ssize_t read_file_queue(struct file *file, char __user *user_buf,
 			       size_t count, loff_t *ppos)
 {
@@ -505,6 +528,8 @@ int ath9k_htc_init_debug(struct ath_hw *ah)
 
 	debugfs_create_file("slot", 0400, priv->debug.debugfs_phy,
 			    priv, &fops_slot);
+	debugfs_create_file("wmi", 0400, priv->debug.debugfs_phy,
+			    priv, &fops_wmi);
 	debugfs_create_file("queue", 0400, priv->debug.debugfs_phy,
 			    priv, &fops_queue);
 	debugfs_create_file("debug", 0600, priv->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 299064a7fa49..aa4c8beb10d5 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -986,6 +986,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
 	if (skb->len < HTC_RX_FRAME_HEADER_SIZE) {
 		ath_err(common, "Corrupted RX frame, dropping (len: %d)\n",
 			skb->len);
+		RX_LEN_ERR_INC(priv);
 		goto rx_next;
 	}
 
@@ -997,6 +998,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
 		ath_err(common,
 			"Corrupted RX data len, dropping (dlen: %d, skblen: %d)\n",
 			rs_datalen, skb->len);
+		RX_LEN_ERR_INC(priv);
 		goto rx_next;
 	}
 
@@ -1009,6 +1011,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
 		ath_dbg(common, ANY,
 			"Short RX data len, dropping (dlen: %d)\n",
 			rs_datalen);
+		RX_LEN_ERR_INC(priv);
 		goto rx_next;
 	}
 
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index 284e8c13b043..d293f3adc214 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -345,8 +345,10 @@ int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
 	if (ret)
 		goto out;
 
+	wmi->cmds_sent++;
 	time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout);
 	if (!time_left) {
+		wmi->cmds_timed_out++;
 		ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n",
 			wmi_cmd_to_name(cmd_id));
 		spin_lock_irqsave(&wmi->wmi_lock, flags);
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 5c3b710b8f31..9dc5deffcdfe 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -158,6 +158,8 @@ struct wmi {
 	u8 *cmd_rsp_buf;
 	u32 cmd_rsp_len;
 	bool stopped;
+	u32 cmds_sent;
+	u32 cmds_timed_out;
 
 	struct list_head pending_tx_events;
 	spinlock_t event_lock;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-09-03 17:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03 17:00 [PATCH 0/2] wifi: ath9k_htc: cope with lost WMI reads Nerijus Bendžiūnas
2026-09-03 17:00 ` [PATCH 1/2] wifi: ath9k_htc: derive the PHY error filter bits from software state Nerijus Bendžiūnas
2026-09-03 17:00 ` [PATCH 2/2] wifi: ath9k_htc: count WMI timeouts and length-discarded frames Nerijus Bendžiūnas

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox