From: Sean Wang <sean.wang@kernel.org>
To: nbd@nbd.name, lorenzo.bianconi@redhat.com
Cc: linux-wireless@vger.kernel.org,
linux-mediatek@lists.infradead.org, yu-ching.liu@mediatek.com,
jenhao.yang@mediatek.com, posh.sun@mediatek.com,
Jacobs Wu <jacobs.wu@mediatek.com>,
Sean Wang <sean.wang@mediatek.com>
Subject: [PATCH 14/15] wifi: mt76: mt7925: support deferred NAN schedule update and cluster events
Date: Mon, 24 Aug 2026 14:52:26 -0500 [thread overview]
Message-ID: <20260824195227.12589-15-sean.wang@kernel.org> (raw)
In-Reply-To: <20260824195227.12589-1-sean.wang@kernel.org>
From: Jacobs Wu <jacobs.wu@mediatek.com>
Send the is_deferred flag in the availability control TLV so FW
applies a deferred schedule change at the next DW boundary, and report
the NAN_UNI_EVENT_ID_SCHED_UPDATE_DONE and STARTED_CLUSTER events to
mac80211 via ieee80211_nan_sched_update_done() and
ieee80211_nan_cluster_joined().
Both events arrive on the BH-disabled MCU RX path, where notifying
mac80211 inline is wrong: ieee80211_nan_sched_update_done() needs the
wiphy mutex and may sleep, and ieee80211_nan_cluster_joined() would
fire during NAN_START before mac80211 sets sdata->u.nan.started and
before the supplicant subscribes to NL80211_MCGRP_NAN, so the
self-anchor cluster-join is lost and a solo anchor master can never
publish a service.
Deliver both events from a shared nan_deferred_work selected by a
pending bitmap instead. The bitmap spinlock is taken with
spin_lock_bh() on the RX side, the work re-checks nan_vif under the
wiphy mutex, and the stop path clears the bitmap. STARTED_CLUSTER
signals new_cluster=true, and JOINED_CLUSTER is corrected to
new_cluster=false.
Co-developed-by: Sean Wang <sean.wang@mediatek.com>
Signed-off-by: Sean Wang <sean.wang@mediatek.com>
Co-developed-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Stella Liu <yu-ching.liu@mediatek.com>
Signed-off-by: Jacobs Wu <jacobs.wu@mediatek.com>
---
.../net/wireless/mediatek/mt76/mt7925/init.c | 2 +
.../net/wireless/mediatek/mt76/mt7925/main.c | 7 ++
.../wireless/mediatek/mt76/mt7925/mt7925.h | 1 +
.../net/wireless/mediatek/mt76/mt7925/nan.c | 85 ++++++++++++++++++-
.../net/wireless/mediatek/mt76/mt7925/nan.h | 11 ++-
.../net/wireless/mediatek/mt76/mt7925/pci.c | 1 +
drivers/net/wireless/mediatek/mt76/mt792x.h | 8 ++
7 files changed, 111 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
index cd22fcc021b1..8c3049b66a85 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c
@@ -270,6 +270,8 @@ int mt7925_register_device(struct mt792x_dev *dev)
INIT_WORK(&dev->reset_work, mt7925_mac_reset_work);
INIT_WORK(&dev->init_work, mt7925_init_work);
+ INIT_WORK(&dev->nan_deferred_work, mt7925_nan_deferred_work);
+ spin_lock_init(&dev->nan_deferred_lock);
INIT_WORK(&dev->phy.roc_work, mt7925_roc_work);
timer_setup(&dev->phy.roc_timer, mt792x_roc_timer, 0);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 93de42c2bfd9..e1c688af7c59 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -2613,6 +2613,13 @@ static int mt7925_stop_nan(struct ieee80211_hw *hw,
struct mt792x_dev *dev = mt792x_hw_dev(hw);
int err, ret;
+ /* Drop a deferred event queued just before stop so a stale cluster_id
+ * cannot leak into a restart; the work re-checks liveness anyway.
+ */
+ spin_lock_bh(&dev->nan_deferred_lock);
+ dev->nan_deferred_pending = 0;
+ spin_unlock_bh(&dev->nan_deferred_lock);
+
mt792x_mutex_acquire(dev);
err = mt7925_nan_disable(vif, dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
index 321e732347f2..33782d9ba9ed 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h
@@ -361,6 +361,7 @@ void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,
void mt7925_connac_mcu_set_suspend_iter(void *priv, u8 *mac,
struct ieee80211_vif *vif);
void mt7925_set_ipv6_ns_work(struct work_struct *work);
+void mt7925_nan_deferred_work(struct work_struct *work);
int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif,
bool enable);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
index 1f695f0c4473..4b479edab018 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c
@@ -454,9 +454,27 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)
dev_dbg(dev->mt76.dev, "nan: evt=%u cluster=%pM\n",
de_evt->event_type, de_evt->cluster_id);
- if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER)
+ if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER &&
+ de_evt->event_type != NAN_EVENT_ID_STARTED_CLUSTER)
return;
+ /* STARTED_CLUSTER fires during NAN_START, before nan.started is set and
+ * before the supplicant subscribes - defer past NAN_START via the work
+ * so ieee80211_nan_cluster_joined() actually reaches userspace.
+ */
+ if (de_evt->event_type == NAN_EVENT_ID_STARTED_CLUSTER) {
+ dev_dbg(dev->mt76.dev,
+ "nan: deferring STARTED_CLUSTER cluster=%pM\n",
+ cluster_id);
+ spin_lock_bh(&dev->nan_deferred_lock);
+ memcpy(dev->nan_started_cluster_id, cluster_id, ETH_ALEN);
+ set_bit(MT7925_NAN_DEFERRED_STARTED_CLUSTER,
+ &dev->nan_deferred_pending);
+ spin_unlock_bh(&dev->nan_deferred_lock);
+ ieee80211_queue_work(dev->mt76.hw, &dev->nan_deferred_work);
+ return;
+ }
+
if (!dev->nan_vif || !ieee80211_vif_nan_started(dev->nan_vif)) {
dev_warn(dev->mt76.dev, "nan: joined-cluster event but NAN not started\n");
return;
@@ -468,7 +486,43 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)
dev_dbg(dev->mt76.dev, "nan: own_nmi=%pM master_nmi=%pM\n",
de_evt->own_nmi, de_evt->master_nmi);
- ieee80211_nan_cluster_joined(dev->nan_vif, cluster_id, true, GFP_KERNEL);
+ /* joined an existing cluster, not a self-anchored new one */
+ ieee80211_nan_cluster_joined(dev->nan_vif, cluster_id, false, GFP_KERNEL);
+}
+
+/* Runs the deferred NAN MCU events in process context; takes wiphy_lock
+ * before nan_vif, which the NAN stop path frees under that mutex.
+ */
+void
+mt7925_nan_deferred_work(struct work_struct *work)
+{
+ struct mt792x_dev *dev = container_of(work, struct mt792x_dev,
+ nan_deferred_work);
+ struct ieee80211_vif *vif;
+ unsigned long pending;
+ u8 cluster_id[ETH_ALEN];
+
+ spin_lock_bh(&dev->nan_deferred_lock);
+ pending = dev->nan_deferred_pending;
+ dev->nan_deferred_pending = 0;
+ memcpy(cluster_id, dev->nan_started_cluster_id, ETH_ALEN);
+ spin_unlock_bh(&dev->nan_deferred_lock);
+
+ if (!pending)
+ return;
+
+ wiphy_lock(dev->mt76.hw->wiphy);
+ vif = dev->nan_vif;
+ if (!vif || !ieee80211_vif_nan_started(vif))
+ goto out;
+
+ if (test_bit(MT7925_NAN_DEFERRED_STARTED_CLUSTER, &pending))
+ ieee80211_nan_cluster_joined(vif, cluster_id, true, GFP_KERNEL);
+
+ if (test_bit(MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE, &pending))
+ ieee80211_nan_sched_update_done(vif);
+out:
+ wiphy_unlock(dev->mt76.hw->wiphy);
}
static void
@@ -517,6 +571,27 @@ mt7925_nan_handle_ulw_update(struct mt792x_dev *dev, struct tlv *tlv)
GFP_KERNEL);
}
+static void
+mt7925_nan_handle_sched_update_done(struct mt792x_dev *dev, struct tlv *tlv)
+{
+ struct ieee80211_vif *vif;
+
+ if (!dev || !tlv)
+ return;
+
+ vif = dev->nan_vif;
+ if (!vif || !ieee80211_vif_nan_started(vif))
+ return;
+
+ /* Runs in the BH-disabled MCU-event RX path; the mac80211 helper needs
+ * the wiphy mutex and may sleep, so hand it to the work instead.
+ */
+ spin_lock_bh(&dev->nan_deferred_lock);
+ set_bit(MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE, &dev->nan_deferred_pending);
+ spin_unlock_bh(&dev->nan_deferred_lock);
+ ieee80211_queue_work(dev->mt76.hw, &dev->nan_deferred_work);
+}
+
void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb)
{
struct tlv *tlv;
@@ -548,6 +623,9 @@ void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb)
case NAN_UNI_EVENT_ID_ULW_UPDATE:
mt7925_nan_handle_ulw_update(dev, tlv);
break;
+ case NAN_UNI_EVENT_ID_SCHED_UPDATE_DONE:
+ mt7925_nan_handle_sched_update_done(dev, tlv);
+ break;
default:
break;
}
@@ -585,6 +663,7 @@ static int mt7925_nan_avail_ctrl_tlv(struct sk_buff *skb,
avail_ctrl_tlv->avail_ctrl =
cpu_to_le16(ctrl & NAN_AVAIL_CTRL_CHECK_FOR_CHANGED);
avail_ctrl_tlv->seq_id = seq_id;
+ avail_ctrl_tlv->is_deferred = sched->deferred ? 1 : 0;
return 0;
}
@@ -710,7 +789,7 @@ void mt7925_nan_local_sched_changed(struct mt792x_dev *dev,
goto out;
}
- mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), true);
+ mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), false);
out:
mt792x_mutex_release(dev);
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
index 1e8f5b76fc3d..c78a1350c456 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h
@@ -96,15 +96,24 @@ enum nan_uni_cmd_tag {
enum nan_uni_event_tag {
NAN_UNI_EVENT_ID_DE_EVENT_IND = 19,
NAN_UNI_EVENT_ID_ULW_UPDATE = 39,
+ NAN_UNI_EVENT_ID_SCHED_UPDATE_DONE = 43,
NAN_UNI_EVENT_REPORT_DW_START = 59,
NAN_UNI_EVENT_REPORT_DW_END = 60,
};
enum nan_disc_event_type {
NAN_EVENT_ID_DISC_MAC_ADDR = 0,
+ NAN_EVENT_ID_STARTED_CLUSTER = 1,
NAN_EVENT_ID_JOINED_CLUSTER = 2,
};
+/* bit indices into mt792x_dev->nan_deferred_pending, set from the atomic
+ * MCU-event RX path and consumed by mt7925_nan_deferred_work()
+ */
+enum mt7925_nan_deferred_event {
+ MT7925_NAN_DEFERRED_STARTED_CLUSTER,
+ MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE,
+};
/* NAN 4.0 Table 79. Device Capability attribute format, Supported Bands */
enum nan_supported_bands {
NAN_SUPPORTED_BAND_ID_2P4G = 2,
@@ -344,7 +353,7 @@ struct mt7925_nan_avail_ctrl_tlv {
__le16 len;
__le16 avail_ctrl;
u8 seq_id;
- u8 reserved[1];
+ u8 is_deferred;
} __packed __aligned(4);
struct mt7925_nan_ch_timeline {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
index 02ef09dd797d..24585c992ff5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c
@@ -55,6 +55,7 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)
cancel_delayed_work_sync(&pm->ps_work);
cancel_delayed_work_sync(&dev->mlo_pm_work);
cancel_work_sync(&pm->wake_work);
+ cancel_work_sync(&dev->nan_deferred_work);
mt7925_tx_token_put(dev);
__mt792x_mcu_drv_pmctrl(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 9efc251cb745..9722d840c1af 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -342,6 +342,14 @@ struct mt792x_dev {
struct ieee80211_vif *nan_vif;
const struct ieee80211_iface_combination *iface_combinations;
int n_iface_combinations;
+ /* deferred NAN MCU events run out of the atomic RX path on one shared
+ * work; see mt7925_nan_deferred_work() and enum mt7925_nan_deferred_event
+ */
+ struct work_struct nan_deferred_work;
+ /* protects @nan_deferred_pending */
+ spinlock_t nan_deferred_lock;
+ unsigned long nan_deferred_pending;
+ u8 nan_started_cluster_id[ETH_ALEN];
};
static inline struct mt792x_bss_conf *
--
2.43.0
next prev parent reply other threads:[~2026-08-24 19:53 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-24 19:52 [PATCH 00/15] wifi: mt76: mt7925: fix NAN operation and NDP performance Sean Wang
2026-08-24 19:52 ` [PATCH 01/15] wifi: mt76: mt7925: fix NAN NDP STA record role index Sean Wang
2026-08-24 19:52 ` [PATCH 02/15] wifi: mt76: mt7925: fix NAN start failure Sean Wang
2026-08-24 19:52 ` [PATCH 03/15] wifi: mt76: mt7925: fix NAN committed CRB timeline layout Sean Wang
2026-08-24 19:52 ` [PATCH 04/15] wifi: mt76: mt7925: report only 2.4 GHz NAN supported band Sean Wang
2026-08-24 19:52 ` [PATCH 05/15] wifi: mt76: mt7925: drop unused NAN 2.4/5 GHz support config Sean Wang
2026-08-24 19:52 ` [PATCH 06/15] wifi: mt76: mt7925: drop deferred NAN local schedule update handling Sean Wang
2026-08-24 19:52 ` [PATCH 07/15] wifi: mt76: mt7925: use OFDM-only PHY mode for NAN STA records Sean Wang
2026-08-24 19:52 ` [PATCH 08/15] wifi: mt76: mt7925: share TLV setup for NAN enable command Sean Wang
2026-08-24 19:52 ` [PATCH 09/15] wifi: mt76: mt7925: replace NAN DW end event with DW start Sean Wang
2026-08-24 19:52 ` [PATCH 10/15] wifi: mt76: mt7925: fill all DW intervals in NAN avail_map Sean Wang
2026-08-24 19:52 ` [PATCH 11/15] wifi: mt76: mt7925: clear CRB before deactivating NAN peer record Sean Wang
2026-08-24 19:52 ` [PATCH 12/15] wifi: mt76: mt7925: add ULW event handling and peer ULW update Sean Wang
2026-08-24 19:52 ` [PATCH 13/15] wifi: mt76: mt7925: configure NAN PHY setting on enable Sean Wang
2026-08-24 19:52 ` Sean Wang [this message]
2026-08-24 19:52 ` [PATCH 15/15] wifi: mt76: mt7925: fix HT/VHT caps and rates for NAN NDP peers Sean Wang
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=20260824195227.12589-15-sean.wang@kernel.org \
--to=sean.wang@kernel.org \
--cc=jacobs.wu@mediatek.com \
--cc=jenhao.yang@mediatek.com \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-wireless@vger.kernel.org \
--cc=lorenzo.bianconi@redhat.com \
--cc=nbd@nbd.name \
--cc=posh.sun@mediatek.com \
--cc=sean.wang@mediatek.com \
--cc=yu-ching.liu@mediatek.com \
/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