From: JB Tsai <jb.tsai@mediatek.com>
To: <nbd@nbd.name>, <lorenzo@kernel.org>
Cc: <linux-wireless@vger.kernel.org>,
<linux-mediatek@lists.infradead.org>, <Sean.Wang@mediatek.com>,
<James-LY.Huang@mediatek.com>, <jb.tsai@mediatek.com>,
james-ly huang <james-ly.huang@mediatek.com>
Subject: [PATCH 8/9] wifi: mt76: mt7925: track BSS absence and drop stale GO probe responses
Date: Wed, 4 Nov 2026 17:23:43 +0800 [thread overview]
Message-ID: <20261104092344.1932406-8-jb.tsai@mediatek.com> (raw)
In-Reply-To: <20261104092344.1932406-1-jb.tsai@mediatek.com>
From: james-ly huang <james-ly.huang@mediatek.com>
The FW tells the host with a BSS_IS_ABSENCE event when a P2P GO's
BSS currently has no channel
- latch the absent / free-quota state per bss_conf
(mt7925_p2p_mcu_bss_is_absence_event in p2p.c), and kick the
TX path (tx_worker) on the absent->present edge so whatever
piled up while the BSS had no channel is dequeued
immediately;
- clear the latched state where the BSS index is (re)assigned:
a GO stopped while absent and restarted on the same netdev
would otherwise come back with absent still latched and drop
every Probe Response until the FW sends a presence event
for the new bss_idx;
- the TX path (mt792x_tx) drops a GO Probe Response while the
BSS is absent - by the time the FW comes back the requester
has moved on, so the response is stale and only costs airtime
and a TX-abort.
Signed-off-by: james-ly huang <james-ly.huang@mediatek.com>
---
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 +
.../net/wireless/mediatek/mt76/mt7925/main.c | 12 +++
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 3 +
.../net/wireless/mediatek/mt76/mt7925/p2p.c | 97 +++++++++++++++++++
.../net/wireless/mediatek/mt76/mt7925/p2p.h | 20 ++++
drivers/net/wireless/mediatek/mt76/mt792x.h | 25 +++++
.../net/wireless/mediatek/mt76/mt792x_core.c | 16 +++
7 files changed, 174 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 817554ffb9d8..2acaae9830fb 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1102,6 +1102,7 @@ enum {
MCU_UNI_EVENT_SAP = 0x1c,
MCU_UNI_EVENT_ROC = 0x27,
MCU_UNI_EVENT_MBMC = 0x28,
+ MCU_UNI_EVENT_BSS_IS_ABSENCE = 0x29,
MCU_UNI_EVENT_TX_DONE = 0x2d,
MCU_UNI_EVENT_THERMAL = 0x35,
MCU_UNI_EVENT_RSSI_MONITOR = 0x41,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index cfdb736f53ca..25e981e38ea9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -401,6 +401,18 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev,
0 : mconf->mt76.idx;
mconf->mt76.band_idx = 0xff;
+ /* A freshly configured BSS is on channel until the FW says otherwise.
+ * mt792x_bss_conf lives in vif->drv_priv / mvif->link_conf[], which
+ * mac80211 only zeroes when the interface is created - a GO that is
+ * stopped while absent and restarted on the same netdev would come back
+ * with absent still latched, and then drop every Probe Response until
+ * the FW happens to send a presence event for the new bss_idx. Clear it
+ * here, where the BSS index is (re)assigned.
+ */
+ mconf->absent = false;
+ mconf->free_quota = 0;
+ mconf->absence_tstamp = 0;
+
if (is_mt7927(&dev->mt76)) {
struct ieee80211_channel *chan;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index d9a5b5f57fba..24de2ff3a15d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -738,6 +738,9 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev,
case MCU_UNI_EVENT_SAP:
mt7925_p2p_mcu_sap_event(dev, skb);
break;
+ case MCU_UNI_EVENT_BSS_IS_ABSENCE:
+ mt7925_p2p_mcu_bss_is_absence_event(dev, skb);
+ break;
case MCU_UNI_EVENT_RSSI_MONITOR:
mt7925_mcu_rssi_monitor_event(dev, skb);
break;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c
index d05c936d40ea..9be6891cc5d9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.c
@@ -603,3 +603,100 @@ void mt7925_p2p_mcu_sap_event(struct mt792x_dev *dev, struct sk_buff *skb)
}
}
+struct mt7925_bss_absence_data {
+ u8 bss_idx;
+ u8 is_absent;
+ u8 free_quota;
+};
+
+/* Per-BSS handling of the BSS_IS_ABSENCE event: latch the absent /
+ * free-quota state, and on the absent->present edge kick the TX path so
+ * whatever piled up while the BSS had no channel gets dequeued
+ * immediately (the kick is the tx_worker).
+ */
+static void
+mt7925_mcu_bss_absence_iter(void *priv, u8 *mac, struct ieee80211_vif *vif)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+ struct mt7925_bss_absence_data *data = priv;
+ struct mt792x_bss_conf *mconf = NULL;
+ struct mt792x_dev *dev = mvif->phy->dev;
+ unsigned long valid_links, now = jiffies;
+ unsigned int link_id;
+ bool absent, was_absent;
+
+ if (ieee80211_vif_is_mld(vif)) {
+ valid_links = mvif->valid_links;
+ for_each_set_bit(link_id, &valid_links,
+ IEEE80211_MLD_MAX_NUM_LINKS) {
+ struct mt792x_bss_conf *link_mconf;
+
+ link_mconf = rcu_dereference(mvif->link_conf[link_id]);
+ if (link_mconf && link_mconf->mt76.idx == data->bss_idx) {
+ mconf = link_mconf;
+ break;
+ }
+ }
+ } else if (mvif->bss_conf.mt76.idx == data->bss_idx) {
+ mconf = &mvif->bss_conf;
+ }
+
+ if (!mconf)
+ return;
+
+ absent = !!data->is_absent;
+ was_absent = mconf->absent;
+
+ mconf->free_quota = data->free_quota;
+ if (was_absent != absent) {
+ mconf->absent = absent;
+ mconf->absence_tstamp = now;
+ }
+
+ /* From Absent to Present */
+ if (was_absent && !absent)
+ mt76_worker_schedule(&dev->mt76.tx_worker);
+}
+
+void mt7925_p2p_mcu_bss_is_absence_event(struct mt792x_dev *dev, struct sk_buff *skb)
+{
+ struct mt7925_uni_bss_is_absence_event *event;
+ struct mt7925_bss_absence_data data;
+ u32 tlv_len;
+ u8 *tlv;
+
+ skb_pull(skb, sizeof(struct mt7925_mcu_rxd));
+ if (skb->len < sizeof(*event))
+ return;
+
+ event = (struct mt7925_uni_bss_is_absence_event *)skb->data;
+ tlv = event->tlv;
+ tlv_len = skb->len - sizeof(*event);
+
+ while (tlv_len >= sizeof(struct tlv)) {
+ struct tlv *hdr = (struct tlv *)tlv;
+ u16 tag = le16_to_cpu(hdr->tag);
+ u16 len = le16_to_cpu(hdr->len);
+
+ if (len < sizeof(*hdr) || len > tlv_len)
+ break;
+
+ if (tag == MT7925_BSS_IS_ABSENCE_TAG_INFO &&
+ len >= sizeof(struct mt7925_bss_absence_info_tlv)) {
+ struct mt7925_bss_absence_info_tlv *info =
+ (struct mt7925_bss_absence_info_tlv *)tlv;
+
+ data.bss_idx = event->bss_idx;
+ data.is_absent = info->is_absent;
+ data.free_quota = info->free_quota;
+
+ ieee80211_iterate_active_interfaces_atomic(dev->mt76.phy.hw,
+ IEEE80211_IFACE_ITER_RESUME_ALL,
+ mt7925_mcu_bss_absence_iter,
+ &data);
+ }
+
+ tlv += len;
+ tlv_len -= len;
+ }
+}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h
index 9afae222845f..e189f9e56e93 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/p2p.h
@@ -53,6 +53,24 @@ struct mt7925_uni_sap_event {
u8 pad[4]; /* fixed field */
u8 tlv[];
} __packed;
+
+/* BSS absence/presence event (FW event UNI_EVENT_ID_BSS_IS_ABSENCE) */
+#define MT7925_BSS_IS_ABSENCE_TAG_INFO 0
+
+struct mt7925_bss_absence_info_tlv {
+ __le16 tag;
+ __le16 len;
+ u8 is_absent;
+ u8 free_quota;
+ u8 pad[2];
+} __packed;
+
+struct mt7925_uni_bss_is_absence_event {
+ u8 bss_idx;
+ u8 pad[3];
+ u8 tlv[];
+} __packed;
+
/* TX/RX path overrides, BMC wcid bookkeeping and the P2P-only MCU event
* handlers.
*/
@@ -69,5 +87,7 @@ bool mt7925_p2p_chanctx_bss_programmed(struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf);
void mt7925_p2p_mcu_sap_event(struct mt792x_dev *dev, struct sk_buff *skb);
+void mt7925_p2p_mcu_bss_is_absence_event(struct mt792x_dev *dev,
+ struct sk_buff *skb);
#endif /* __MT7925_P2P_H */
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 94186e9f77c8..6bfbeceaf026 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -163,6 +163,17 @@ struct mt792x_bss_conf {
struct ewma_rssi rssi;
struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS];
unsigned int link_id;
+ /* FW BSS absence/presence state, updated by the BSS_IS_ABSENCE
+ * event handler.
+ */
+ bool absent;
+ u8 free_quota;
+ unsigned long absence_tstamp;
+
+ /* Dedicated BMC entry, only allocated for a p2p GO. Group-addressed
+ * frames are transmitted on this wcid instead of the vif wcid, so
+ * they no longer share the wcid
+ */
struct mt792x_link_sta bmc;
bool bmc_valid;
};
@@ -407,6 +418,20 @@ mt792x_link_conf_to_mconf(struct ieee80211_bss_conf *link_conf)
return mt792x_vif_to_link(mvif, link_conf->link_id);
}
+/* TRUE only while the BSS has no channel, so nothing we hand to the FW
+ * for it can go on air. Callable from the TX path: reads a single byte
+ * updated by the BSS_IS_ABSENCE event handler.
+ */
+static inline bool mt792x_vif_is_absent(struct ieee80211_vif *vif)
+{
+ struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+
+ if (ieee80211_vif_is_mld(vif))
+ return false;
+
+ return mvif->bss_conf.absent;
+}
+
static inline struct ieee80211_bss_conf *
mt792x_vif_to_bss_conf(struct ieee80211_vif *vif, unsigned int link_id)
{
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index fa29af8fff49..98fa46e85c1b 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -178,6 +178,22 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control,
u8 link_id;
int qid;
+ /* Drop a GO Probe Response outright when mt792x_vif_is_absent()
+ * says the BSS has no channel right now: by the time the FW comes
+ * back the requester has moved on, so the response is stale and
+ * only costs airtime and a TX-abort in the FW.
+ */
+ if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && skb->len >= 2) {
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+ if (vif && ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO &&
+ ieee80211_is_probe_resp(mgmt->frame_control) &&
+ mt792x_vif_is_absent(vif)) {
+ ieee80211_free_txskb(hw, skb);
+ return;
+ }
+ }
+
if (control->sta) {
struct mt792x_link_sta *mlink;
struct mt792x_sta *sta;
--
2.45.2
next prev parent reply other threads:[~2026-11-04 9:24 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 9:23 [PATCH 1/9] wifi: mt76: mt7925: drop P2P mgmt frames received on foreign channel JB Tsai
2026-11-04 9:23 ` [PATCH 2/9] wifi: mt76: mt7925: bound P2P off-channel mgmt TX lifetime JB Tsai
2026-11-04 9:23 ` [PATCH 3/9] wifi: mt76: mt7925: give every P2P interface a BMC wcid of its own JB Tsai
2026-11-04 9:23 ` [PATCH 4/9] wifi: mt76: mt7925: harden the ROC channel-grant handshake JB Tsai
2026-11-04 9:23 ` [PATCH 5/9] wifi: mt76: mt7925: describe the real chandef in ROC requests JB Tsai
2026-11-04 9:23 ` [PATCH 6/9] wifi: mt76: mt7925: request the channel before programming a GO BSS JB Tsai
2026-11-04 9:23 ` [PATCH 7/9] wifi: mt76: mt7925: inject the deauth for an aging-timeout SAP event JB Tsai
2026-11-06 0:34 ` kernel test robot
2026-11-04 9:23 ` JB Tsai [this message]
2026-11-04 9:23 ` [PATCH 9/9] wifi: mt76: mt7925: send the common scan IEs and size the request JB Tsai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261104092344.1932406-8-jb.tsai@mediatek.com \
--to=jb.tsai@mediatek.com \
--cc=James-LY.Huang@mediatek.com \
--cc=Sean.Wang@mediatek.com \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-wireless@vger.kernel.org \
--cc=lorenzo@kernel.org \
--cc=nbd@nbd.name \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox