* Re: [PATCH wireless-next] wifi: cfg80211: don't apply HT flags to S1G channels
From: Johannes Berg @ 2026-01-20 7:50 UTC (permalink / raw)
To: Lachlan Hodges; +Cc: linux-wireless, arien.judge
In-Reply-To: <tyreg7kb5ownpgrbcwo72rk5tevk6jpzbthtlqr5z5zykvhaji@wpybm6oversy>
On Tue, 2026-01-20 at 16:22 +1100, Lachlan Hodges wrote:
> > > Playing around with some 5 GHz configurations in OpenWRT it appears that only
> > > so much information can be conveyed and indeed there are configurations that
> > > will be rejected by cfg80211 (or maybe hostapd in some cases, not sure).
> >
> > Really? Got an example, perhaps even with hostapd log to see how it's
> > rejected? That clearly contradicts what I wrote above which I really did
> > believe to be true until this moment ;-)
>
> Sorry, it is indeed cfg80211 that is not accepting the chandef configuration.
Maybe I still know something ;-)
> channel=165
>
> vht_oper_centr_freq_seg0_idx=171
> This is the result of selecting mode AX, channel 165 with a width of 80MHz.
Well, I guess that could be done in some countries, U-NII-4 goes up to
channel 177 inclusive? But not for Australia which only has up to 173
(according to Wikipedia, maybe it's changing, I didn't check better
sources now.)
> The GL-MT3000 using the mt76 driver with mt7915e driver (?)
>
> cfg80211 299008 4 mt7915e,mt76_connac_lib,mt76,mac80211
> compat 12288 3 mt76,mac80211,cfg80211
> mac80211 581632 3 mt7915e,mt76_connac_lib,mt76
> mt76 73728 2 mt7915e,mt76_connac_lib
> mt76_connac_lib 45056 1 mt7915e
>
> And for hostapd output (Sorry I don't have anything more verbose)
>
> Tue Jan 20 04:10:43 2026 daemon.err hostapd: nl80211: kernel reports: (extension) channel is disabled
Right, makes sense, it's at least saying channel 177 doesn't exist or
cannot be used, perhaps already on 169/173 depending on what the device
knows about U-NII-4.
> Tue Jan 20 04:10:43 2026 daemon.err hostapd: Could not set channel for kernel driver
> Tue Jan 20 04:10:43 2026 daemon.err hostapd: Interface initialization failed
>
> where _cfg80211_chandef_usable() is (rightfully) rejecting the configuration.
> This simply appears to be the logic to generate the config doesn't cover this
> case properly.
Right.
> Anyways, I don't think this is all that useful too be honest,
> it just comes down to ensuring the static configurations are known especially
> since this range is specified as AUTO-BW anyway as per the regdom.
Yeah, or checking against the regulatory, or something ... but basically
channel 165 + 80 MHz isn't _really_ possible then, since the
channelization plan says then you should use 171 as it does now.
> > > I
> > > suppose what I'm asking is - how much do we need to protect usermode from
> > > this? Do you feel it's worth including a flag that somewhat emulates
> > > NOHT40+/-? I know much of that logic is quite old and S1G is "modern" ... ?
> >
> > It's always nice to have userspace be aware of things, but I guess if
> > it's a configuration that's statically known to be invalid, it wouldn't
> > matter so much?
But I think in the case you describe above, userspace should well have
been able to, at least in theory, predict that this would happen, based
on the channel flags.
> > But I don't think I've understood the S1G angle - are
> > you talking about primary 1 MHz? Primary 2 MHz?
>
> Putting primaries aside and just thinking of operating - eventually we would
> need a NO_2MHZ flag as India for example only has 1MHz channels.. however
> this is well into the future .. we first need to add support for other
> countries :').
:)
> But yes as you said since these configurations are static it's not really a
> big deal and more up to userspace to be aware of it. I think what we have in
> cfg80211 now is good enough and seems to be working well when integrating
> into an actual userspace stack.
OK, sounds good.
> > We just posted a lot of hwsim changes for NAN - better send yours sooner
> > rather than later ;-)
>
> Yes I have seen, lots of Wi-Fi 8 documents aswell so I assume that will get busy
> soon too. Well see .. Lots to do!!
I don't think hwsim will get too many wifi8 changes, but yeah there are
a lot of things brewing now. Seems you (mostly?) got just ahead of it
though, so it shouldn't be much of an issue, I'd think.
johannes
^ permalink raw reply
* Re: [PATCH v2] wifi: ath11k: fix memory leaks in beacon template setup
From: Baochen Qiang @ 2026-01-20 7:19 UTC (permalink / raw)
To: Zilin Guan, jjohnson; +Cc: linux-wireless, ath11k, linux-kernel, jianhao.xu
In-Reply-To: <20260120063731.2383695-1-zilin@seu.edu.cn>
On 1/20/2026 2:37 PM, Zilin Guan wrote:
> The functions ath11k_mac_setup_bcn_tmpl_ema() and
> ath11k_mac_setup_bcn_tmpl_mbssid() allocate memory for beacon templates
> but fail to free it when parameter setup returns an error.
>
> Since beacon templates must be released during normal execution, they
> must also be released in the error handling paths to prevent memory
> leaks.
>
> Fix this by adding the missing deallocation calls in the respective
> error paths.
>
> Compile tested only. Issue found using a prototype static analysis tool
> and code review.
>
> Fixes: 3a415daa3e8b ("wifi: ath11k: add P2P IE in beacon template")
> Fixes: 335a92765d30 ("wifi: ath11k: MBSSID beacon support")
> Suggested-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
> Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
> ---
> Changes in v2:
> - Use unified exit paths for cleanup.
>
> drivers/net/wireless/ath/ath11k/mac.c | 25 +++++++++++++++----------
> 1 file changed, 15 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
> index 4dfd08b58416..42edcc5e9e49 100644
> --- a/drivers/net/wireless/ath/ath11k/mac.c
> +++ b/drivers/net/wireless/ath/ath11k/mac.c
> @@ -1561,8 +1561,10 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
> }
>
> if (tx_arvif == arvif) {
> - if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb))
> - return -EINVAL;
> + if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) {
> + ret = -EINVAL;
> + goto free;
> + }
> } else {
> arvif->wpaie_present = tx_arvif->wpaie_present;
> }
> @@ -1589,11 +1591,11 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
> }
> }
>
> - ieee80211_beacon_free_ema_list(beacons);
> -
> if (tx_arvif != arvif && !nontx_vif_params_set)
> - return -EINVAL; /* Profile not found in the beacons */
> + ret = -EINVAL; /* Profile not found in the beacons */
>
> +free:
> + ieee80211_beacon_free_ema_list(beacons);
> return ret;
> }
>
> @@ -1622,19 +1624,22 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
> }
>
> if (tx_arvif == arvif) {
> - if (ath11k_mac_set_vif_params(tx_arvif, bcn))
> - return -EINVAL;
> + if (ath11k_mac_set_vif_params(tx_arvif, bcn)) {
> + ret = -EINVAL;
> + goto free;
> + }
> } else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
> - return -EINVAL;
> + ret = -EINVAL;
> + goto free;
> }
>
> ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
> - kfree_skb(bcn);
> -
> if (ret)
> ath11k_warn(ab, "failed to submit beacon template command: %d\n",
> ret);
>
> +free:
> + kfree_skb(bcn);
> return ret;
> }
>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH 02/11] wifi: mt76: mt792x: fix NULL pointer and firmware reload issues
From: Greg KH @ 2026-01-20 7:04 UTC (permalink / raw)
To: Zac
Cc: sean.wang, deren.wu, kvalo, linux-kernel, linux-mediatek,
linux-wireless, lorenzo, nbd, ryder.lee, sean.wang, stable, linux,
zbowling
In-Reply-To: <20260120062854.126501-3-zac@zacbowling.com>
On Mon, Jan 19, 2026 at 10:28:45PM -0800, Zac wrote:
> From: Zac Bowling <zac@zacbowling.com>
>
> This patch combines two fixes for the shared mt792x code used by both
> MT7921 and MT7925 drivers:
>
> 1. Fix NULL pointer dereference in TX path:
>
> Add NULL pointer checks in mt792x_tx() to prevent kernel crashes when
> transmitting packets during MLO link removal.
>
> The function calls mt792x_sta_to_link() which can return NULL if the
> link is being removed, but the return value was dereferenced without
> checking. Similarly, the RCU-protected link_conf and link_sta pointers
> were used without NULL validation.
>
> This race can occur when:
> - A packet is queued for transmission
> - Concurrently, the link is being removed (mt7925_mac_link_sta_remove)
> - mt792x_sta_to_link() returns NULL for the removed link
> - Kernel crashes on wcid = &mlink->wcid dereference
>
> Fix by checking mlink, conf, and link_sta before use, freeing the SKB
> and returning early if any pointer is NULL.
>
> 2. Fix firmware reload failure after previous load crash:
>
> If the firmware loading process crashes or is interrupted after
> acquiring the patch semaphore but before releasing it, subsequent
> firmware load attempts will fail with 'Failed to get patch semaphore'.
>
> Apply the same fix from MT7915 (commit 79dd14f): release the patch
> semaphore before starting firmware load and restart MCU firmware to
> ensure clean state.
>
> Fixes: c74df1c067f2 ("wifi: mt76: mt792x: introduce mt792x-lib module")
> Fixes: 583204ae70f9 ("wifi: mt76: mt792x: move mt7921_load_firmware in mt792x-lib module")
> Link: https://github.com/openwrt/mt76/commit/79dd14f2e8161b656341b6653261779199aedbe4
> Signed-off-by: Zac Bowling <zac@zacbowling.com>
> ---
> .../net/wireless/mediatek/mt76/mt792x_core.c | 27 +++++++++++++++++--
> 1 file changed, 25 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
> index f2ed16feb6c1..05598202b488 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
> @@ -95,6 +95,8 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
> IEEE80211_TX_CTRL_MLO_LINK);
> sta = (struct mt792x_sta *)control->sta->drv_priv;
> mlink = mt792x_sta_to_link(sta, link_id);
> + if (!mlink)
> + goto free_skb;
> wcid = &mlink->wcid;
> }
>
> @@ -113,9 +115,12 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
> link_id = wcid->link_id;
> rcu_read_lock();
> conf = rcu_dereference(vif->link_conf[link_id]);
> - memcpy(hdr->addr2, conf->addr, ETH_ALEN);
> -
> link_sta = rcu_dereference(control->sta->link[link_id]);
> + if (!conf || !link_sta) {
> + rcu_read_unlock();
> + goto free_skb;
> + }
> + memcpy(hdr->addr2, conf->addr, ETH_ALEN);
> memcpy(hdr->addr1, link_sta->addr, ETH_ALEN);
>
> if (vif->type == NL80211_IFTYPE_STATION)
> @@ -136,6 +141,10 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
> }
>
> mt76_connac_pm_queue_skb(hw, &dev->pm, wcid, skb);
> + return;
> +
> +free_skb:
> + ieee80211_free_txskb(hw, skb);
> }
> EXPORT_SYMBOL_GPL(mt792x_tx);
>
> @@ -927,6 +936,20 @@ int mt792x_load_firmware(struct mt792x_dev *dev)
> {
> int ret;
>
> + /* Release semaphore if taken by previous failed load attempt.
> + * This prevents "Failed to get patch semaphore" errors when
> + * recovering from firmware crashes or suspend/resume failures.
> + */
> + ret = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false);
> + if (ret < 0)
> + dev_dbg(dev->mt76.dev, "Semaphore release returned %d (may be expected)\n", ret);
> +
> + /* Always restart MCU to ensure clean state before loading firmware */
> + mt76_connac_mcu_restart(&dev->mt76);
> +
> + /* Wait for MCU to be ready after restart */
> + msleep(100);
> +
> ret = mt76_connac2_load_patch(&dev->mt76, mt792x_patch_name(dev));
> if (ret)
> return ret;
> --
> 2.52.0
>
<formletter>
This is not the correct way to submit patches for inclusion in the
stable kernel tree. Please read:
https://www.kernel.org/doc/html/latest/process/stable-kernel-rules.html
for how to do this properly.
</formletter>
^ permalink raw reply
* [PATCH v2] wifi: ath11k: fix memory leaks in beacon template setup
From: Zilin Guan @ 2026-01-20 6:37 UTC (permalink / raw)
To: jjohnson
Cc: baochen.qiang, linux-wireless, ath11k, linux-kernel, jianhao.xu,
Zilin Guan
The functions ath11k_mac_setup_bcn_tmpl_ema() and
ath11k_mac_setup_bcn_tmpl_mbssid() allocate memory for beacon templates
but fail to free it when parameter setup returns an error.
Since beacon templates must be released during normal execution, they
must also be released in the error handling paths to prevent memory
leaks.
Fix this by adding the missing deallocation calls in the respective
error paths.
Compile tested only. Issue found using a prototype static analysis tool
and code review.
Fixes: 3a415daa3e8b ("wifi: ath11k: add P2P IE in beacon template")
Fixes: 335a92765d30 ("wifi: ath11k: MBSSID beacon support")
Suggested-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
---
Changes in v2:
- Use unified exit paths for cleanup.
drivers/net/wireless/ath/ath11k/mac.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 4dfd08b58416..42edcc5e9e49 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -1561,8 +1561,10 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
}
if (tx_arvif == arvif) {
- if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb))
- return -EINVAL;
+ if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) {
+ ret = -EINVAL;
+ goto free;
+ }
} else {
arvif->wpaie_present = tx_arvif->wpaie_present;
}
@@ -1589,11 +1591,11 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
}
}
- ieee80211_beacon_free_ema_list(beacons);
-
if (tx_arvif != arvif && !nontx_vif_params_set)
- return -EINVAL; /* Profile not found in the beacons */
+ ret = -EINVAL; /* Profile not found in the beacons */
+free:
+ ieee80211_beacon_free_ema_list(beacons);
return ret;
}
@@ -1622,19 +1624,22 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
}
if (tx_arvif == arvif) {
- if (ath11k_mac_set_vif_params(tx_arvif, bcn))
- return -EINVAL;
+ if (ath11k_mac_set_vif_params(tx_arvif, bcn)) {
+ ret = -EINVAL;
+ goto free;
+ }
} else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto free;
}
ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
- kfree_skb(bcn);
-
if (ret)
ath11k_warn(ab, "failed to submit beacon template command: %d\n",
ret);
+free:
+ kfree_skb(bcn);
return ret;
}
--
2.34.1
^ permalink raw reply related
* [PATCH 11/11] wifi: mt76: mt7925: fix ROC deadlocks and race conditions
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Fix multiple interrelated issues in the remain-on-channel (ROC) handling
that cause deadlocks, race conditions, and resource leaks.
Problems fixed:
1. Deadlock in sta removal ROC abort path:
When a station is removed while a ROC operation is in progress, the
driver would call mt7925_roc_abort_sync() which waits for ROC completion.
However, the ROC work itself needs to acquire mt792x_mutex which is
already held during station removal, causing a deadlock.
Fix: Use async ROC abort (mt76_connac_mcu_abort_roc) when called from
paths that already hold the mutex, and add MT76_STATE_ROC_ABORT flag
to coordinate between the abort and the ROC timer.
2. ROC timer race during suspend:
The ROC timer could fire after the device started suspending but before
the ROC was properly aborted, causing undefined behavior.
Fix: Delete ROC timer synchronously before suspend and check device
state before processing ROC timeout.
3. ROC rate limiting for MLO auth failures:
Rapid ROC requests during MLO authentication can overwhelm the firmware,
causing authentication timeouts. The MT7925 firmware has limited ROC
handling capacity.
Fix: Add rate limiting infrastructure with configurable minimum interval
between ROC requests. Track last ROC completion time and defer new
requests if they arrive too quickly.
4. WCID leak in ROC cleanup:
When ROC operations are aborted, the associated WCID resources were
not being properly released, causing resource exhaustion over time.
Fix: Ensure WCID cleanup happens in all ROC termination paths.
5. Async ROC abort race condition:
The async ROC abort could race with normal ROC completion, causing
double-free or use-after-free of ROC resources.
Fix: Use MT76_STATE_ROC_ABORT flag and proper synchronization to
prevent races between async abort and normal completion paths.
These fixes work together to provide robust ROC handling that doesn't
deadlock, properly releases resources, and handles edge cases during
suspend and MLO operations.
Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
.../net/wireless/mediatek/mt76/mt7925/main.c | 175 ++++++++++++++++--
drivers/net/wireless/mediatek/mt76/mt792x.h | 7 +
3 files changed, 170 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index d05e83ea1cac..91f9dd95c89e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -511,6 +511,7 @@ enum {
MT76_STATE_POWER_OFF,
MT76_STATE_SUSPEND,
MT76_STATE_ROC,
+ MT76_STATE_ROC_ABORT,
MT76_STATE_PM,
MT76_STATE_WED_RESET,
};
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index cc7ef2c17032..2404f7812897 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -453,6 +453,24 @@ static void mt7925_roc_iter(void *priv, u8 *mac,
mt7925_mcu_abort_roc(phy, &mvif->bss_conf, phy->roc_token_id);
}
+/* Async ROC abort - safe to call while holding mutex.
+ * Sets abort flag and lets roc_work handle cleanup without blocking.
+ * This prevents deadlock when called from sta_remove path which holds mutex.
+ */
+static void mt7925_roc_abort_async(struct mt792x_dev *dev)
+{
+ struct mt792x_phy *phy = &dev->phy;
+
+ /* Set abort flag - roc_work checks this before acquiring mutex */
+ set_bit(MT76_STATE_ROC_ABORT, &phy->mt76->state);
+
+ /* Stop timer and schedule work to handle cleanup.
+ * Must schedule work since timer may not have fired yet.
+ */
+ timer_delete(&phy->roc_timer);
+ ieee80211_queue_work(phy->mt76->hw, &phy->roc_work);
+}
+
void mt7925_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
@@ -473,6 +491,17 @@ void mt7925_roc_work(struct work_struct *work)
phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy,
roc_work);
+ /* Check abort flag BEFORE acquiring mutex to prevent deadlock.
+ * If abort is requested while we're in the sta_remove path (which
+ * holds the mutex), we must not try to acquire it or we'll deadlock.
+ * Clear the flags and only notify mac80211 if ROC was actually active.
+ */
+ if (test_and_clear_bit(MT76_STATE_ROC_ABORT, &phy->mt76->state)) {
+ if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+ ieee80211_remain_on_channel_expired(phy->mt76->hw);
+ return;
+ }
+
if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
return;
@@ -500,14 +529,93 @@ static int mt7925_abort_roc(struct mt792x_phy *phy,
return err;
}
+/* ROC rate limiting constants - exponential backoff to prevent MCU overload
+ * when upper layers trigger rapid reconnection cycles (e.g., MLO auth failures).
+ * Max backoff ~1.6s, resets after 10s of no timeouts.
+ */
+#define MT7925_ROC_BACKOFF_BASE_MS 100
+#define MT7925_ROC_BACKOFF_MAX_MS 1600
+#define MT7925_ROC_TIMEOUT_RESET_MS 10000
+#define MT7925_ROC_TIMEOUT_WARN_THRESH 5
+
+/* Check if ROC should be throttled due to recent timeouts.
+ * Returns delay in jiffies if throttling, 0 if OK to proceed.
+ */
+static unsigned long mt7925_roc_throttle_check(struct mt792x_phy *phy)
+{
+ unsigned long now = jiffies;
+
+ /* Reset timeout counter if it's been a while since last timeout */
+ if (phy->roc_timeout_count &&
+ time_after(now, phy->roc_last_timeout +
+ msecs_to_jiffies(MT7925_ROC_TIMEOUT_RESET_MS))) {
+ phy->roc_timeout_count = 0;
+ phy->roc_backoff_until = 0;
+ }
+
+ /* Check if we're still in backoff period */
+ if (phy->roc_backoff_until && time_before(now, phy->roc_backoff_until))
+ return phy->roc_backoff_until - now;
+
+ return 0;
+}
+
+/* Record ROC timeout and calculate backoff period */
+static void mt7925_roc_record_timeout(struct mt792x_phy *phy)
+{
+ unsigned int backoff_ms;
+
+ phy->roc_last_timeout = jiffies;
+ phy->roc_timeout_count++;
+
+ /* Exponential backoff: 100ms, 200ms, 400ms, 800ms, 1600ms (capped) */
+ backoff_ms = MT7925_ROC_BACKOFF_BASE_MS <<
+ min_t(u8, phy->roc_timeout_count - 1, 4);
+ if (backoff_ms > MT7925_ROC_BACKOFF_MAX_MS)
+ backoff_ms = MT7925_ROC_BACKOFF_MAX_MS;
+
+ phy->roc_backoff_until = jiffies + msecs_to_jiffies(backoff_ms);
+
+ /* Warn if we're seeing repeated timeouts - likely upper layer issue */
+ if (phy->roc_timeout_count == MT7925_ROC_TIMEOUT_WARN_THRESH)
+ dev_warn(phy->dev->mt76.dev,
+ "mt7925: %u consecutive ROC timeouts, possible mac80211/wpa_supplicant issue (MLO key race?)\n",
+ phy->roc_timeout_count);
+}
+
+/* Clear timeout tracking on successful ROC */
+static void mt7925_roc_clear_timeout(struct mt792x_phy *phy)
+{
+ phy->roc_timeout_count = 0;
+ phy->roc_backoff_until = 0;
+}
+
static int mt7925_set_roc(struct mt792x_phy *phy,
struct mt792x_bss_conf *mconf,
struct ieee80211_channel *chan,
int duration,
enum mt7925_roc_req type)
{
+ unsigned long throttle;
int err;
+ /* Check rate limiting - if in backoff period, wait or return busy */
+ throttle = mt7925_roc_throttle_check(phy);
+ if (throttle) {
+ /* For short backoffs, wait; for longer ones, return busy */
+ if (throttle < msecs_to_jiffies(200)) {
+ msleep(jiffies_to_msecs(throttle));
+ } else {
+ dev_dbg(phy->dev->mt76.dev,
+ "mt7925: ROC throttled, %lu ms remaining\n",
+ jiffies_to_msecs(throttle));
+ return -EBUSY;
+ }
+ }
+
+ /* Clear stale abort flag from previous ROC */
+ clear_bit(MT76_STATE_ROC_ABORT, &phy->mt76->state);
+
if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state))
return -EBUSY;
@@ -523,7 +631,11 @@ static int mt7925_set_roc(struct mt792x_phy *phy,
if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, 4 * HZ)) {
mt7925_mcu_abort_roc(phy, mconf, phy->roc_token_id);
clear_bit(MT76_STATE_ROC, &phy->mt76->state);
+ mt7925_roc_record_timeout(phy);
err = -ETIMEDOUT;
+ } else {
+ /* Successful ROC - reset timeout tracking */
+ mt7925_roc_clear_timeout(phy);
}
out:
@@ -534,8 +646,27 @@ static int mt7925_set_mlo_roc(struct mt792x_phy *phy,
struct mt792x_bss_conf *mconf,
u16 sel_links)
{
+ unsigned long throttle;
int err;
+ /* Check rate limiting - MLO ROC is especially prone to rapid-fire
+ * during reconnection cycles after MLO authentication failures.
+ */
+ throttle = mt7925_roc_throttle_check(phy);
+ if (throttle) {
+ if (throttle < msecs_to_jiffies(200)) {
+ msleep(jiffies_to_msecs(throttle));
+ } else {
+ dev_dbg(phy->dev->mt76.dev,
+ "mt7925: MLO ROC throttled, %lu ms remaining\n",
+ jiffies_to_msecs(throttle));
+ return -EBUSY;
+ }
+ }
+
+ /* Clear stale abort flag from previous ROC */
+ clear_bit(MT76_STATE_ROC_ABORT, &phy->mt76->state);
+
if (WARN_ON_ONCE(test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state)))
return -EBUSY;
@@ -550,7 +681,10 @@ static int mt7925_set_mlo_roc(struct mt792x_phy *phy,
if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, 4 * HZ)) {
mt7925_mcu_abort_roc(phy, mconf, phy->roc_token_id);
clear_bit(MT76_STATE_ROC, &phy->mt76->state);
+ mt7925_roc_record_timeout(phy);
err = -ETIMEDOUT;
+ } else {
+ mt7925_roc_clear_timeout(phy);
}
out:
@@ -567,6 +701,7 @@ static int mt7925_remain_on_channel(struct ieee80211_hw *hw,
struct mt792x_phy *phy = mt792x_hw_phy(hw);
int err;
+ cancel_work_sync(&phy->roc_work);
mt792x_mutex_acquire(phy->dev);
err = mt7925_set_roc(phy, &mvif->bss_conf,
chan, duration, MT7925_ROC_REQ_ROC);
@@ -874,14 +1009,14 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
if (!mlink)
return -EINVAL;
- idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
- if (idx < 0)
- return -ENOSPC;
-
mconf = mt792x_vif_to_link(mvif, link_id);
if (!mconf)
return -EINVAL;
+ idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
+ if (idx < 0)
+ return -ENOSPC;
+
mt76_wcid_init(&mlink->wcid, 0);
mlink->wcid.sta = 1;
mlink->wcid.idx = idx;
@@ -901,14 +1036,16 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm);
if (ret)
- return ret;
+ goto err_wcid;
mt7925_mac_wtbl_update(dev, idx,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
link_conf = mt792x_vif_to_bss_conf(vif, link_id);
- if (!link_conf)
- return -EINVAL;
+ if (!link_conf) {
+ ret = -EINVAL;
+ goto err_wcid;
+ }
/* should update bss info before STA add */
if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
@@ -920,7 +1057,7 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
ret = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
link_conf, link_sta, false);
if (ret)
- return ret;
+ goto err_wcid;
}
if (ieee80211_vif_is_mld(vif) &&
@@ -928,28 +1065,34 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
MT76_STA_INFO_STATE_NONE);
if (ret)
- return ret;
+ goto err_wcid;
} else if (ieee80211_vif_is_mld(vif) &&
link_sta != mlink->pri_link) {
ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif,
true, MT76_STA_INFO_STATE_ASSOC);
if (ret)
- return ret;
+ goto err_wcid;
ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
MT76_STA_INFO_STATE_ASSOC);
if (ret)
- return ret;
+ goto err_wcid;
} else {
ret = mt7925_mcu_sta_update(dev, link_sta, vif, true,
MT76_STA_INFO_STATE_NONE);
if (ret)
- return ret;
+ goto err_wcid;
}
mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
return 0;
+
+err_wcid:
+ rcu_assign_pointer(dev->mt76.wcid[idx], NULL);
+ mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx);
+ mt76_connac_power_save_sched(&dev->mphy, &dev->pm);
+ return ret;
}
static int
@@ -1135,7 +1278,8 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
if (!mlink)
return;
- mt7925_roc_abort_sync(dev);
+ /* Async abort - caller already holds mutex */
+ mt7925_roc_abort_async(dev);
mt76_connac_free_pending_tx_skbs(&dev->pm, &mlink->wcid);
mt76_connac_pm_wake(&dev->mphy, &dev->pm);
@@ -1530,6 +1674,8 @@ static int mt7925_suspend(struct ieee80211_hw *hw,
cancel_delayed_work_sync(&dev->pm.ps_work);
mt76_connac_free_pending_tx_skbs(&dev->pm, NULL);
+ /* Cancel ROC before quiescing starts */
+ mt7925_roc_abort_sync(dev);
mt792x_mutex_acquire(dev);
clear_bit(MT76_STATE_RUNNING, &phy->mt76->state);
@@ -1876,6 +2022,8 @@ static void mt7925_mgd_prepare_tx(struct ieee80211_hw *hw,
u16 duration = info->duration ? info->duration :
jiffies_to_msecs(HZ);
+ cancel_work_sync(&mvif->phy->roc_work);
+
mt792x_mutex_acquire(dev);
mt7925_set_roc(mvif->phy, &mvif->bss_conf,
mvif->bss_conf.mt76.ctx->def.chan, duration,
@@ -2033,6 +2181,7 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
if (old_links == new_links)
return 0;
+ cancel_work_sync(&phy->roc_work);
mt792x_mutex_acquire(dev);
for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) {
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 8388638ed550..d9c1ea709390 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -186,6 +186,13 @@ struct mt792x_phy {
wait_queue_head_t roc_wait;
u8 roc_token_id;
bool roc_grant;
+
+ /* ROC rate limiting to prevent MCU overload during rapid reconnection
+ * cycles (e.g., MLO authentication failures causing repeated ROC).
+ */
+ u8 roc_timeout_count; /* consecutive ROC timeouts */
+ unsigned long roc_last_timeout; /* jiffies of last timeout */
+ unsigned long roc_backoff_until;/* don't issue ROC until this time */
};
struct mt792x_irq_map {
--
2.52.0
^ permalink raw reply related
* [PATCH 10/11] wifi: mt76: mt7925: fix BA session teardown during beacon loss
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
The ieee80211_stop_tx_ba_cb_irqsafe() callback was conditionally called
only when the MCU command succeeded. However, during beacon connection
loss, the MCU command may fail because the AP is no longer reachable.
If the callback is not called, mac80211's BA session state machine gets
stuck in an intermediate state. When mac80211 later tries to tear down
all BA sessions during disconnection, it hits a WARN in
__ieee80211_stop_tx_ba_session() due to the inconsistent state.
Fix by making the callback unconditional, matching the behavior of
mt7921 and mt7996 drivers. The MCU command failure is acceptable during
disconnection - what matters is that mac80211 is notified to complete
the session teardown.
Reported-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 81373e479abd..cc7ef2c17032 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1323,9 +1323,13 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_TX_STOP_CONT:
mtxq->aggr = false;
clear_bit(tid, &msta->deflink.wcid.ampdu_state);
- ret = mt7925_mcu_uni_tx_ba(dev, params, false);
- if (!ret)
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ /* MCU command may fail during beacon loss, but callback must
+ * always be called to complete the BA session teardown in
+ * mac80211. Otherwise the state machine gets stuck and triggers
+ * WARN in __ieee80211_stop_tx_ba_session().
+ */
+ mt7925_mcu_uni_tx_ba(dev, params, false);
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
}
mt792x_mutex_release(dev);
--
2.52.0
^ permalink raw reply related
* [PATCH 09/11] wifi: mt76: mt7925: fix MLO roaming and ROC setup issues
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Fix two issues related to MLO roaming and remain-on-channel operations:
1. Key removal failure during MLO roaming:
During MLO roaming, key removal can fail because the WCID (wireless client
ID) is already cleaned up before the key removal operation completes.
When roaming between APs in an MLO setup:
- mac80211 triggers sta_state changes
- mt7925_mac_link_sta_remove() is called for the old link
- WCID is cleared via mt76_wcid_cleanup()
- Later, key removal MCU command uses the now-invalid WCID
Fix by checking if the WCID is still valid before sending key removal
commands to firmware. If the WCID has already been cleaned up, skip
the MCU command since the firmware has already removed the keys.
2. Kernel warning in MLO ROC setup:
When starting a remain-on-channel operation in MLO mode, the driver
passes incorrect parameters to mt7925_mcu_set_roc(), causing a kernel
warning about invalid chanctx usage.
Fix by checking for valid chanctx and link configuration before
setting up ROC, and use the correct link_id from the vif when
available.
Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 9 ++++++++-
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 20 +++++++++++++------
2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 5f8a28d5ff72..81373e479abd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -605,8 +605,15 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
mconf = mt792x_vif_to_link(mvif, link_id);
mlink = mt792x_sta_to_link(msta, link_id);
- if (!link_conf || !mconf || !mlink)
+ if (!link_conf || !mconf || !mlink) {
+ /* During MLO roaming, link state may be torn down before
+ * mac80211 requests key removal. If removing a key and
+ * the link is already gone, consider it successfully removed.
+ */
+ if (cmd != SET_KEY)
+ return 0;
return -EINVAL;
+ }
wcid = &mlink->wcid;
wcid_keyidx = &wcid->hw_key_idx;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 1c58b0be2be4..6f7fc1b9a440 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1342,15 +1342,23 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links,
for (i = 0; i < ARRAY_SIZE(links); i++) {
links[i].id = i ? __ffs(~BIT(mconf->link_id) & sel_links) :
mconf->link_id;
+
link_conf = mt792x_vif_to_bss_conf(vif, links[i].id);
- if (WARN_ON_ONCE(!link_conf))
- return -EPERM;
+ if (!link_conf)
+ return -ENOLINK;
links[i].chan = link_conf->chanreq.oper.chan;
- if (WARN_ON_ONCE(!links[i].chan))
- return -EPERM;
+ if (!links[i].chan)
+ /* Channel not configured yet - this can happen during
+ * MLO AP setup when links are being added sequentially.
+ * Return -ENOLINK to indicate link not ready.
+ */
+ return -ENOLINK;
links[i].mconf = mt792x_vif_to_link(mvif, links[i].id);
+ if (!links[i].mconf)
+ return -ENOLINK;
+
links[i].tag = links[i].id == mconf->link_id ?
UNI_ROC_ACQUIRE : UNI_ROC_SUB_LINK;
@@ -1364,8 +1372,8 @@ int mt7925_mcu_set_mlo_roc(struct mt792x_bss_conf *mconf, u16 sel_links,
type = MT7925_ROC_REQ_JOIN;
for (i = 0; i < ARRAY_SIZE(links) && i < hweight16(vif->active_links); i++) {
- if (WARN_ON_ONCE(!links[i].mconf || !links[i].chan))
- continue;
+ if (!links[i].mconf || !links[i].chan)
+ return -ENOLINK;
chan = links[i].chan;
center_ch = ieee80211_frequency_to_channel(chan->center_freq);
--
2.52.0
^ permalink raw reply related
* [PATCH 08/11] wifi: mt76: mt7925: add lockdep assertions for mutex verification
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Add lockdep_assert_held() calls to critical MCU functions to help catch
mutex violations during development and debugging. This follows the
pattern used in other mt76 drivers (mt7996, mt7915, mt7615).
Functions with new assertions:
- mt7925_mcu_add_bss_info(): Core BSS configuration MCU command
- mt7925_mcu_sta_update(): Station record update MCU command
- mt7925_mcu_uni_bss_ps(): Power save state MCU command
These functions modify firmware state and must be called with the
device mutex held to prevent race conditions. The lockdep assertions
will trigger warnings at runtime if code paths exist that call these
functions without proper mutex protection.
This aids in detecting the class of bugs fixed by patches in this series.
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 94ec62a4538a..1c58b0be2be4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1532,6 +1532,8 @@ int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev,
},
};
+ lockdep_assert_held(&dev->mt76.mutex);
+
if (link_conf->vif->type != NL80211_IFTYPE_STATION)
return -EOPNOTSUPP;
@@ -2047,6 +2049,8 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
struct mt792x_sta *msta;
struct mt792x_link_sta *mlink = NULL;
+ lockdep_assert_held(&dev->mt76.mutex);
+
if (link_sta) {
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
@@ -2853,6 +2857,8 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy,
struct mt792x_link_sta *mlink_bc;
struct sk_buff *skb;
+ lockdep_assert_held(&dev->mt76.mutex);
+
skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76,
MT7925_BSS_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
--
2.52.0
^ permalink raw reply related
* [PATCH 07/11] wifi: mt76: mt7925: add MCU command error handling
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Add proper error handling for MCU command return values that were
previously being ignored. Without proper error handling, failures in
MCU communication can leave the driver in an inconsistent state.
Functions updated:
1. main.c: mt7925_ampdu_action() - BA session setup
- Check mt7925_mcu_uni_tx_ba() return value
- Check mt7925_mcu_uni_rx_ba() return value
- Return error to mac80211 on failure
2. main.c: mt7925_mac_link_sta_add() - Station addition
- Check mt7925_mcu_add_bss_info() return value
- Propagate errors during station setup
3. main.c: mt7925_set_key() - Key installation
- Check mt7925_mcu_add_bss_info() return value when setting
BSS info before key installation
- Prevent key setup on communication failure
These changes ensure that MCU communication failures are properly
detected and reported to mac80211, allowing proper error recovery
instead of leaving the driver in an undefined state.
Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
.../net/wireless/mediatek/mt76/mt7925/main.c | 30 +++++++++++--------
1 file changed, 18 insertions(+), 12 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 80ca5181150b..5f8a28d5ff72 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -637,8 +637,10 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
struct mt792x_phy *phy = mt792x_hw_phy(hw);
mconf->mt76.cipher = mt7925_mcu_get_cipher(key->cipher);
- mt7925_mcu_add_bss_info(phy, mconf->mt76.ctx, link_conf,
- link_sta, true);
+ err = mt7925_mcu_add_bss_info(phy, mconf->mt76.ctx, link_conf,
+ link_sta, true);
+ if (err)
+ goto out;
}
if (cmd == SET_KEY)
@@ -904,11 +906,14 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
/* should update bss info before STA add */
if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
if (ieee80211_vif_is_mld(vif))
- mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
- link_conf, link_sta, link_sta != mlink->pri_link);
+ ret = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+ link_conf, link_sta,
+ link_sta != mlink->pri_link);
else
- mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
- link_conf, link_sta, false);
+ ret = mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+ link_conf, link_sta, false);
+ if (ret)
+ return ret;
}
if (ieee80211_vif_is_mld(vif) &&
@@ -1287,22 +1292,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_RX_START:
mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn,
params->buf_size);
- mt7925_mcu_uni_rx_ba(dev, params, true);
+ ret = mt7925_mcu_uni_rx_ba(dev, params, true);
break;
case IEEE80211_AMPDU_RX_STOP:
mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid);
- mt7925_mcu_uni_rx_ba(dev, params, false);
+ ret = mt7925_mcu_uni_rx_ba(dev, params, false);
break;
case IEEE80211_AMPDU_TX_OPERATIONAL:
mtxq->aggr = true;
mtxq->send_bar = false;
- mt7925_mcu_uni_tx_ba(dev, params, true);
+ ret = mt7925_mcu_uni_tx_ba(dev, params, true);
break;
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
mtxq->aggr = false;
clear_bit(tid, &msta->deflink.wcid.ampdu_state);
- mt7925_mcu_uni_tx_ba(dev, params, false);
+ ret = mt7925_mcu_uni_tx_ba(dev, params, false);
break;
case IEEE80211_AMPDU_TX_START:
set_bit(tid, &msta->deflink.wcid.ampdu_state);
@@ -1311,8 +1316,9 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_TX_STOP_CONT:
mtxq->aggr = false;
clear_bit(tid, &msta->deflink.wcid.ampdu_state);
- mt7925_mcu_uni_tx_ba(dev, params, false);
- ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ ret = mt7925_mcu_uni_tx_ba(dev, params, false);
+ if (!ret)
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break;
}
mt792x_mutex_release(dev);
--
2.52.0
^ permalink raw reply related
* [PATCH 06/11] wifi: mt76: mt7925: add mutex protection in critical paths
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Add proper mutex protection for mt7925 driver operations that access
hardware state without proper synchronization. This fixes race conditions
that can cause system instability during power management and recovery.
Fixes added:
1. mac.c: mt7925_mac_reset_work()
- Wrap ieee80211_iterate_active_interfaces() with mt792x_mutex
- The vif_connect_iter callback accesses hardware state
2. mac.c: mt7925_mac_sta_assoc()
- Wrap vif_connect_iter call with mutex protection
- Called during station association which races with PM
3. main.c: mt7925_set_runtime_pm()
- Add mutex protection around mt76_connac_pm_wake/sleep
- Runtime PM can race with other operations
4. main.c: mt7925_set_mlo_pm()
- Add mutex protection around MLO PM configuration
- Prevents races during MLO link setup/teardown
5. pci.c: mt7925_pci_resume()
- Add mutex protection around ieee80211_iterate_active_interfaces
- The vif iteration accesses hardware state that needs synchronization
These protections ensure consistent hardware state access during power
management transitions and recovery operations.
Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 2 ++
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 6 ++++--
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 ++++
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index 184efe8afa10..06420ac6ed55 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1331,9 +1331,11 @@ void mt7925_mac_reset_work(struct work_struct *work)
dev->hw_full_reset = false;
pm->suspended = false;
ieee80211_wake_queues(hw);
+ mt792x_mutex_acquire(dev);
ieee80211_iterate_active_interfaces(hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7925_vif_connect_iter, NULL);
+ mt792x_mutex_release(dev);
mt76_connac_power_save_sched(&dev->mt76.phy, pm);
mt7925_regd_change(&dev->phy, "00");
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 15d1b1b8d9f8..80ca5181150b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -755,9 +755,11 @@ void mt7925_set_runtime_pm(struct mt792x_dev *dev)
bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
pm->enable = pm->enable_user && !monitor;
+ mt792x_mutex_acquire(dev);
ieee80211_iterate_active_interfaces(hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7925_pm_interface_iter, dev);
+ mt792x_mutex_release(dev);
pm->ds_enable = pm->ds_enable_user && !monitor;
mt7925_mcu_set_deep_sleep(dev, pm->ds_enable);
}
@@ -1331,14 +1333,12 @@ mt7925_mlo_pm_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
if (mvif->mlo_pm_state != MT792x_MLO_CHANGED_PS)
return;
- mt792x_mutex_acquire(dev);
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
bss_conf = mt792x_vif_to_bss_conf(vif, i);
if (!bss_conf)
continue;
mt7925_mcu_uni_bss_ps(dev, bss_conf);
}
- mt792x_mutex_release(dev);
}
void mt7925_mlo_pm_work(struct work_struct *work)
@@ -1347,9 +1347,11 @@ void mt7925_mlo_pm_work(struct work_struct *work)
mlo_pm_work.work);
struct ieee80211_hw *hw = mt76_hw(dev);
+ mt792x_mutex_acquire(dev);
ieee80211_iterate_active_interfaces(hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7925_mlo_pm_iter, dev);
+ mt792x_mutex_release(dev);
}
void mt7925_scan_work(struct work_struct *work)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index c4161754c01d..3a9e32a1759d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -455,7 +455,9 @@ static int mt7925_pci_suspend(struct device *device)
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
+ mt792x_mutex_acquire(dev);
mt7925_roc_abort_sync(dev);
+ mt792x_mutex_release(dev);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
@@ -582,10 +584,12 @@ static int _mt7925_pci_resume(struct device *device, bool restore)
}
/* restore previous ds setting */
+ mt792x_mutex_acquire(dev);
if (!pm->ds_enable)
mt7925_mcu_set_deep_sleep(dev, false);
mt7925_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
+ mt792x_mutex_release(dev);
failed:
pm->suspended = false;
--
2.52.0
^ permalink raw reply related
* [PATCH 05/11] wifi: mt76: mt7925: add comprehensive NULL pointer protection for MLO
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Add NULL pointer checks for functions that return pointers to link-related
structures throughout the mt7925 driver. During MLO state transitions,
these functions can return NULL when link configuration is not synchronized.
Functions protected:
- mt792x_vif_to_bss_conf(): Returns link BSS configuration
- mt792x_vif_to_link(): Returns driver link state
- mt792x_sta_to_link(): Returns station link state
Files updated:
1. mac.c:
- mt7925_vif_connect_iter(): Check bss_conf before use
- mt7925_mac_sta_assoc(): Check bss_conf before use
2. main.c:
- mt7925_set_key(): Check link_conf and mlink
- mt7925_mac_link_sta_add(): Check link_conf and mlink
- mt7925_mac_link_sta_assoc(): Check bss_conf and mlink
- mt7925_mac_link_sta_remove(): Check bss_conf and mlink
- mt7925_change_vif_links(): Check conf before use
- mt7925_assign_vif_chanctx(): Check mconf and mlink
- mt7925_unassign_vif_chanctx(): Check mconf and mlink
- mt7925_mgd_prepare_tx(): Check link_conf
3. mcu.c:
- mt7925_mcu_sta_phy_tlv(): Check link_sta
- mt7925_mcu_sta_amsdu_tlv(): Check link_sta
- mt7925_mcu_sta_mld_tlv(): Check link_sta
- mt7925_mcu_sta_cmd(): Check mlink
- mt7925_mcu_add_bss_info(): Check link_conf
- mt7925_mcu_set_chctx(): Check link_conf and mlink
Prevents crashes during:
- BSSID roaming transitions
- MLO setup and teardown
- Hardware reset operations
- Runtime power management
Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 device")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
.../net/wireless/mediatek/mt76/mt7925/mac.c | 6 ++
.../net/wireless/mediatek/mt76/mt7925/main.c | 82 ++++++++++++++++---
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 22 ++++-
3 files changed, 97 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
index 871b67101976..184efe8afa10 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c
@@ -1271,6 +1271,12 @@ mt7925_vif_connect_iter(void *priv, u8 *mac,
bss_conf = mt792x_vif_to_bss_conf(vif, i);
mconf = mt792x_vif_to_link(mvif, i);
+ /* Skip links that don't have bss_conf set up yet in mac80211.
+ * This can happen during HW reset when link state is inconsistent.
+ */
+ if (!bss_conf)
+ continue;
+
mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76,
&mvif->sta.deflink.wcid, true);
mt7925_mcu_set_tx(dev, bss_conf);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 2d358a96640c..15d1b1b8d9f8 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -604,6 +604,10 @@ static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
link_sta = sta ? mt792x_sta_to_link_sta(vif, sta, link_id) : NULL;
mconf = mt792x_vif_to_link(mvif, link_id);
mlink = mt792x_sta_to_link(msta, link_id);
+
+ if (!link_conf || !mconf || !mlink)
+ return -EINVAL;
+
wcid = &mlink->wcid;
wcid_keyidx = &wcid->hw_key_idx;
@@ -856,12 +860,17 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_id);
+ if (!mlink)
+ return -EINVAL;
idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1);
if (idx < 0)
return -ENOSPC;
mconf = mt792x_vif_to_link(mvif, link_id);
+ if (!mconf)
+ return -EINVAL;
+
mt76_wcid_init(&mlink->wcid, 0);
mlink->wcid.sta = 1;
mlink->wcid.idx = idx;
@@ -887,6 +896,8 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev,
MT_WTBL_UPDATE_ADM_COUNT_CLEAR);
link_conf = mt792x_vif_to_bss_conf(vif, link_id);
+ if (!link_conf)
+ return -EINVAL;
/* should update bss info before STA add */
if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
@@ -993,18 +1004,29 @@ mt7925_mac_set_links(struct mt76_dev *mdev, struct ieee80211_vif *vif)
{
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
- struct ieee80211_bss_conf *link_conf =
- mt792x_vif_to_bss_conf(vif, mvif->deflink_id);
- struct cfg80211_chan_def *chandef = &link_conf->chanreq.oper;
- enum nl80211_band band = chandef->chan->band, secondary_band;
+ struct ieee80211_bss_conf *link_conf;
+ struct cfg80211_chan_def *chandef;
+ enum nl80211_band band, secondary_band;
+ u16 sel_links;
+ u8 secondary_link_id;
- u16 sel_links = mt76_select_links(vif, 2);
- u8 secondary_link_id = __ffs(~BIT(mvif->deflink_id) & sel_links);
+ link_conf = mt792x_vif_to_bss_conf(vif, mvif->deflink_id);
+ if (!link_conf)
+ return;
+
+ chandef = &link_conf->chanreq.oper;
+ band = chandef->chan->band;
+
+ sel_links = mt76_select_links(vif, 2);
+ secondary_link_id = __ffs(~BIT(mvif->deflink_id) & sel_links);
if (!ieee80211_vif_is_mld(vif) || hweight16(sel_links) < 2)
return;
link_conf = mt792x_vif_to_bss_conf(vif, secondary_link_id);
+ if (!link_conf)
+ return;
+
secondary_band = link_conf->chanreq.oper.chan->band;
if (band == NL80211_BAND_2GHZ ||
@@ -1032,6 +1054,8 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
+ if (!mlink)
+ return;
mt792x_mutex_acquire(dev);
@@ -1041,12 +1065,13 @@ static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev,
link_conf = mt792x_vif_to_bss_conf(vif, vif->bss_conf.link_id);
}
- if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
+ if (link_conf && vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
struct mt792x_bss_conf *mconf;
mconf = mt792x_link_conf_to_mconf(link_conf);
- mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
- link_conf, link_sta, true);
+ if (mconf)
+ mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx,
+ link_conf, link_sta, true);
}
ewma_avg_signal_init(&mlink->avg_ack_signal);
@@ -1093,6 +1118,8 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_id);
+ if (!mlink)
+ return;
mt7925_roc_abort_sync(dev);
@@ -1106,10 +1133,12 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
link_conf = mt792x_vif_to_bss_conf(vif, link_id);
- if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
+ if (link_conf && vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) {
struct mt792x_bss_conf *mconf;
mconf = mt792x_link_conf_to_mconf(link_conf);
+ if (!mconf)
+ goto out;
if (ieee80211_vif_is_mld(vif))
mt792x_mac_link_bss_remove(dev, mconf, mlink);
@@ -1117,6 +1146,7 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,
mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf,
link_sta, false);
}
+out:
spin_lock_bh(&mdev->sta_poll_lock);
if (!list_empty(&mlink->wcid.poll_list))
@@ -1304,6 +1334,8 @@ mt7925_mlo_pm_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
mt792x_mutex_acquire(dev);
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ if (!bss_conf)
+ continue;
mt7925_mcu_uni_bss_ps(dev, bss_conf);
}
mt792x_mutex_release(dev);
@@ -1630,6 +1662,8 @@ static void mt7925_ipv6_addr_change(struct ieee80211_hw *hw,
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ if (!bss_conf)
+ continue;
__mt7925_ipv6_addr_change(hw, bss_conf, idev);
}
}
@@ -1691,6 +1725,9 @@ mt7925_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
[IEEE80211_AC_BK] = 1,
};
+ if (!mconf)
+ return -EINVAL;
+
/* firmware uses access class index */
mconf->queue_params[mq_to_aci[queue]] = *params;
@@ -1861,6 +1898,8 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
if (changed & BSS_CHANGED_ARP_FILTER) {
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ if (!bss_conf)
+ continue;
mt7925_mcu_update_arp_filter(&dev->mt76, bss_conf);
}
}
@@ -1876,6 +1915,8 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw,
} else if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS) {
for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
bss_conf = mt792x_vif_to_bss_conf(vif, i);
+ if (!bss_conf)
+ continue;
mt7925_mcu_uni_bss_ps(dev, bss_conf);
}
}
@@ -1897,7 +1938,12 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw,
struct ieee80211_bss_conf *link_conf;
mconf = mt792x_vif_to_link(mvif, info->link_id);
+ if (!mconf)
+ return;
+
link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id);
+ if (!link_conf)
+ return;
mt792x_mutex_acquire(dev);
@@ -2021,6 +2067,11 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
mlink = mlinks[link_id];
link_conf = mt792x_vif_to_bss_conf(vif, link_id);
+ if (!link_conf) {
+ err = -EINVAL;
+ goto free;
+ }
+
rcu_assign_pointer(mvif->link_conf[link_id], mconf);
rcu_assign_pointer(mvif->sta.link[link_id], mlink);
@@ -2101,9 +2152,14 @@ static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw,
if (ieee80211_vif_is_mld(vif)) {
mconf = mt792x_vif_to_link(mvif, link_conf->link_id);
+ if (!mconf) {
+ mutex_unlock(&dev->mt76.mutex);
+ return -EINVAL;
+ }
+
pri_link_conf = mt792x_vif_to_bss_conf(vif, mvif->deflink_id);
- if (vif->type == NL80211_IFTYPE_STATION &&
+ if (pri_link_conf && vif->type == NL80211_IFTYPE_STATION &&
mconf == &mvif->bss_conf)
mt7925_mcu_add_bss_info(&dev->phy, NULL, pri_link_conf,
NULL, true);
@@ -2132,6 +2188,10 @@ static void mt7925_unassign_vif_chanctx(struct ieee80211_hw *hw,
if (ieee80211_vif_is_mld(vif)) {
mconf = mt792x_vif_to_link(mvif, link_conf->link_id);
+ if (!mconf) {
+ mutex_unlock(&dev->mt76.mutex);
+ return;
+ }
if (vif->type == NL80211_IFTYPE_STATION &&
mconf == &mvif->bss_conf)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index cf0fdea45cf7..94ec62a4538a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1087,6 +1087,8 @@ mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb,
struct mt792x_link_sta *mlink;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
+ if (!mlink)
+ return;
wcid = &mlink->wcid;
} else {
wcid = &mvif->sta.deflink.wcid;
@@ -1120,6 +1122,9 @@ int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev,
link_sta = mt792x_sta_to_link_sta(vif, sta, link_id);
mconf = mt792x_vif_to_link(mvif, link_id);
+ if (!mlink || !mconf)
+ return -EINVAL;
+
skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76,
&mlink->wcid,
MT7925_STA_UPDATE_MAX_SIZE);
@@ -1741,6 +1746,8 @@ mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb,
amsdu->amsdu_en = true;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
+ if (!mlink)
+ return;
mlink->wcid.amsdu = true;
switch (link_sta->agg.max_amsdu_len) {
@@ -1773,6 +1780,10 @@ mt7925_mcu_sta_phy_tlv(struct sk_buff *skb,
link_conf = mt792x_vif_to_bss_conf(vif, link_sta->link_id);
mconf = mt792x_vif_to_link(mvif, link_sta->link_id);
+
+ if (!link_conf || !mconf)
+ return;
+
chandef = mconf->mt76.ctx ? &mconf->mt76.ctx->def :
&link_conf->chanreq.oper;
@@ -1851,6 +1862,10 @@ mt7925_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb,
link_conf = mt792x_vif_to_bss_conf(vif, link_sta->link_id);
mconf = mt792x_vif_to_link(mvif, link_sta->link_id);
+
+ if (!link_conf || !mconf)
+ return;
+
chandef = mconf->mt76.ctx ? &mconf->mt76.ctx->def :
&link_conf->chanreq.oper;
band = chandef->chan->band;
@@ -1935,6 +1950,9 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb,
mconf = mt792x_vif_to_link(mvif, i);
mlink = mt792x_sta_to_link(msta, i);
+ if (!mconf || !mlink)
+ continue;
+
mld->link[cnt].wlan_id = cpu_to_le16(mlink->wcid.idx);
mld->link[cnt++].bss_idx = mconf->mt76.idx;
@@ -2027,13 +2045,13 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev,
.rcpi = to_rcpi(rssi),
};
struct mt792x_sta *msta;
- struct mt792x_link_sta *mlink;
+ struct mt792x_link_sta *mlink = NULL;
if (link_sta) {
msta = (struct mt792x_sta *)link_sta->sta->drv_priv;
mlink = mt792x_sta_to_link(msta, link_sta->link_id);
}
- info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid;
+ info.wcid = (link_sta && mlink) ? &mlink->wcid : &mvif->sta.deflink.wcid;
info.newly = state != MT76_STA_INFO_STATE_ASSOC;
return mt7925_mcu_sta_cmd(&dev->mphy, &info);
--
2.52.0
^ permalink raw reply related
* [PATCH 04/11] wifi: mt76: mt7921: fix deadlock in sta removal and suspend ROC abort
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Fix deadlock scenarios in mt7921 ROC (Remain On Channel) abort paths:
1. Suspend path deadlock (pci.c, sdio.c):
- Previous fix (b74d48c46f) added mutex around mt7921_roc_abort_sync
- But roc_work acquires mutex, so cancel_work_sync can deadlock
- Fix: Remove mutex wrappers since mt7921_roc_abort_sync doesn't
actually need them (it only calls timer_delete_sync, cancel_work_sync,
and ieee80211_iterate_interfaces which handles its own locking)
2. sta_remove path deadlock:
- mt7921_mac_sta_remove is called from mt76_sta_remove which holds mutex
- Calling mt7921_roc_abort_sync → cancel_work_sync can deadlock if
roc_work is waiting for the mutex
- Fix: Add mt7921_roc_abort_async (matching mt7925 pattern) that sets
abort flag and schedules work instead of blocking
- Add abort flag checking in mt7921_roc_work to handle async abort
The fix mirrors the mt7925 implementation which already handles these
scenarios correctly.
Fixes: b74d48c46f ("wifi: mt76: mt7921: fix mutex handling in multiple paths")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
.../net/wireless/mediatek/mt76/mt7921/main.c | 29 +++++++++++++++----
.../net/wireless/mediatek/mt76/mt7921/pci.c | 2 --
.../net/wireless/mediatek/mt76/mt7921/sdio.c | 2 --
3 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 9315dbdf8880..07d1d0d497f1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -367,17 +367,24 @@ static void mt7921_roc_iter(void *priv, u8 *mac,
mt7921_mcu_abort_roc(phy, mvif, phy->roc_token_id);
}
+/* Async ROC abort - safe to call while holding mutex.
+ * Sets abort flag and schedules roc_work for cleanup.
+ */
+static void mt7921_roc_abort_async(struct mt792x_dev *dev)
+{
+ struct mt792x_phy *phy = &dev->phy;
+
+ set_bit(MT76_STATE_ROC_ABORT, &phy->mt76->state);
+ timer_delete(&phy->roc_timer);
+ ieee80211_queue_work(phy->mt76->hw, &phy->roc_work);
+}
+
void mt7921_roc_abort_sync(struct mt792x_dev *dev)
{
struct mt792x_phy *phy = &dev->phy;
timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
- /* Note: caller must hold mutex if ieee80211_iterate_interfaces is
- * needed for ROC cleanup. Some call sites (like mt7921_mac_sta_remove)
- * already hold the mutex via mt76_sta_remove(). For suspend paths,
- * the mutex should be acquired before calling this function.
- */
if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
ieee80211_iterate_interfaces(mt76_hw(dev),
IEEE80211_IFACE_ITER_RESUME_ALL,
@@ -392,6 +399,15 @@ void mt7921_roc_work(struct work_struct *work)
phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy,
roc_work);
+ /* Check abort flag before acquiring mutex to prevent deadlock.
+ * Only send expired callback if ROC was actually active.
+ */
+ if (test_and_clear_bit(MT76_STATE_ROC_ABORT, &phy->mt76->state)) {
+ if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
+ ieee80211_remain_on_channel_expired(phy->mt76->hw);
+ return;
+ }
+
if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
return;
@@ -887,7 +903,8 @@ void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76);
struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
- mt7921_roc_abort_sync(dev);
+ /* Async abort - caller already holds mutex */
+ mt7921_roc_abort_async(dev);
mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->deflink.wcid);
mt76_connac_pm_wake(&dev->mphy, &dev->pm);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index 9f76b334b93d..ec9686183251 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -426,9 +426,7 @@ static int mt7921_pci_suspend(struct device *device)
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
- mt792x_mutex_acquire(dev);
mt7921_roc_abort_sync(dev);
- mt792x_mutex_release(dev);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
index 92ea2811816f..3421e53dc948 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
@@ -219,9 +219,7 @@ static int mt7921s_suspend(struct device *__dev)
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
- mt792x_mutex_acquire(dev);
mt7921_roc_abort_sync(dev);
- mt792x_mutex_release(dev);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
--
2.52.0
^ permalink raw reply related
* [PATCH 03/11] wifi: mt76: mt7921: add mutex protection in critical paths
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
Add proper mutex protection for mt7921 driver operations that access
hardware state without proper synchronization. This fixes multiple race
conditions that can cause system instability.
Fixes added:
1. mac.c: mt7921_mac_reset_work()
- Wrap ieee80211_iterate_active_interfaces() with mt792x_mutex
- The vif_connect_iter callback accesses hw_encap state
2. main.c: mt7921_remain_on_channel()
- Remove mt792x_mutex_acquire/release around mt7925_set_channel_state()
- The function is already called with mutex held from mac80211
- This was causing double-lock deadlock
3. main.c: mt7921_cancel_remain_on_channel()
- Remove mt792x_mutex_acquire/release
- Function is called from mac80211 with mutex already held
4. pci.c: mt7921_pci_pm_complete()
- Remove mt792x_mutex_acquire/release around ieee80211_iterate_active_interfaces
- This was causing deadlock as the vif connect iteration tries
to acquire the mutex again
5. usb.c: mt7921_usb_pm_complete()
- Same fix as pci.c for USB driver path
These changes prevent both missing mutex protection and mutex deadlocks
in the mt7921 driver.
Fixes: 5c14a5f944b9 ("wifi: mt76: mt7921: introduce remain_on_channel support")
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 2 ++
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 8 ++++++++
drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 2 ++
drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 2 ++
4 files changed, 14 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
index 03b4960db73f..f5c882e45bbe 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
@@ -693,9 +693,11 @@ void mt7921_mac_reset_work(struct work_struct *work)
clear_bit(MT76_RESET, &dev->mphy.state);
pm->suspended = false;
ieee80211_wake_queues(hw);
+ mt792x_mutex_acquire(dev);
ieee80211_iterate_active_interfaces(hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7921_vif_connect_iter, NULL);
+ mt792x_mutex_release(dev);
mt76_connac_power_save_sched(&dev->mt76.phy, pm);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
index 5fae9a6e273c..9315dbdf8880 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
@@ -373,6 +373,11 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev)
timer_delete_sync(&phy->roc_timer);
cancel_work_sync(&phy->roc_work);
+ /* Note: caller must hold mutex if ieee80211_iterate_interfaces is
+ * needed for ROC cleanup. Some call sites (like mt7921_mac_sta_remove)
+ * already hold the mutex via mt76_sta_remove(). For suspend paths,
+ * the mutex should be acquired before calling this function.
+ */
if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state))
ieee80211_iterate_interfaces(mt76_hw(dev),
IEEE80211_IFACE_ITER_RESUME_ALL,
@@ -619,6 +624,7 @@ void mt7921_set_runtime_pm(struct mt792x_dev *dev)
bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR);
pm->enable = pm->enable_user && !monitor;
+ /* Note: caller (debugfs) must hold mutex before calling this function */
ieee80211_iterate_active_interfaces(hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7921_pm_interface_iter, dev);
@@ -765,9 +771,11 @@ mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add)
struct mt792x_dev *dev = phy->dev;
u32 valid_vif_num = 0;
+ mt792x_mutex_acquire(dev);
ieee80211_iterate_active_interfaces(mt76_hw(dev),
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7921_calc_vif_num, &valid_vif_num);
+ mt792x_mutex_release(dev);
if (valid_vif_num > 1) {
phy->power_type = MT_AP_DEFAULT;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
index ec9686183251..9f76b334b93d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
@@ -426,7 +426,9 @@ static int mt7921_pci_suspend(struct device *device)
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
+ mt792x_mutex_acquire(dev);
mt7921_roc_abort_sync(dev);
+ mt792x_mutex_release(dev);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
index 3421e53dc948..92ea2811816f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c
@@ -219,7 +219,9 @@ static int mt7921s_suspend(struct device *__dev)
cancel_delayed_work_sync(&pm->ps_work);
cancel_work_sync(&pm->wake_work);
+ mt792x_mutex_acquire(dev);
mt7921_roc_abort_sync(dev);
+ mt792x_mutex_release(dev);
err = mt792x_mcu_drv_pmctrl(dev);
if (err < 0)
--
2.52.0
^ permalink raw reply related
* [PATCH 02/11] wifi: mt76: mt792x: fix NULL pointer and firmware reload issues
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
This patch combines two fixes for the shared mt792x code used by both
MT7921 and MT7925 drivers:
1. Fix NULL pointer dereference in TX path:
Add NULL pointer checks in mt792x_tx() to prevent kernel crashes when
transmitting packets during MLO link removal.
The function calls mt792x_sta_to_link() which can return NULL if the
link is being removed, but the return value was dereferenced without
checking. Similarly, the RCU-protected link_conf and link_sta pointers
were used without NULL validation.
This race can occur when:
- A packet is queued for transmission
- Concurrently, the link is being removed (mt7925_mac_link_sta_remove)
- mt792x_sta_to_link() returns NULL for the removed link
- Kernel crashes on wcid = &mlink->wcid dereference
Fix by checking mlink, conf, and link_sta before use, freeing the SKB
and returning early if any pointer is NULL.
2. Fix firmware reload failure after previous load crash:
If the firmware loading process crashes or is interrupted after
acquiring the patch semaphore but before releasing it, subsequent
firmware load attempts will fail with 'Failed to get patch semaphore'.
Apply the same fix from MT7915 (commit 79dd14f): release the patch
semaphore before starting firmware load and restart MCU firmware to
ensure clean state.
Fixes: c74df1c067f2 ("wifi: mt76: mt792x: introduce mt792x-lib module")
Fixes: 583204ae70f9 ("wifi: mt76: mt792x: move mt7921_load_firmware in mt792x-lib module")
Link: https://github.com/openwrt/mt76/commit/79dd14f2e8161b656341b6653261779199aedbe4
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
.../net/wireless/mediatek/mt76/mt792x_core.c | 27 +++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index f2ed16feb6c1..05598202b488 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -95,6 +95,8 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
IEEE80211_TX_CTRL_MLO_LINK);
sta = (struct mt792x_sta *)control->sta->drv_priv;
mlink = mt792x_sta_to_link(sta, link_id);
+ if (!mlink)
+ goto free_skb;
wcid = &mlink->wcid;
}
@@ -113,9 +115,12 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
link_id = wcid->link_id;
rcu_read_lock();
conf = rcu_dereference(vif->link_conf[link_id]);
- memcpy(hdr->addr2, conf->addr, ETH_ALEN);
-
link_sta = rcu_dereference(control->sta->link[link_id]);
+ if (!conf || !link_sta) {
+ rcu_read_unlock();
+ goto free_skb;
+ }
+ memcpy(hdr->addr2, conf->addr, ETH_ALEN);
memcpy(hdr->addr1, link_sta->addr, ETH_ALEN);
if (vif->type == NL80211_IFTYPE_STATION)
@@ -136,6 +141,10 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
}
mt76_connac_pm_queue_skb(hw, &dev->pm, wcid, skb);
+ return;
+
+free_skb:
+ ieee80211_free_txskb(hw, skb);
}
EXPORT_SYMBOL_GPL(mt792x_tx);
@@ -927,6 +936,20 @@ int mt792x_load_firmware(struct mt792x_dev *dev)
{
int ret;
+ /* Release semaphore if taken by previous failed load attempt.
+ * This prevents "Failed to get patch semaphore" errors when
+ * recovering from firmware crashes or suspend/resume failures.
+ */
+ ret = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false);
+ if (ret < 0)
+ dev_dbg(dev->mt76.dev, "Semaphore release returned %d (may be expected)\n", ret);
+
+ /* Always restart MCU to ensure clean state before loading firmware */
+ mt76_connac_mcu_restart(&dev->mt76);
+
+ /* Wait for MCU to be ready after restart */
+ msleep(100);
+
ret = mt76_connac2_load_patch(&dev->mt76, mt792x_patch_name(dev));
if (ret)
return ret;
--
2.52.0
^ permalink raw reply related
* [PATCH 01/11] wifi: mt76: fix list corruption in mt76_wcid_cleanup
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <20260120062854.126501-1-zac@zacbowling.com>
From: Zac Bowling <zac@zacbowling.com>
mt76_wcid_cleanup() was not removing wcid entries from sta_poll_list
before mt76_reset_device() reinitializes the master list. This leaves
stale pointers in wcid->poll_list, causing list corruption when
mt76_wcid_add_poll() later checks list_empty() and tries to add the
entry back.
The fix adds proper cleanup of poll_list in mt76_wcid_cleanup(),
matching how tx_list is already handled. This is similar to what
mt7996_mac_sta_deinit_link() already does correctly.
Fixes list corruption warnings like:
list_add corruption. prev->next should be next (ffffffff...)
Signed-off-by: Zac Bowling <zac@zacbowling.com>
---
drivers/net/wireless/mediatek/mt76/mac80211.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 75772979f438..d0c522909e98 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1716,6 +1716,16 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
idr_destroy(&wcid->pktid);
+ /* Remove from sta_poll_list to prevent list corruption after reset.
+ * Without this, mt76_reset_device() reinitializes sta_poll_list but
+ * leaves wcid->poll_list with stale pointers, causing list corruption
+ * when mt76_wcid_add_poll() checks list_empty().
+ */
+ spin_lock_bh(&dev->sta_poll_lock);
+ if (!list_empty(&wcid->poll_list))
+ list_del_init(&wcid->poll_list);
+ spin_unlock_bh(&dev->sta_poll_lock);
+
spin_lock_bh(&phy->tx_lock);
if (!list_empty(&wcid->tx_list))
--
2.52.0
^ permalink raw reply related
* [PATCH v5 00/11] wifi: mt76: mt7925/mt7921 stability fixes
From: Zac @ 2026-01-20 6:28 UTC (permalink / raw)
To: sean.wang
Cc: deren.wu, kvalo, linux-kernel, linux-mediatek, linux-wireless,
lorenzo, nbd, ryder.lee, sean.wang, stable, linux, zbowling,
Zac Bowling
In-Reply-To: <CAGp9LzpuyXRDa=TxqY+Xd5ZhDVvNayWbpMGDD1T0g7apkn7P0A@mail.gmail.com>
From: Zac Bowling <zac@zacbowling.com>
This series addresses stability issues in the mt7925 (WiFi 7) and mt7921
drivers, focusing on NULL pointer dereferences, mutex protection, MLO
(Multi-Link Operation) handling, and ROC (Remain-On-Channel) state machine
fixes.
Changes since v4:
- Reorganized 27 patches into 11 cleaner, logically-grouped patches for
easier review. Patches are now ordered by subsystem dependency:
mt76 core -> mt792x shared -> mt7921 -> mt7925
- Consolidated ROC-related fixes (previously patches 22-27) into a single
comprehensive patch (11/11) that addresses the interconnected deadlock
and race condition issues discovered through extended testing
- New issues fixed since v4:
* ROC deadlock in sta removal path - cancel_work_sync() was waiting for
roc_work which needed the mutex already held by sta_remove
* ROC timer race during suspend - timer could fire after suspend started
but before ROC was properly aborted
* Async ROC abort race condition - double-free when async abort raced
with normal ROC completion
* Added ROC rate limiting with exponential backoff to mitigate MLO
authentication failures caused by rapid ROC requests overwhelming
the MT7925 firmware
* Fixed spurious ieee80211_remain_on_channel_expired() callback when
ROC wasn't actually active (found via code review)
- Added corresponding mt7921 fixes (patches 3-4) since the older driver
shares similar code paths and exhibited the same deadlock patterns
- Firmware reload fix (patch 2) addresses crashes when the device needs
recovery after a failed firmware load - the semaphore wasn't being
released, causing subsequent loads to hang
Investigation and Testing:
All issues were discovered through real-world testing on Framework 16
laptops with the MT7925 (RZ616) WiFi module. Crash dumps, dmesg logs,
and detailed analysis are available in the repository below.
A DKMS version with extensive debug logging is available for community
testing. This has been instrumental in tracking down the more subtle
race conditions and deadlocks that only manifest under specific timing
conditions.
Repository: https://github.com/zbowling/mt7925
- kernels/ - Pre-built patches for 6.17, 6.18, 6.19-rc, nbd168
- dkms/ - DKMS module with extra debug logging
- crashes/ - Crash investigation logs and analysis
Acknowledgments:
Thank you to the community members who tested the DKMS version and
provided crash reports, dmesg dumps, and helped track down the more
elusive deadlocks. Your patience and detailed bug reports made these
fixes possible.
Tested on MT7925 (RZ616) with kernels 6.17.13, 6.18.5, and 6.19-rc5.
Zac Bowling (11):
wifi: mt76: fix list corruption in mt76_wcid_cleanup
wifi: mt76: mt792x: fix NULL pointer and firmware reload issues
wifi: mt76: mt7921: add mutex protection in critical paths
wifi: mt76: mt7921: fix deadlock in sta removal and suspend ROC abort
wifi: mt76: mt7925: add comprehensive NULL pointer protection for MLO
wifi: mt76: mt7925: add mutex protection in critical paths
wifi: mt76: mt7925: add MCU command error handling
wifi: mt76: mt7925: add lockdep assertions for mutex verification
wifi: mt76: mt7925: fix MLO roaming and ROC setup issues
wifi: mt76: mt7925: fix BA session teardown during beacon loss
wifi: mt76: mt7925: fix ROC deadlocks and race conditions
drivers/net/wireless/mediatek/mt76/mac80211.c | 8 +
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 2 +
drivers/net/wireless/mediatek/mt76/mt7921/main.c | 37 ++-
drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 2 -
drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 2 -
drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 8 +
drivers/net/wireless/mediatek/mt76/mt7925/main.c | 257 +++++++++++++--
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 46 ++-
drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 4 +
drivers/net/wireless/mediatek/mt76/mt792x.h | 7 +
drivers/net/wireless/mediatek/mt76/mt792x_core.c | 17 +-
12 files changed, 340 insertions(+), 51 deletions(-)
--
2.52.0
^ permalink raw reply
* Re: [PATCH] wifi: ath11k: fix memory leaks in beacon template setup
From: Zilin Guan @ 2026-01-20 5:55 UTC (permalink / raw)
To: baochen.qiang
Cc: ath11k, jianhao.xu, jjohnson, linux-kernel, linux-wireless, zilin
In-Reply-To: <55fad8d1-ab4f-48b8-8712-24664a6bab2c@oss.qualcomm.com>
On Tue, Jan 20, 2026 at 10:52:54AM +0800, Baochen Qiang wrote:
> below would be better?
>
> @@ -1622,19 +1622,21 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
> }
>
> if (tx_arvif == arvif) {
> - if (ath11k_mac_set_vif_params(tx_arvif, bcn))
> - return -EINVAL;
> + if (ath11k_mac_set_vif_params(tx_arvif, bcn)) {
> + ret = -EINVAL;
> + goto free;
> + }
> } else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
> - return -EINVAL;
> + ret = -EINVAL;
> + goto free;
> }
>
> ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
> - kfree_skb(bcn);
> -
> if (ret)
> ath11k_warn(ab, "failed to submit beacon template command: %d\n",
> ret);
> -
> +free:
> + kfree_skb(bcn);
> return ret;
> }
>
> > static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
Thanks for your suggestion. I will send a v2 to change it.
Regards,
Zilin Guan
^ permalink raw reply
* Re: [PATCH wireless-next] wifi: cfg80211: don't apply HT flags to S1G channels
From: Lachlan Hodges @ 2026-01-20 5:22 UTC (permalink / raw)
To: Johannes Berg; +Cc: linux-wireless, arien.judge
In-Reply-To: <14bc5d3362b071c7c7a6a64724d5b354c173a501.camel@sipsolutions.net>
> > Playing around with some 5 GHz configurations in OpenWRT it appears that only
> > so much information can be conveyed and indeed there are configurations that
> > will be rejected by cfg80211 (or maybe hostapd in some cases, not sure).
>
> Really? Got an example, perhaps even with hostapd log to see how it's
> rejected? That clearly contradicts what I wrote above which I really did
> believe to be true until this moment ;-)
Sorry, it is indeed cfg80211 that is not accepting the chandef configuration.
Taking a look at the generated config:
country_code=AU
ieee80211d=1
ieee80211h=1
hw_mode=a
beacon_int=100
stationary_ap=1
channel=165
[...]
ieee80211n=1
ht_coex=0
ht_capab=[HT40+][LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935]
ieee80211ac=1
vht_oper_chwidth=1
vht_oper_centr_freq_seg0_idx=171
[vht_caps..]
ieee80211ax=1
he_oper_chwidth=1
he_oper_centr_freq_seg0_idx=171
This is the result of selecting mode AX, channel 165 with a width of 80MHz.
The GL-MT3000 using the mt76 driver with mt7915e driver (?)
cfg80211 299008 4 mt7915e,mt76_connac_lib,mt76,mac80211
compat 12288 3 mt76,mac80211,cfg80211
mac80211 581632 3 mt7915e,mt76_connac_lib,mt76
mt76 73728 2 mt7915e,mt76_connac_lib
mt76_connac_lib 45056 1 mt7915e
And for hostapd output (Sorry I don't have anything more verbose)
Tue Jan 20 04:10:43 2026 daemon.err hostapd: nl80211: kernel reports: (extension) channel is disabled
Tue Jan 20 04:10:43 2026 daemon.err hostapd: Could not set channel for kernel driver
Tue Jan 20 04:10:43 2026 daemon.err hostapd: Interface initialization failed
where _cfg80211_chandef_usable() is (rightfully) rejecting the configuration.
This simply appears to be the logic to generate the config doesn't cover this
case properly. Anyways, I don't think this is all that useful too be honest,
it just comes down to ensuring the static configurations are known especially
since this range is specified as AUTO-BW anyway as per the regdom.
> > I
> > suppose what I'm asking is - how much do we need to protect usermode from
> > this? Do you feel it's worth including a flag that somewhat emulates
> > NOHT40+/-? I know much of that logic is quite old and S1G is "modern" ... ?
>
> It's always nice to have userspace be aware of things, but I guess if
> it's a configuration that's statically known to be invalid, it wouldn't
> matter so much? But I don't think I've understood the S1G angle - are
> you talking about primary 1 MHz? Primary 2 MHz?
Putting primaries aside and just thinking of operating - eventually we would
need a NO_2MHZ flag as India for example only has 1MHz channels.. however
this is well into the future .. we first need to add support for other
countries :').
But yes as you said since these configurations are static it's not really a
big deal and more up to userspace to be aware of it. I think what we have in
cfg80211 now is good enough and seems to be working well when integrating
into an actual userspace stack.
> > Now on an unrelated note while I'm sending this email, we've sent a few additional
> > fixups / cleanups over the last 2 months as we hope to have a driver submitted
> > for review in the coming weeks. There's still 2 small fixes that need to be
> > done within hwsim before we can send up our hostapd patchset but I will
> > wait until we have at least kicked of the driver review process before sending
> > them.
>
> Very cool! :)
>
> We just posted a lot of hwsim changes for NAN - better send yours sooner
> rather than later ;-)
Yes I have seen, lots of Wi-Fi 8 documents aswell so I assume that will get busy
soon too. Well see .. Lots to do!!
lachlan
^ permalink raw reply
* RE: [PATCH v2] wifi: rtw89: debug: Fix memory leak in __print_txpwr_map()
From: Zong-Zhe Yang @ 2026-01-20 3:39 UTC (permalink / raw)
To: Ping-Ke Shih, Zilin Guan
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
jianhao.xu@seu.edu.cn
In-Reply-To: <1127a2bb3b86437e82cf4b46c1a73f44@realtek.com>
Ping-Ke Shih <pkshih@realtek.com> wrote:
> Zong-Zhe Yang <kevin_yang@realtek.com> wrote:
> > Zilin Guan <zilin@seu.edu.cn> wrote:
> > >
> > > In __print_txpwr_map(), memory is allocated to bufp via vzalloc().
> > > If max_valid_addr is 0, the function returns -EOPNOTSUPP immediately
> > > without freeing bufp, leading to a memory leak.
> > >
> > > Since the validation of max_valid_addr does not depend on the allocated
> > > memory, fix this by moving the vzalloc() call after the check.
> > >
> > > Compile tested only. Issue found using a prototype static analysis tool
> > > and code review.
> > >
> > > Fixes: 036042e15770 ("wifi: rtw89: debug: txpwr table supports Wi-Fi 7 chips")
> > > Suggested-by: Zong-Zhe Yang <kevin_yang@realtek.com>
> > > Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
> > > ---
> > > Changes in v2:
> > > - Move memory allocation after validation check to avoid leak.
> > >
> > > drivers/net/wireless/realtek/rtw89/debug.c | 8 ++++----
> > > 1 file changed, 4 insertions(+), 4 deletions(-)
> > >
> > > diff --git a/drivers/net/wireless/realtek/rtw89/debug.c
> > > b/drivers/net/wireless/realtek/rtw89/debug.c
> > > index 1264c2f82600..987eef8170f2 100644
> > > --- a/drivers/net/wireless/realtek/rtw89/debug.c
> > > +++ b/drivers/net/wireless/realtek/rtw89/debug.c
> > > @@ -825,10 +825,6 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev,
> char
> > > *buf, size_t buf
> > > s8 *bufp, tmp;
> > > int ret;
> > >
> > > - bufp = vzalloc(map->addr_to - map->addr_from + 4);
> > > - if (!bufp)
> > > - return -ENOMEM;
> > > -
> > > if (path_num == 1)
> > > max_valid_addr = map->addr_to_1ss;
> > > else
> > > @@ -837,6 +833,10 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev,
> char
> > > *buf, size_t buf
> > > if (max_valid_addr == 0)
> > > return -EOPNOTSUPP;
> > >
> > > + bufp = vzalloc(map->addr_to - map->addr_from + 4);
> > > + if (!bufp)
> > > + return -ENOMEM;
> > > +
> > > for (addr = map->addr_from; addr <= max_valid_addr; addr += 4) {
> > > ret = rtw89_mac_txpwr_read32(rtwdev, RTW89_PHY_0, addr, &val);
> > > if (ret)
> > > --
> > > 2.34.1
> >
> > Looks good to me.
>
> I suppose I can add
> Reviewed-by: Zong-Zhe Yang <kevin_yang@realtek.com>
>
> Okay?
Okay, thanks.
^ permalink raw reply
* RE: [PATCH v2] wifi: rtw89: debug: Fix memory leak in __print_txpwr_map()
From: Ping-Ke Shih @ 2026-01-20 3:31 UTC (permalink / raw)
To: Zong-Zhe Yang, Zilin Guan
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
jianhao.xu@seu.edu.cn
In-Reply-To: <e8e7b26c37084c418e2d6778c77ca100@realtek.com>
Zong-Zhe Yang <kevin_yang@realtek.com> wrote:
> Zilin Guan <zilin@seu.edu.cn> wrote:
> >
> > In __print_txpwr_map(), memory is allocated to bufp via vzalloc().
> > If max_valid_addr is 0, the function returns -EOPNOTSUPP immediately
> > without freeing bufp, leading to a memory leak.
> >
> > Since the validation of max_valid_addr does not depend on the allocated
> > memory, fix this by moving the vzalloc() call after the check.
> >
> > Compile tested only. Issue found using a prototype static analysis tool
> > and code review.
> >
> > Fixes: 036042e15770 ("wifi: rtw89: debug: txpwr table supports Wi-Fi 7 chips")
> > Suggested-by: Zong-Zhe Yang <kevin_yang@realtek.com>
> > Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
> > ---
> > Changes in v2:
> > - Move memory allocation after validation check to avoid leak.
> >
> > drivers/net/wireless/realtek/rtw89/debug.c | 8 ++++----
> > 1 file changed, 4 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/net/wireless/realtek/rtw89/debug.c
> > b/drivers/net/wireless/realtek/rtw89/debug.c
> > index 1264c2f82600..987eef8170f2 100644
> > --- a/drivers/net/wireless/realtek/rtw89/debug.c
> > +++ b/drivers/net/wireless/realtek/rtw89/debug.c
> > @@ -825,10 +825,6 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char
> > *buf, size_t buf
> > s8 *bufp, tmp;
> > int ret;
> >
> > - bufp = vzalloc(map->addr_to - map->addr_from + 4);
> > - if (!bufp)
> > - return -ENOMEM;
> > -
> > if (path_num == 1)
> > max_valid_addr = map->addr_to_1ss;
> > else
> > @@ -837,6 +833,10 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char
> > *buf, size_t buf
> > if (max_valid_addr == 0)
> > return -EOPNOTSUPP;
> >
> > + bufp = vzalloc(map->addr_to - map->addr_from + 4);
> > + if (!bufp)
> > + return -ENOMEM;
> > +
> > for (addr = map->addr_from; addr <= max_valid_addr; addr += 4) {
> > ret = rtw89_mac_txpwr_read32(rtwdev, RTW89_PHY_0, addr, &val);
> > if (ret)
> > --
> > 2.34.1
>
> Looks good to me.
I suppose I can add
Reviewed-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Okay?
^ permalink raw reply
* RE: [PATCH v2] wifi: rtw89: debug: Fix memory leak in __print_txpwr_map()
From: Zong-Zhe Yang @ 2026-01-20 3:27 UTC (permalink / raw)
To: Zilin Guan, Ping-Ke Shih
Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
jianhao.xu@seu.edu.cn
In-Reply-To: <20260116130834.1413924-1-zilin@seu.edu.cn>
Zilin Guan <zilin@seu.edu.cn> wrote:
>
> In __print_txpwr_map(), memory is allocated to bufp via vzalloc().
> If max_valid_addr is 0, the function returns -EOPNOTSUPP immediately
> without freeing bufp, leading to a memory leak.
>
> Since the validation of max_valid_addr does not depend on the allocated
> memory, fix this by moving the vzalloc() call after the check.
>
> Compile tested only. Issue found using a prototype static analysis tool
> and code review.
>
> Fixes: 036042e15770 ("wifi: rtw89: debug: txpwr table supports Wi-Fi 7 chips")
> Suggested-by: Zong-Zhe Yang <kevin_yang@realtek.com>
> Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
> ---
> Changes in v2:
> - Move memory allocation after validation check to avoid leak.
>
> drivers/net/wireless/realtek/rtw89/debug.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/net/wireless/realtek/rtw89/debug.c
> b/drivers/net/wireless/realtek/rtw89/debug.c
> index 1264c2f82600..987eef8170f2 100644
> --- a/drivers/net/wireless/realtek/rtw89/debug.c
> +++ b/drivers/net/wireless/realtek/rtw89/debug.c
> @@ -825,10 +825,6 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char
> *buf, size_t buf
> s8 *bufp, tmp;
> int ret;
>
> - bufp = vzalloc(map->addr_to - map->addr_from + 4);
> - if (!bufp)
> - return -ENOMEM;
> -
> if (path_num == 1)
> max_valid_addr = map->addr_to_1ss;
> else
> @@ -837,6 +833,10 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char
> *buf, size_t buf
> if (max_valid_addr == 0)
> return -EOPNOTSUPP;
>
> + bufp = vzalloc(map->addr_to - map->addr_from + 4);
> + if (!bufp)
> + return -ENOMEM;
> +
> for (addr = map->addr_from; addr <= max_valid_addr; addr += 4) {
> ret = rtw89_mac_txpwr_read32(rtwdev, RTW89_PHY_0, addr, &val);
> if (ret)
> --
> 2.34.1
Looks good to me.
Thanks.
^ permalink raw reply
* [PATCH wireless] wifi: mac80211: don't perform DA check on S1G beacon
From: Lachlan Hodges @ 2026-01-20 3:11 UTC (permalink / raw)
To: johannes; +Cc: linux-wireless, arien.judge, Lachlan Hodges
S1G beacons don't contain the DA field as per IEEE80211-2024 9.3.4.3,
so the DA broadcast check reads the SA address of the S1G beacon which
will subsequently lead to the beacon being dropped. As a result, passive
scanning is not possible. Fix this by only performing the check on
non-S1G beacons to allow S1G long beacons to be processed during a
passive scan.
Fixes: ddf82e752f8a ("wifi: mac80211: Allow beacons to update BSS table regardless of scan")
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
---
net/mac80211/scan.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 5ef315ed3b0f..4823c8d45639 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -347,8 +347,13 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
mgmt->da))
return;
} else {
- /* Beacons are expected only with broadcast address */
- if (!is_broadcast_ether_addr(mgmt->da))
+ /*
+ * Non-S1G beacons are expected only with broadcast address.
+ * S1G beacons only carry the SA so no DA check is required
+ * nor possible.
+ */
+ if (!ieee80211_is_s1g_beacon(mgmt->frame_control) &&
+ !is_broadcast_ether_addr(mgmt->da))
return;
}
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] wifi: ath11k: fix memory leaks in beacon template setup
From: Baochen Qiang @ 2026-01-20 2:52 UTC (permalink / raw)
To: Zilin Guan, jjohnson; +Cc: linux-wireless, ath11k, linux-kernel, jianhao.xu
In-Reply-To: <20260119160535.1874628-1-zilin@seu.edu.cn>
On 1/20/2026 12:05 AM, Zilin Guan wrote:
> The functions ath11k_mac_setup_bcn_tmpl_ema() and
> ath11k_mac_setup_bcn_tmpl_mbssid() allocate memory for beacon templates
> but fail to free it when parameter setup returns an error.
>
> Since beacon templates must be released during normal execution, they
> must also be released in the error handling paths to prevent memory
> leaks.
>
> Fix this by adding the missing deallocation calls in the respective
> error paths.
>
> Compile tested only. Issue found using a prototype static analysis tool
> and code review.
>
> Fixes: 3a415daa3e8b ("wifi: ath11k: add P2P IE in beacon template")
> Fixes: 335a92765d30 ("wifi: ath11k: MBSSID beacon support")
> Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
> ---
> drivers/net/wireless/ath/ath11k/mac.c | 12 +++++++++---
> 1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
> index 4dfd08b58416..005cc81a3244 100644
> --- a/drivers/net/wireless/ath/ath11k/mac.c
> +++ b/drivers/net/wireless/ath/ath11k/mac.c
> @@ -1561,8 +1561,10 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif,
> }
>
> if (tx_arvif == arvif) {
> - if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb))
> + if (ath11k_mac_set_vif_params(tx_arvif, beacons->bcn[0].skb)) {
> + ieee80211_beacon_free_ema_list(beacons);
> return -EINVAL;
> + }
> } else {
> arvif->wpaie_present = tx_arvif->wpaie_present;
> }
> @@ -1623,9 +1625,9 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
>
> if (tx_arvif == arvif) {
> if (ath11k_mac_set_vif_params(tx_arvif, bcn))
> - return -EINVAL;
> + goto err;
> } else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
> - return -EINVAL;
> + goto err;
> }
>
> ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
> @@ -1636,6 +1638,10 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
> ret);
>
> return ret;
> +
> +err:
> + kfree_skb(bcn);
> + return -EINVAL;
> }
>
below would be better?
@@ -1622,19 +1622,21 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif,
}
if (tx_arvif == arvif) {
- if (ath11k_mac_set_vif_params(tx_arvif, bcn))
- return -EINVAL;
+ if (ath11k_mac_set_vif_params(tx_arvif, bcn)) {
+ ret = -EINVAL;
+ goto free;
+ }
} else if (!ath11k_mac_set_nontx_vif_params(tx_arvif, arvif, bcn)) {
- return -EINVAL;
+ ret = -EINVAL;
+ goto free;
}
ret = ath11k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, 0);
- kfree_skb(bcn);
-
if (ret)
ath11k_warn(ab, "failed to submit beacon template command: %d\n",
ret);
-
+free:
+ kfree_skb(bcn);
return ret;
}
> static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif)
^ permalink raw reply
* Re: New warning `ath10k_pci 0000:3a:00.0: not found station for peer stats`
From: Baochen Qiang @ 2026-01-20 2:04 UTC (permalink / raw)
To: Paul Menzel, Jeff Johnson; +Cc: linux-wireless, ath10k
In-Reply-To: <6655c609-2b5b-448c-8cda-cb4a97da64e1@oss.qualcomm.com>
On 1/20/2026 10:01 AM, Baochen Qiang wrote:
>
>
> On 1/20/2026 12:41 AM, Paul Menzel wrote:
>> Dear Linux folks,
>>
>>
>> Since January 10th, I have started seeing the warning below in my Linux logs, that reach
>> back to September 24th, 2025:
>>
>> [ 37.108902] ath10k_pci 0000:3a:00.0: not found station for peer stats
>> [ 37.108906] ath10k_pci 0000:3a:00.0: failed to parse stats info tlv: -22
>>
>> It started appearing with 6.19.0-rc4-00282-gb6151c4e60e5, the version running before is
>> 6.19.0-rc4-00003-g3609fa95fb0f, but I do not see anything related in the commit list:
>>
>> git log --oneline 3609fa95fb0f...b6151c4e60e5
>>
>> The warning log from `drivers/net/wireless/ath/ath10k/wmi-tlv.c` has also been there since
>> 2021.
>>
>> Do you have an idea? Please find the output of `dmesg` attached.
>
> please try
>
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a203dbeeca15a9b924f0d51f510921f4bae96801
wait, seems I mixed it with another issue. Please ignore my comment, it is not expected to
fix the issue here.
>
>>
>>
>> Kind regards,
>>
>> Paul
>
^ permalink raw reply
* Re: New warning `ath10k_pci 0000:3a:00.0: not found station for peer stats`
From: Baochen Qiang @ 2026-01-20 2:01 UTC (permalink / raw)
To: Paul Menzel, Jeff Johnson; +Cc: linux-wireless, ath10k
In-Reply-To: <57671b89-ec9f-4e6c-992c-45eb8e75929c@molgen.mpg.de>
On 1/20/2026 12:41 AM, Paul Menzel wrote:
> Dear Linux folks,
>
>
> Since January 10th, I have started seeing the warning below in my Linux logs, that reach
> back to September 24th, 2025:
>
> [ 37.108902] ath10k_pci 0000:3a:00.0: not found station for peer stats
> [ 37.108906] ath10k_pci 0000:3a:00.0: failed to parse stats info tlv: -22
>
> It started appearing with 6.19.0-rc4-00282-gb6151c4e60e5, the version running before is
> 6.19.0-rc4-00003-g3609fa95fb0f, but I do not see anything related in the commit list:
>
> git log --oneline 3609fa95fb0f...b6151c4e60e5
>
> The warning log from `drivers/net/wireless/ath/ath10k/wmi-tlv.c` has also been there since
> 2021.
>
> Do you have an idea? Please find the output of `dmesg` attached.
please try
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a203dbeeca15a9b924f0d51f510921f4bae96801
>
>
> Kind regards,
>
> Paul
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox